Skip to content

Commit e073496

Browse files
Delay minutes (#293)
* protect again zero results * add with delay option * add METOFFICE_DELAY_MINUTES * add option for 0 and 12 running hours * try new raw_repo * lint * lint * try out with both delay minutes * lint * tidy up * extract model names * tidy up * tidy * tidy * tidy up * tidy up * move delay_minutes to metamodel * fix + lint * clean up * fix to 300 for UM global 17 km * lint
1 parent 75e452b commit e073496

File tree

12 files changed

+42
-22
lines changed

12 files changed

+42
-22
lines changed

src/nwp_consumer/internal/entities/modelmetadata.py

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,12 @@ class ModelMetadata:
6565
to the repository.
6666
"""
6767

68+
delay_minutes: int
69+
"""The model delay in minutes.
70+
71+
Note that this can be overwritten by using `with_delay_minutes`.
72+
"""
73+
6874
chunk_count_overrides: dict[str, int] = dataclasses.field(default_factory=dict)
6975
"""Mapping of dimension names to the desired number of chunks in that dimension.
7076
@@ -201,6 +207,10 @@ def with_running_hours(self, hours: list[int]) -> "ModelMetadata":
201207
"""Returns metadata for the given model with the given running hours."""
202208
return dataclasses.replace(self, running_hours=hours)
203209

210+
def with_delay_minutes(self, delay_minutes: int) -> "ModelMetadata":
211+
"""Returns metadata for the given model with the given delay minutes."""
212+
return dataclasses.replace(self, delay_minutes=delay_minutes)
213+
204214
def with_max_step(self, max_step: int) -> "ModelMetadata":
205215
"""Returns metadata for the given model with the given max step."""
206216
return dataclasses.replace(
@@ -254,6 +264,7 @@ class Models:
254264
longitude=[float(f"{lon / 10:.2f}") for lon in range(-1800, 1800 + 1, 1)],
255265
),
256266
running_hours=[0, 6, 12, 18],
267+
delay_minutes=(60 * 26), # 1 day, plus leeway
257268
)
258269
"""ECMWF's High Resolution Integrated Forecast System."""
259270

@@ -274,6 +285,7 @@ class Models:
274285
longitude=[v / 10 for v in range(-1800, 1800, 1)],
275286
),
276287
running_hours=[0, 12],
288+
delay_minutes=(60 * 26), # 1 day, plus leeway
277289
)
278290
"""Summary statistics from ECMWF's Ensemble Forecast System."""
279291

@@ -303,6 +315,7 @@ class Models:
303315
longitude=[v / 10 for v in range(-1800, 1800, 1)],
304316
),
305317
running_hours=[0, 6, 12, 18],
318+
delay_minutes=(60 * 26), # 1 day, plus leeway
306319
)
307320
"""Full ensemble data from ECMWF's Ensemble Forecast System."""
308321

@@ -335,6 +348,7 @@ class Models:
335348
longitude=[float(lon) for lon in range(-180, 180 + 1, 1)],
336349
),
337350
running_hours=[0, 6, 12, 18],
351+
delay_minutes=(60 * 5), # 5 hours
338352
)
339353
"""NCEP's Global Forecast System."""
340354

@@ -372,6 +386,7 @@ class Models:
372386
# TODO: Change to -180 -> 180
373387
),
374388
running_hours=[0, 6, 12, 18],
389+
delay_minutes=300,
375390
)
376391
"""MetOffice's Unified Model, in the Global configuration, at a resolution of 17km."""
377392

@@ -404,7 +419,8 @@ class Models:
404419
for lon in np.arange(-179.929687, 179.929688 + 0.140625, 0.140625)
405420
],
406421
),
407-
running_hours=[0, 6, 12, 18],
422+
running_hours=[0, 12],
423+
delay_minutes=300,
408424
)
409425
"""MetOffice's Unified Model, in the Global configuration, at a resolution of 10km."""
410426

@@ -438,6 +454,7 @@ class Models:
438454
x_osgb=[int(x) for x in np.arange(start=-239000, stop=857000, step=2000)],
439455
),
440456
running_hours=list(range(0, 24, 6)),
457+
delay_minutes=120,
441458
)
442459
"""MetOffice's Unified Model in the UKV configuration, at a resolution of 2km"""
443460

@@ -468,5 +485,6 @@ class Models:
468485
x_laea=[int(x) for x in np.arange(start=-576000, stop=332000 + 2000, step=2000)],
469486
),
470487
running_hours=list(range(0, 24, 3)), # Only first 12 steps available for hourly runs
488+
delay_minutes=120,
471489
)
472490
"""MetOffice's Unified Model in the UKV configuration, at a resolution of 2km"""

src/nwp_consumer/internal/entities/repometadata.py

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -40,12 +40,6 @@ class RawRepositoryMetadata:
4040
but rather are defined by pre-selected agreements with the provider.
4141
"""
4242

43-
delay_minutes: int
44-
"""The approximate model delay in minutes.
45-
46-
This delay is the time between the running of the model and the time
47-
at which the data is actually available."""
48-
4943
required_env: list[str]
5044
"""Environment variables required for usage."""
5145

@@ -65,18 +59,23 @@ class RawRepositoryMetadata:
6559
available_models: dict[str, ModelMetadata]
6660
"""A dictionary of available models and their metadata."""
6761

68-
def determine_latest_it_from(self, t: dt.datetime, running_hours: list[int]) -> dt.datetime:
62+
def determine_latest_it_from(
63+
self, t: dt.datetime,
64+
running_hours: list[int],
65+
delay_minutes:int) -> dt.datetime:
6966
"""Determine the latest available initialization time from a given time.
7067
7168
Args:
7269
t: The time from which to determine the latest initialization time.
7370
running_hours: A list of hours at which the model runs each day.
71+
delay_minutes: The delay in minutes after the initialization time
72+
before data is available.
7473
7574
Returns:
7675
The latest available initialization time prior to the given time.
7776
"""
7877
it = (
79-
t.replace(minute=0, second=0, microsecond=0) - dt.timedelta(minutes=self.delay_minutes)
78+
t.replace(minute=0, second=0, microsecond=0) - dt.timedelta(minutes=delay_minutes)
8079
).replace(minute=0)
8180
while it.hour not in running_hours:
8281
it -= dt.timedelta(hours=1)
@@ -97,7 +96,6 @@ def __str__(self) -> str:
9796
(
9897
"Model Repository: ",
9998
f"\n\t{self.name} ({'archive' if self.is_archive else 'live/rolling'} dataset.)",
100-
f"\n\t\t(available after {self.delay_minutes} minute delay)",
10199
"\nEnvironment variables:",
102100
"\n\tRequired:",
103101
"\n".join(f"\t\t{var}" for var in self.required_env),

src/nwp_consumer/internal/entities/test_modelmetadata.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ def test_with_region(self) -> None:
2727
longitude=[float(f"{lon / 10:.2f}") for lon in range(-1800, 1800 + 1, 1)],
2828
),
2929
running_hours=[0, 6, 12, 18],
30+
delay_minutes=60,
3031
)
3132

3233
@dataclasses.dataclass

src/nwp_consumer/internal/entities/test_repometadata.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ class TestRawRepositoryMetadata(unittest.TestCase):
1313
name="test",
1414
is_archive=False,
1515
is_order_based=False,
16-
delay_minutes=55,
1716
required_env=["TEST"],
1817
optional_env={"TEST": "test"},
1918
max_connections=1,
@@ -50,7 +49,9 @@ class TestCase:
5049

5150
for test in tests:
5251
with self.subTest(name=test.name):
53-
result = self.metadata.determine_latest_it_from(test.t, [0, 6, 12, 18])
52+
result = self.metadata.determine_latest_it_from(test.t,
53+
[0, 6, 12, 18],
54+
delay_minutes=60)
5455
self.assertEqual(result, test.expected)
5556

5657

src/nwp_consumer/internal/repositories/raw_repositories/ceda.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,6 @@ def repository() -> entities.RawRepositoryMetadata:
117117
name="CEDA",
118118
is_archive=True,
119119
is_order_based=False,
120-
delay_minutes=(60 * 24 * 7) + (60 * 12), # 7.5 days
121120
max_connections=20,
122121
required_env=["CEDA_USER", "CEDA_PASS"],
123122
optional_env={},

src/nwp_consumer/internal/repositories/raw_repositories/ecmwf_mars.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -211,7 +211,6 @@ def repository() -> entities.RawRepositoryMetadata:
211211
name="ECMWF-MARS",
212212
is_archive=True,
213213
is_order_based=False,
214-
delay_minutes=(60 * 26), # 1 day, plus leeway
215214
max_connections=20,
216215
required_env=[
217216
"ECMWF_API_KEY",

src/nwp_consumer/internal/repositories/raw_repositories/ecmwf_realtime.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,6 @@ def repository() -> entities.RawRepositoryMetadata:
6767
name="ECMWF-Realtime-S3",
6868
is_archive=False,
6969
is_order_based=True,
70-
delay_minutes=(60 * 7), # 7 hours
7170
max_connections=100,
7271
required_env=[
7372
"ECMWF_REALTIME_S3_ACCESS_KEY",
@@ -81,14 +80,16 @@ def repository() -> entities.RawRepositoryMetadata:
8180
},
8281
postprocess_options=entities.PostProcessOptions(),
8382
available_models={
84-
"default": entities.Models.ECMWF_HRES_IFS_0P1DEGREE.with_region("uk-north60"),
85-
"hres-ifs-uk": entities.Models.ECMWF_HRES_IFS_0P1DEGREE.with_region("uk-north60"),
83+
"default": entities.Models.ECMWF_HRES_IFS_0P1DEGREE.with_region("uk-north60").
84+
with_delay_minutes(60 * 7),
85+
"hres-ifs-uk": entities.Models.ECMWF_HRES_IFS_0P1DEGREE.with_region("uk-north60").
86+
with_delay_minutes(60 * 7),
8687
"hres-ifs-india": entities.Models.ECMWF_HRES_IFS_0P1DEGREE.with_region(
8788
"india",
88-
).with_chunk_count_overrides({"variable": 1}),
89+
).with_chunk_count_overrides({"variable": 1}).with_delay_minutes(60 * 7),
8990
"hres-ifs-nl": entities.Models.ECMWF_HRES_IFS_0P1DEGREE.with_region(
9091
"nl",
91-
).with_max_step(84),
92+
).with_max_step(84).with_delay_minutes(60 * 7),
9293
},
9394
)
9495

src/nwp_consumer/internal/repositories/raw_repositories/mo_datahub.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,11 +152,11 @@ def __init__(self, order_id: str, api_key: str) -> None:
152152
@staticmethod
153153
@override
154154
def repository() -> entities.RawRepositoryMetadata:
155+
155156
return entities.RawRepositoryMetadata(
156157
name="MetOffice-Weather-Datahub",
157158
is_archive=False,
158159
is_order_based=True,
159-
delay_minutes=120,
160160
max_connections=10,
161161
required_env=["METOFFICE_API_KEY", "METOFFICE_ORDER_ID"],
162162
optional_env={"METOFFICE_DATASPEC": "1.1.0"},

src/nwp_consumer/internal/repositories/raw_repositories/noaa_s3.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,6 @@ def repository() -> entities.RawRepositoryMetadata:
5555
name="NOAA-GFS-S3",
5656
is_archive=False,
5757
is_order_based=False,
58-
delay_minutes=(60 * 5), # 5 hours
5958
max_connections=100,
6059
required_env=[],
6160
optional_env={},

src/nwp_consumer/internal/repositories/raw_repositories/test_mo_datahub.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ def test__download(self) -> None:
2828
test_it = c.repository().determine_latest_it_from(
2929
dt.datetime.now(tz=dt.UTC),
3030
c.model().running_hours,
31+
delay_minutes=c.model().delay_minutes,
3132
)
3233

3334
dl_result = c._download(
@@ -92,6 +93,8 @@ class TestCase:
9293
else:
9394
self.assertIsInstance(region_result, Success, msg=f"{region_result}")
9495

96+
97+
9598
@patch.dict(os.environ, {"MODEL": "um-ukv-2km"}, clear=True)
9699
def test_convert_ukv(self) -> None:
97100
@dataclasses.dataclass

0 commit comments

Comments
 (0)