Skip to content

Commit 1c9b232

Browse files
authored
feat(nwp-consumer): Add west-europe option for IFS (#225)
1 parent 2bb79a9 commit 1c9b232

File tree

4 files changed

+45
-19
lines changed

4 files changed

+45
-19
lines changed

src/nwp_consumer/internal/entities/coordinates.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -510,6 +510,14 @@ def crop(
510510
" and both must sit between 180 and -180 degrees.",
511511
))
512512

513+
if (north > self.latitude[0] or south < self.latitude[-1]
514+
or west < self.longitude[0] or east > self.longitude[-1]):
515+
return Failure(ValueError(
516+
"Cannot crop coordinates to a region outside the bounds of the map. "
517+
f"Crop region '{north, west, south, east}' not in "
518+
f"map bounds '{self.nwse()}'.",
519+
))
520+
513521
# Determine the indices of the region in the latitude and longitude lists
514522
lat_indices = [
515523
i for i, lat in enumerate(self.latitude)

src/nwp_consumer/internal/entities/modelmetadata.py

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,12 @@ def with_region(self, region: str) -> "ModelMetadata":
9898
).map(lambda coords: dataclasses.replace(
9999
self, name=f"{self.name}_india", expected_coordinates=coords,
100100
)).unwrap()
101+
case "west-europe":
102+
return self.expected_coordinates.crop(
103+
north=63, west=-12, south=35, east=26,
104+
).map(lambda coords: dataclasses.replace(
105+
self, name=f"{self.name}_west-europe", expected_coordinates=coords,
106+
)).unwrap()
101107
case _:
102108
log.warning(f"Unknown region '{region}', not cropping expected coordinates.")
103109
return self
@@ -115,7 +121,7 @@ class Models:
115121
"""Namespace containing known models."""
116122

117123
ECMWF_HRES_IFS_0P1DEGREE: ModelMetadata = ModelMetadata(
118-
name="HRES-IFS",
124+
name="hres-ifs",
119125
resolution="0.1 degrees",
120126
expected_coordinates=NWPDimensionCoordinateMap(
121127
init_time=[],
@@ -147,7 +153,7 @@ class Models:
147153
"""ECMWF's High Resolution Integrated Forecast System."""
148154

149155
ECMWF_ENS_STAT_0P1DEGREE: ModelMetadata = ModelMetadata(
150-
name="ENS-Stat",
156+
name="ens-stat",
151157
resolution="0.1 degrees",
152158
expected_coordinates=NWPDimensionCoordinateMap(
153159
init_time=[],
@@ -166,7 +172,7 @@ class Models:
166172
"""Summary statistics from ECMWF's Ensemble Forecast System."""
167173

168174
ECMWF_ENS_0P1DEGREE: ModelMetadata = ModelMetadata(
169-
name="ENS",
175+
name="ens",
170176
resolution="0.1 degrees",
171177
expected_coordinates=NWPDimensionCoordinateMap(
172178
init_time=[],
@@ -193,7 +199,7 @@ class Models:
193199
"""Full ensemble data from ECMWF's Ensemble Forecast System."""
194200

195201
NCEP_GFS_1DEGREE: ModelMetadata = ModelMetadata(
196-
name="NCEP-GFS",
202+
name="ncep-gfs",
197203
resolution="1 degree",
198204
expected_coordinates=NWPDimensionCoordinateMap(
199205
init_time=[],
@@ -224,7 +230,7 @@ class Models:
224230
"""NCEP's Global Forecast System."""
225231

226232
MO_UM_GLOBAL_17KM: ModelMetadata = ModelMetadata(
227-
name="UM-Global",
233+
name="um-global",
228234
resolution="17km",
229235
expected_coordinates = NWPDimensionCoordinateMap(
230236
init_time=[],
@@ -259,7 +265,7 @@ class Models:
259265
"""MetOffice's Unified Model, in the Global configuration, at a resolution of 17km."""
260266

261267
MO_UM_GLOBAL_10KM: ModelMetadata = ModelMetadata(
262-
name="UM-Global",
268+
name="um-global",
263269
resolution="10km",
264270
expected_coordinates=NWPDimensionCoordinateMap(
265271
init_time=[],

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

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -222,12 +222,14 @@ def repository() -> entities.RawRepositoryMetadata:
222222
"default": entities.Models.ECMWF_HRES_IFS_0P1DEGREE.with_region("uk"),
223223
"hres-ifs-uk": entities.Models.ECMWF_HRES_IFS_0P1DEGREE.with_region("uk"),
224224
"hres-ifs-india": entities.Models.ECMWF_HRES_IFS_0P1DEGREE.with_region("india"),
225+
"hres-ifs-west-europe": entities.Models.ECMWF_HRES_IFS_0P1DEGREE\
226+
.with_region("west-europe"),
225227
"ens-stat-india": entities.Models.ECMWF_ENS_STAT_0P1DEGREE.with_region("india"),
226228
"ens-stat-uk": entities.Models.ECMWF_ENS_STAT_0P1DEGREE.with_region("uk"),
227229
"ens-uk": entities.Models.ECMWF_ENS_0P1DEGREE.with_region("uk")\
228-
.with_chunk_count_overrides(
229-
{"latitude": 2, "longitude": 2, "variable": 1, "ensemble_member": 5},
230-
),
230+
.with_chunk_count_overrides(
231+
{"latitude": 2, "longitude": 2, "variable": 1, "ensemble_member": 5},
232+
),
231233
},
232234
)
233235

@@ -331,6 +333,7 @@ def _convert(path: pathlib.Path) -> ResultE[list[xr.DataArray]]:
331333
f"Error context: {e}",
332334
))
333335

336+
processed_das: list[xr.DataArray] = []
334337
try:
335338
# Merge the datasets back into one
336339
ds: xr.Dataset = xr.merge(
@@ -340,12 +343,11 @@ def _convert(path: pathlib.Path) -> ResultE[list[xr.DataArray]]:
340343
)
341344
del dss
342345

343-
if "ens" in ECMWFMARSRawRepository.model().name.lower():
344-
# Add in missing coordinates for mean/std data
345-
if "enfo-es" in path.name:
346-
ds = ds.expand_dims(dim={"ensemble_stat": ["std"]})
347-
elif "enfo-em" in path.name:
348-
ds = ds.expand_dims(dim={"ensemble_stat": ["mean"]})
346+
# Add in missing coordinates for mean/std data
347+
if "enfo-es" in path.name:
348+
ds = ds.expand_dims(dim={"ensemble_stat": ["std"]})
349+
elif "enfo-em" in path.name:
350+
ds = ds.expand_dims(dim={"ensemble_stat": ["mean"]})
349351
da: xr.DataArray = (
350352
ds
351353
.pipe(
@@ -356,7 +358,7 @@ def _convert(path: pathlib.Path) -> ResultE[list[xr.DataArray]]:
356358
.expand_dims("init_time")
357359
.to_dataarray(name=ECMWFMARSRawRepository.model().name)
358360
)
359-
if "number" in da.coords:
361+
if "ens" in path.as_posix():
360362
da = da.rename({"number": "ensemble_member"})
361363
da = (
362364
da.drop_vars(
@@ -370,11 +372,21 @@ def _convert(path: pathlib.Path) -> ResultE[list[xr.DataArray]]:
370372
.sortby(variables=["step", "variable", "longitude"])
371373
.sortby(variables="latitude", ascending=False)
372374
)
375+
# Put each variable into its own DataArray:
376+
# * Each raw file does not contain a full set of parameters
377+
# * and so may not produce a contiguous subset of the expected coordinates.
378+
processed_das.extend(
379+
[
380+
da.where(cond=da.coords["variable"] == v, drop=True)
381+
for v in da.coords["variable"].values
382+
],
383+
)
384+
373385

374386
except Exception as e:
375387
return Failure(ValueError(
376388
f"Error processing DataArray for path '{path!s}'. Error context: {e}",
377389
))
378390

379-
return Success([da])
391+
return Success(processed_das)
380392

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -267,8 +267,8 @@ def _convert(path: pathlib.Path) -> ResultE[list[xr.DataArray]]:
267267
# * and so may not produce a contiguous subset of the expected coordinates.
268268
processed_das.extend(
269269
[
270-
da.where(cond=da["variable"] == v, drop=True)
271-
for v in da["variable"].values
270+
da.where(cond=da.coords["variable"] == v, drop=True)
271+
for v in da.coords["variable"].values
272272
],
273273
)
274274

0 commit comments

Comments
 (0)