|
21 | 21 | time of 2024-10-25 00:00 and a target time of 2024-10-26 01:00 (step of 25 hours). |
22 | 22 |
|
23 | 23 | The file contents is specific to the order agreed with the data provider. |
24 | | -For the order that OCF has created, there are four distinct datasets. |
25 | | -This is because OCF has ordered two separate regions and 17 variables, |
26 | | -which are split across two datasets. |
| 24 | +OCF orders a single west-europe region alongside a separate india region, |
| 25 | +and the variables are split across multiple datasets within each file. |
| 26 | +Datasets that overlap the requested model region are cropped to it, which |
| 27 | +allows building a sub-region store (e.g. nl) from the larger west-europe order. |
27 | 28 |
|
28 | 29 | Also, some of the data contains larger steps than we are interested in due |
29 | 30 | to necessities in the order creation process. |
@@ -91,6 +92,9 @@ def repository() -> entities.RawRepositoryMetadata: |
91 | 92 | "hres-ifs-nl": entities.Models.ECMWF_HRES_IFS_0P1DEGREE.with_region( |
92 | 93 | "nl", |
93 | 94 | ).with_max_step(84).with_delay_minutes(60 * 7), |
| 95 | + "hres-ifs-west-europe": entities.Models.ECMWF_HRES_IFS_0P1DEGREE.with_region( |
| 96 | + "west-europe", |
| 97 | + ).with_delay_minutes(60 * 7), |
94 | 98 | }, |
95 | 99 | ) |
96 | 100 |
|
@@ -273,14 +277,22 @@ def _convert(path: pathlib.Path) -> ResultE[list[xr.DataArray]]: |
273 | 277 | expected_lats = ECMWFRealTimeS3RawRepository.model().expected_coordinates.latitude |
274 | 278 | expected_steps = ECMWFRealTimeS3RawRepository.model().expected_coordinates.step |
275 | 279 |
|
| 280 | + north, west, south, east = \ |
| 281 | + ECMWFRealTimeS3RawRepository.model().expected_coordinates.nwse() |
| 282 | + |
276 | 283 | for i, ds in enumerate(dss): |
277 | | - # ECMWF Realtime provides all regions in one set of datasets, |
278 | | - # so distinguish via their coordinates |
| 284 | + # ECMWF Realtime provides multiple orders in one set of datasets |
| 285 | + # (e.g. west-europe and india), so distinguish via their coordinates. |
| 286 | + # Datasets that overlap the requested region are kept and cropped to it, |
| 287 | + # which allows a sub-region store (e.g. nl) to be built from the |
| 288 | + # larger west-europe order. |
| 289 | + ds_lons = ds.coords["longitude"].values |
| 290 | + ds_lats = ds.coords["latitude"].values |
279 | 291 | step = np.timedelta64(ds.coords["step"].values, "h").astype(int) # type: ignore[arg-type] |
280 | 292 | is_relevant_dataset_predicate: bool = ( |
281 | 293 | (expected_lons is not None and expected_lats is not None) |
282 | | - and (expected_lons[0] <= max(ds.coords["longitude"].values) <= expected_lons[-1]) |
283 | | - and (expected_lats[-1] <= max(ds.coords["latitude"].values) <= expected_lats[0]) |
| 294 | + and (min(ds_lons) <= expected_lons[-1] and max(ds_lons) >= expected_lons[0]) |
| 295 | + and (min(ds_lats) <= expected_lats[0] and max(ds_lats) >= expected_lats[-1]) |
284 | 296 | and (expected_steps[0] <= step <= expected_steps[-1]) |
285 | 297 | ) |
286 | 298 | if not is_relevant_dataset_predicate: |
@@ -311,6 +323,15 @@ def _convert(path: pathlib.Path) -> ResultE[list[xr.DataArray]]: |
311 | 323 | .sortby(variables=["step", "variable", "longitude"]) |
312 | 324 | .sortby(variables="latitude", ascending=False) |
313 | 325 | ) |
| 326 | + # Crop to the requested model region, since the order may cover |
| 327 | + # a larger extent (e.g. cropping nl from the west-europe order). |
| 328 | + # Round coordinates to the store's 4 d.p. precision first, else |
| 329 | + # floating-point grid-edge differences drop boundary points and |
| 330 | + # misalign the write with chunk boundaries. |
| 331 | + da = da.assign_coords( |
| 332 | + latitude=[float(f"{v:.4f}") for v in da["latitude"].values], |
| 333 | + longitude=[float(f"{v:.4f}") for v in da["longitude"].values], |
| 334 | + ).sel(latitude=slice(north, south), longitude=slice(west, east)) |
314 | 335 |
|
315 | 336 | except Exception as e: |
316 | 337 | return Failure( |
|
0 commit comments