anemoi-datasets 0.5.26__py3-none-any.whl → 0.5.28__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- anemoi/datasets/__init__.py +1 -2
- anemoi/datasets/_version.py +16 -3
- anemoi/datasets/commands/check.py +1 -1
- anemoi/datasets/commands/copy.py +1 -2
- anemoi/datasets/commands/create.py +1 -1
- anemoi/datasets/commands/inspect.py +27 -35
- anemoi/datasets/commands/recipe/__init__.py +93 -0
- anemoi/datasets/commands/recipe/format.py +55 -0
- anemoi/datasets/commands/recipe/migrate.py +555 -0
- anemoi/datasets/commands/validate.py +59 -0
- anemoi/datasets/compute/recentre.py +3 -6
- anemoi/datasets/create/__init__.py +64 -26
- anemoi/datasets/create/check.py +10 -12
- anemoi/datasets/create/chunks.py +1 -2
- anemoi/datasets/create/config.py +5 -6
- anemoi/datasets/create/input/__init__.py +44 -65
- anemoi/datasets/create/input/action.py +296 -238
- anemoi/datasets/create/input/context/__init__.py +71 -0
- anemoi/datasets/create/input/context/field.py +54 -0
- anemoi/datasets/create/input/data_sources.py +7 -9
- anemoi/datasets/create/input/misc.py +2 -75
- anemoi/datasets/create/input/repeated_dates.py +11 -130
- anemoi/datasets/{utils → create/input/result}/__init__.py +10 -1
- anemoi/datasets/create/input/{result.py → result/field.py} +36 -120
- anemoi/datasets/create/input/trace.py +1 -1
- anemoi/datasets/create/patch.py +1 -2
- anemoi/datasets/create/persistent.py +3 -5
- anemoi/datasets/create/size.py +1 -3
- anemoi/datasets/create/sources/accumulations.py +120 -145
- anemoi/datasets/create/sources/accumulations2.py +20 -53
- anemoi/datasets/create/sources/anemoi_dataset.py +46 -42
- anemoi/datasets/create/sources/constants.py +39 -40
- anemoi/datasets/create/sources/empty.py +22 -19
- anemoi/datasets/create/sources/fdb.py +133 -0
- anemoi/datasets/create/sources/forcings.py +29 -29
- anemoi/datasets/create/sources/grib.py +94 -78
- anemoi/datasets/create/sources/grib_index.py +57 -55
- anemoi/datasets/create/sources/hindcasts.py +57 -59
- anemoi/datasets/create/sources/legacy.py +10 -62
- anemoi/datasets/create/sources/mars.py +121 -149
- anemoi/datasets/create/sources/netcdf.py +28 -25
- anemoi/datasets/create/sources/opendap.py +28 -26
- anemoi/datasets/create/sources/patterns.py +4 -6
- anemoi/datasets/create/sources/recentre.py +46 -48
- anemoi/datasets/create/sources/repeated_dates.py +44 -0
- anemoi/datasets/create/sources/source.py +26 -51
- anemoi/datasets/create/sources/tendencies.py +68 -98
- anemoi/datasets/create/sources/xarray.py +4 -6
- anemoi/datasets/create/sources/xarray_support/__init__.py +40 -36
- anemoi/datasets/create/sources/xarray_support/coordinates.py +8 -12
- anemoi/datasets/create/sources/xarray_support/field.py +20 -16
- anemoi/datasets/create/sources/xarray_support/fieldlist.py +11 -15
- anemoi/datasets/create/sources/xarray_support/flavour.py +42 -42
- anemoi/datasets/create/sources/xarray_support/grid.py +15 -9
- anemoi/datasets/create/sources/xarray_support/metadata.py +19 -128
- anemoi/datasets/create/sources/xarray_support/patch.py +4 -6
- anemoi/datasets/create/sources/xarray_support/time.py +10 -13
- anemoi/datasets/create/sources/xarray_support/variable.py +21 -21
- anemoi/datasets/create/sources/xarray_zarr.py +28 -25
- anemoi/datasets/create/sources/zenodo.py +43 -41
- anemoi/datasets/create/statistics/__init__.py +3 -6
- anemoi/datasets/create/testing.py +4 -0
- anemoi/datasets/create/typing.py +1 -2
- anemoi/datasets/create/utils.py +0 -43
- anemoi/datasets/create/zarr.py +7 -2
- anemoi/datasets/data/__init__.py +15 -6
- anemoi/datasets/data/complement.py +7 -12
- anemoi/datasets/data/concat.py +5 -8
- anemoi/datasets/data/dataset.py +48 -47
- anemoi/datasets/data/debug.py +7 -9
- anemoi/datasets/data/ensemble.py +4 -6
- anemoi/datasets/data/fill_missing.py +7 -10
- anemoi/datasets/data/forwards.py +22 -26
- anemoi/datasets/data/grids.py +12 -168
- anemoi/datasets/data/indexing.py +9 -12
- anemoi/datasets/data/interpolate.py +7 -15
- anemoi/datasets/data/join.py +8 -12
- anemoi/datasets/data/masked.py +6 -11
- anemoi/datasets/data/merge.py +5 -9
- anemoi/datasets/data/misc.py +41 -45
- anemoi/datasets/data/missing.py +11 -16
- anemoi/datasets/data/observations/__init__.py +8 -14
- anemoi/datasets/data/padded.py +3 -5
- anemoi/datasets/data/records/backends/__init__.py +2 -2
- anemoi/datasets/data/rescale.py +5 -12
- anemoi/datasets/data/rolling_average.py +141 -0
- anemoi/datasets/data/select.py +13 -16
- anemoi/datasets/data/statistics.py +4 -7
- anemoi/datasets/data/stores.py +22 -29
- anemoi/datasets/data/subset.py +8 -11
- anemoi/datasets/data/unchecked.py +7 -11
- anemoi/datasets/data/xy.py +25 -21
- anemoi/datasets/dates/__init__.py +15 -18
- anemoi/datasets/dates/groups.py +7 -10
- anemoi/datasets/dumper.py +76 -0
- anemoi/datasets/grids.py +4 -185
- anemoi/datasets/schemas/recipe.json +131 -0
- anemoi/datasets/testing.py +93 -7
- anemoi/datasets/validate.py +598 -0
- {anemoi_datasets-0.5.26.dist-info → anemoi_datasets-0.5.28.dist-info}/METADATA +7 -4
- anemoi_datasets-0.5.28.dist-info/RECORD +134 -0
- anemoi/datasets/create/filter.py +0 -48
- anemoi/datasets/create/input/concat.py +0 -164
- anemoi/datasets/create/input/context.py +0 -89
- anemoi/datasets/create/input/empty.py +0 -54
- anemoi/datasets/create/input/filter.py +0 -118
- anemoi/datasets/create/input/function.py +0 -233
- anemoi/datasets/create/input/join.py +0 -130
- anemoi/datasets/create/input/pipe.py +0 -66
- anemoi/datasets/create/input/step.py +0 -177
- anemoi/datasets/create/input/template.py +0 -162
- anemoi_datasets-0.5.26.dist-info/RECORD +0 -131
- {anemoi_datasets-0.5.26.dist-info → anemoi_datasets-0.5.28.dist-info}/WHEEL +0 -0
- {anemoi_datasets-0.5.26.dist-info → anemoi_datasets-0.5.28.dist-info}/entry_points.txt +0 -0
- {anemoi_datasets-0.5.26.dist-info → anemoi_datasets-0.5.28.dist-info}/licenses/LICENSE +0 -0
- {anemoi_datasets-0.5.26.dist-info → anemoi_datasets-0.5.28.dist-info}/top_level.txt +0 -0
|
@@ -11,10 +11,6 @@
|
|
|
11
11
|
import json
|
|
12
12
|
import logging
|
|
13
13
|
from typing import Any
|
|
14
|
-
from typing import Dict
|
|
15
|
-
from typing import List
|
|
16
|
-
from typing import Optional
|
|
17
|
-
from typing import Union
|
|
18
14
|
|
|
19
15
|
import xarray as xr
|
|
20
16
|
import yaml
|
|
@@ -33,7 +29,7 @@ LOG = logging.getLogger(__name__)
|
|
|
33
29
|
class XarrayFieldList(FieldList):
|
|
34
30
|
"""A class to represent a list of fields from an xarray Dataset."""
|
|
35
31
|
|
|
36
|
-
def __init__(self, ds: xr.Dataset, variables:
|
|
32
|
+
def __init__(self, ds: xr.Dataset, variables: list[Variable]) -> None:
|
|
37
33
|
"""Initialize the XarrayFieldList.
|
|
38
34
|
|
|
39
35
|
Parameters
|
|
@@ -44,7 +40,7 @@ class XarrayFieldList(FieldList):
|
|
|
44
40
|
The list of variables.
|
|
45
41
|
"""
|
|
46
42
|
self.ds: xr.Dataset = ds
|
|
47
|
-
self.variables:
|
|
43
|
+
self.variables: list[Variable] = variables.copy()
|
|
48
44
|
self.total_length: int = sum(v.length for v in variables)
|
|
49
45
|
|
|
50
46
|
def __repr__(self) -> str:
|
|
@@ -90,8 +86,8 @@ class XarrayFieldList(FieldList):
|
|
|
90
86
|
cls,
|
|
91
87
|
ds: xr.Dataset,
|
|
92
88
|
*,
|
|
93
|
-
flavour:
|
|
94
|
-
patch:
|
|
89
|
+
flavour: str | dict[str, Any] | None = None,
|
|
90
|
+
patch: dict[str, Any] | None = None,
|
|
95
91
|
) -> "XarrayFieldList":
|
|
96
92
|
"""Create an XarrayFieldList from an xarray Dataset.
|
|
97
93
|
|
|
@@ -112,7 +108,7 @@ class XarrayFieldList(FieldList):
|
|
|
112
108
|
if patch is not None:
|
|
113
109
|
ds = patch_dataset(ds, patch)
|
|
114
110
|
|
|
115
|
-
variables:
|
|
111
|
+
variables: list[Variable] = []
|
|
116
112
|
|
|
117
113
|
if isinstance(flavour, str):
|
|
118
114
|
with open(flavour) as f:
|
|
@@ -121,9 +117,9 @@ class XarrayFieldList(FieldList):
|
|
|
121
117
|
else:
|
|
122
118
|
flavour = json.load(f)
|
|
123
119
|
|
|
124
|
-
if isinstance(flavour,
|
|
125
|
-
flavour_coords:
|
|
126
|
-
ds_dims:
|
|
120
|
+
if isinstance(flavour, dict):
|
|
121
|
+
flavour_coords: list[str] = [coords["name"] for coords in flavour["rules"].values()]
|
|
122
|
+
ds_dims: list[str] = [dim for dim in ds._dims]
|
|
127
123
|
for dim in ds_dims:
|
|
128
124
|
if dim in flavour_coords and dim not in ds._coord_names:
|
|
129
125
|
ds = ds.assign_coords({dim: ds[dim]})
|
|
@@ -154,7 +150,7 @@ class XarrayFieldList(FieldList):
|
|
|
154
150
|
continue
|
|
155
151
|
|
|
156
152
|
variable = ds[name]
|
|
157
|
-
coordinates:
|
|
153
|
+
coordinates: list[Any] = []
|
|
158
154
|
|
|
159
155
|
for coord in variable.coords:
|
|
160
156
|
|
|
@@ -210,7 +206,7 @@ class XarrayFieldList(FieldList):
|
|
|
210
206
|
So we get an extra chance to filter the fields by the metadata.
|
|
211
207
|
"""
|
|
212
208
|
|
|
213
|
-
variables:
|
|
209
|
+
variables: list[Variable] = []
|
|
214
210
|
count: int = 0
|
|
215
211
|
|
|
216
212
|
for v in self.variables:
|
|
@@ -223,7 +219,7 @@ class XarrayFieldList(FieldList):
|
|
|
223
219
|
|
|
224
220
|
if match:
|
|
225
221
|
count += 1
|
|
226
|
-
missing:
|
|
222
|
+
missing: dict[str, Any] = {}
|
|
227
223
|
|
|
228
224
|
# Select from the variable's coordinates (time, level, number, ....)
|
|
229
225
|
# This may return a new variable with a isel() slice of the selection
|
|
@@ -11,11 +11,8 @@
|
|
|
11
11
|
import logging
|
|
12
12
|
from abc import ABC
|
|
13
13
|
from abc import abstractmethod
|
|
14
|
+
from collections.abc import Hashable
|
|
14
15
|
from typing import Any
|
|
15
|
-
from typing import Dict
|
|
16
|
-
from typing import Hashable
|
|
17
|
-
from typing import Optional
|
|
18
|
-
from typing import Tuple
|
|
19
16
|
|
|
20
17
|
import xarray as xr
|
|
21
18
|
from anemoi.utils.config import DotDict
|
|
@@ -61,11 +58,11 @@ class CoordinateGuesser(ABC):
|
|
|
61
58
|
The dataset to guess coordinates from.
|
|
62
59
|
"""
|
|
63
60
|
self.ds = ds
|
|
64
|
-
self._coordinate_cache:
|
|
65
|
-
self._grid_cache:
|
|
61
|
+
self._coordinate_cache: dict[Hashable, Coordinate] = {}
|
|
62
|
+
self._grid_cache: dict[Hashable, Grid] = {}
|
|
66
63
|
|
|
67
64
|
@classmethod
|
|
68
|
-
def from_flavour(cls, ds: xr.Dataset, flavour:
|
|
65
|
+
def from_flavour(cls, ds: xr.Dataset, flavour: dict[str, Any] | None) -> "CoordinateGuesser":
|
|
69
66
|
"""Creates a CoordinateGuesser from a flavour.
|
|
70
67
|
|
|
71
68
|
Parameters
|
|
@@ -133,7 +130,7 @@ class CoordinateGuesser(ABC):
|
|
|
133
130
|
units=units,
|
|
134
131
|
)
|
|
135
132
|
|
|
136
|
-
d:
|
|
133
|
+
d: Coordinate | None = None
|
|
137
134
|
|
|
138
135
|
d = self._is_point(coordinate, attributes)
|
|
139
136
|
if d is not None:
|
|
@@ -214,7 +211,7 @@ class CoordinateGuesser(ABC):
|
|
|
214
211
|
|
|
215
212
|
raise NotImplementedError(f"Cannot establish grid {coordinates}")
|
|
216
213
|
|
|
217
|
-
def _check_dims(self, variable: Any, x_or_lon: Any, y_or_lat: Any) ->
|
|
214
|
+
def _check_dims(self, variable: Any, x_or_lon: Any, y_or_lat: Any) -> tuple[Any, bool]:
|
|
218
215
|
"""Checks the dimensions of the variable against the coordinates.
|
|
219
216
|
|
|
220
217
|
Parameters
|
|
@@ -362,7 +359,7 @@ class CoordinateGuesser(ABC):
|
|
|
362
359
|
grid_mapping = self.ds.attrs["crs"]
|
|
363
360
|
LOG.warning(f"Using CRS {grid_mapping} from global attributes")
|
|
364
361
|
|
|
365
|
-
grid:
|
|
362
|
+
grid: Grid | None = None
|
|
366
363
|
if grid_mapping is not None:
|
|
367
364
|
|
|
368
365
|
grid_mapping = dict(self.ds[grid_mapping].attrs)
|
|
@@ -380,7 +377,7 @@ class CoordinateGuesser(ABC):
|
|
|
380
377
|
raise NotImplementedError(f"Unstructured grid {x.name} {y.name}")
|
|
381
378
|
|
|
382
379
|
@abstractmethod
|
|
383
|
-
def _is_longitude(self, c: xr.DataArray, attributes: CoordinateAttributes) ->
|
|
380
|
+
def _is_longitude(self, c: xr.DataArray, attributes: CoordinateAttributes) -> LongitudeCoordinate | None:
|
|
384
381
|
"""Checks if the coordinate is a longitude.
|
|
385
382
|
|
|
386
383
|
Parameters
|
|
@@ -398,11 +395,11 @@ class CoordinateGuesser(ABC):
|
|
|
398
395
|
pass
|
|
399
396
|
|
|
400
397
|
@abstractmethod
|
|
401
|
-
def _is_point(self, c: xr.DataArray, attributes: CoordinateAttributes) ->
|
|
398
|
+
def _is_point(self, c: xr.DataArray, attributes: CoordinateAttributes) -> PointCoordinate | None:
|
|
402
399
|
pass
|
|
403
400
|
|
|
404
401
|
@abstractmethod
|
|
405
|
-
def _is_latitude(self, c: xr.DataArray, attributes: CoordinateAttributes) ->
|
|
402
|
+
def _is_latitude(self, c: xr.DataArray, attributes: CoordinateAttributes) -> LatitudeCoordinate | None:
|
|
406
403
|
"""Checks if the coordinate is a latitude.
|
|
407
404
|
|
|
408
405
|
Parameters
|
|
@@ -420,7 +417,7 @@ class CoordinateGuesser(ABC):
|
|
|
420
417
|
pass
|
|
421
418
|
|
|
422
419
|
@abstractmethod
|
|
423
|
-
def _is_x(self, c: xr.DataArray, attributes: CoordinateAttributes) ->
|
|
420
|
+
def _is_x(self, c: xr.DataArray, attributes: CoordinateAttributes) -> XCoordinate | None:
|
|
424
421
|
"""Checks if the coordinate is an x coordinate.
|
|
425
422
|
|
|
426
423
|
Parameters
|
|
@@ -438,7 +435,7 @@ class CoordinateGuesser(ABC):
|
|
|
438
435
|
pass
|
|
439
436
|
|
|
440
437
|
@abstractmethod
|
|
441
|
-
def _is_y(self, c: xr.DataArray, attributes: CoordinateAttributes) ->
|
|
438
|
+
def _is_y(self, c: xr.DataArray, attributes: CoordinateAttributes) -> YCoordinate | None:
|
|
442
439
|
"""Checks if the coordinate is a y coordinate.
|
|
443
440
|
|
|
444
441
|
Parameters
|
|
@@ -456,7 +453,7 @@ class CoordinateGuesser(ABC):
|
|
|
456
453
|
pass
|
|
457
454
|
|
|
458
455
|
@abstractmethod
|
|
459
|
-
def _is_time(self, c: xr.DataArray, attributes: CoordinateAttributes) ->
|
|
456
|
+
def _is_time(self, c: xr.DataArray, attributes: CoordinateAttributes) -> TimeCoordinate | None:
|
|
460
457
|
"""Checks if the coordinate is a time coordinate.
|
|
461
458
|
|
|
462
459
|
Parameters
|
|
@@ -474,7 +471,7 @@ class CoordinateGuesser(ABC):
|
|
|
474
471
|
pass
|
|
475
472
|
|
|
476
473
|
@abstractmethod
|
|
477
|
-
def _is_date(self, c: xr.DataArray, attributes: CoordinateAttributes) ->
|
|
474
|
+
def _is_date(self, c: xr.DataArray, attributes: CoordinateAttributes) -> DateCoordinate | None:
|
|
478
475
|
"""Checks if the coordinate is a date coordinate.
|
|
479
476
|
|
|
480
477
|
Parameters
|
|
@@ -492,7 +489,7 @@ class CoordinateGuesser(ABC):
|
|
|
492
489
|
pass
|
|
493
490
|
|
|
494
491
|
@abstractmethod
|
|
495
|
-
def _is_step(self, c: xr.DataArray, attributes: CoordinateAttributes) ->
|
|
492
|
+
def _is_step(self, c: xr.DataArray, attributes: CoordinateAttributes) -> StepCoordinate | None:
|
|
496
493
|
"""Checks if the coordinate is a step coordinate.
|
|
497
494
|
|
|
498
495
|
Parameters
|
|
@@ -510,7 +507,7 @@ class CoordinateGuesser(ABC):
|
|
|
510
507
|
pass
|
|
511
508
|
|
|
512
509
|
@abstractmethod
|
|
513
|
-
def _is_level(self, c: xr.DataArray, attributes: CoordinateAttributes) ->
|
|
510
|
+
def _is_level(self, c: xr.DataArray, attributes: CoordinateAttributes) -> LevelCoordinate | None:
|
|
514
511
|
"""Checks if the coordinate is a level coordinate.
|
|
515
512
|
|
|
516
513
|
Parameters
|
|
@@ -528,7 +525,7 @@ class CoordinateGuesser(ABC):
|
|
|
528
525
|
pass
|
|
529
526
|
|
|
530
527
|
@abstractmethod
|
|
531
|
-
def _is_number(self, c: xr.DataArray, attributes: CoordinateAttributes) ->
|
|
528
|
+
def _is_number(self, c: xr.DataArray, attributes: CoordinateAttributes) -> EnsembleCoordinate | None:
|
|
532
529
|
"""Checks if the coordinate is an ensemble coordinate.
|
|
533
530
|
|
|
534
531
|
Parameters
|
|
@@ -559,7 +556,7 @@ class DefaultCoordinateGuesser(CoordinateGuesser):
|
|
|
559
556
|
"""
|
|
560
557
|
super().__init__(ds)
|
|
561
558
|
|
|
562
|
-
def _is_point(self, c: xr.DataArray, attributes: CoordinateAttributes) ->
|
|
559
|
+
def _is_point(self, c: xr.DataArray, attributes: CoordinateAttributes) -> PointCoordinate | None:
|
|
563
560
|
if attributes.standard_name in ["cell", "station", "poi", "point"]:
|
|
564
561
|
return PointCoordinate(c)
|
|
565
562
|
|
|
@@ -568,7 +565,7 @@ class DefaultCoordinateGuesser(CoordinateGuesser):
|
|
|
568
565
|
|
|
569
566
|
return None
|
|
570
567
|
|
|
571
|
-
def _is_longitude(self, c: xr.DataArray, attributes: CoordinateAttributes) ->
|
|
568
|
+
def _is_longitude(self, c: xr.DataArray, attributes: CoordinateAttributes) -> LongitudeCoordinate | None:
|
|
572
569
|
"""Checks if the coordinate is a longitude.
|
|
573
570
|
|
|
574
571
|
Parameters
|
|
@@ -597,7 +594,7 @@ class DefaultCoordinateGuesser(CoordinateGuesser):
|
|
|
597
594
|
|
|
598
595
|
return None
|
|
599
596
|
|
|
600
|
-
def _is_latitude(self, c: xr.DataArray, attributes: CoordinateAttributes) ->
|
|
597
|
+
def _is_latitude(self, c: xr.DataArray, attributes: CoordinateAttributes) -> LatitudeCoordinate | None:
|
|
601
598
|
"""Checks if the coordinate is a latitude.
|
|
602
599
|
|
|
603
600
|
Parameters
|
|
@@ -626,7 +623,7 @@ class DefaultCoordinateGuesser(CoordinateGuesser):
|
|
|
626
623
|
|
|
627
624
|
return None
|
|
628
625
|
|
|
629
|
-
def _is_x(self, c: xr.DataArray, attributes: CoordinateAttributes) ->
|
|
626
|
+
def _is_x(self, c: xr.DataArray, attributes: CoordinateAttributes) -> XCoordinate | None:
|
|
630
627
|
"""Checks if the coordinate is an x coordinate.
|
|
631
628
|
|
|
632
629
|
Parameters
|
|
@@ -649,7 +646,7 @@ class DefaultCoordinateGuesser(CoordinateGuesser):
|
|
|
649
646
|
|
|
650
647
|
return None
|
|
651
648
|
|
|
652
|
-
def _is_y(self, c: xr.DataArray, attributes: CoordinateAttributes) ->
|
|
649
|
+
def _is_y(self, c: xr.DataArray, attributes: CoordinateAttributes) -> YCoordinate | None:
|
|
653
650
|
"""Checks if the coordinate is a y coordinate.
|
|
654
651
|
|
|
655
652
|
Parameters
|
|
@@ -672,7 +669,7 @@ class DefaultCoordinateGuesser(CoordinateGuesser):
|
|
|
672
669
|
|
|
673
670
|
return None
|
|
674
671
|
|
|
675
|
-
def _is_time(self, c: xr.DataArray, attributes: CoordinateAttributes) ->
|
|
672
|
+
def _is_time(self, c: xr.DataArray, attributes: CoordinateAttributes) -> TimeCoordinate | None:
|
|
676
673
|
"""Checks if the coordinate is a time coordinate.
|
|
677
674
|
|
|
678
675
|
Parameters
|
|
@@ -695,7 +692,7 @@ class DefaultCoordinateGuesser(CoordinateGuesser):
|
|
|
695
692
|
|
|
696
693
|
return None
|
|
697
694
|
|
|
698
|
-
def _is_date(self, c: xr.DataArray, attributes: CoordinateAttributes) ->
|
|
695
|
+
def _is_date(self, c: xr.DataArray, attributes: CoordinateAttributes) -> DateCoordinate | None:
|
|
699
696
|
"""Checks if the coordinate is a date coordinate.
|
|
700
697
|
|
|
701
698
|
Parameters
|
|
@@ -718,7 +715,7 @@ class DefaultCoordinateGuesser(CoordinateGuesser):
|
|
|
718
715
|
|
|
719
716
|
return None
|
|
720
717
|
|
|
721
|
-
def _is_step(self, c: xr.DataArray, attributes: CoordinateAttributes) ->
|
|
718
|
+
def _is_step(self, c: xr.DataArray, attributes: CoordinateAttributes) -> StepCoordinate | None:
|
|
722
719
|
"""Checks if the coordinate is a step coordinate.
|
|
723
720
|
|
|
724
721
|
Parameters
|
|
@@ -744,7 +741,7 @@ class DefaultCoordinateGuesser(CoordinateGuesser):
|
|
|
744
741
|
|
|
745
742
|
return None
|
|
746
743
|
|
|
747
|
-
def _is_level(self, c: xr.DataArray, attributes: CoordinateAttributes) ->
|
|
744
|
+
def _is_level(self, c: xr.DataArray, attributes: CoordinateAttributes) -> LevelCoordinate | None:
|
|
748
745
|
"""Checks if the coordinate is a level coordinate.
|
|
749
746
|
|
|
750
747
|
Parameters
|
|
@@ -762,6 +759,9 @@ class DefaultCoordinateGuesser(CoordinateGuesser):
|
|
|
762
759
|
if attributes.standard_name == "atmosphere_hybrid_sigma_pressure_coordinate":
|
|
763
760
|
return LevelCoordinate(c, "ml")
|
|
764
761
|
|
|
762
|
+
if attributes.standard_name == "model_level_number":
|
|
763
|
+
return LevelCoordinate(c, "ml")
|
|
764
|
+
|
|
765
765
|
if attributes.long_name == "height" and attributes.units == "m":
|
|
766
766
|
return LevelCoordinate(c, "height")
|
|
767
767
|
|
|
@@ -782,7 +782,7 @@ class DefaultCoordinateGuesser(CoordinateGuesser):
|
|
|
782
782
|
|
|
783
783
|
return None
|
|
784
784
|
|
|
785
|
-
def _is_number(self, c: xr.DataArray, attributes: CoordinateAttributes) ->
|
|
785
|
+
def _is_number(self, c: xr.DataArray, attributes: CoordinateAttributes) -> EnsembleCoordinate | None:
|
|
786
786
|
"""Checks if the coordinate is an ensemble coordinate.
|
|
787
787
|
|
|
788
788
|
Parameters
|
|
@@ -806,7 +806,7 @@ class DefaultCoordinateGuesser(CoordinateGuesser):
|
|
|
806
806
|
class FlavourCoordinateGuesser(CoordinateGuesser):
|
|
807
807
|
"""Implementation of CoordinateGuesser that uses a flavour for guessing."""
|
|
808
808
|
|
|
809
|
-
def __init__(self, ds: xr.Dataset, flavour:
|
|
809
|
+
def __init__(self, ds: xr.Dataset, flavour: dict[str, Any]) -> None:
|
|
810
810
|
"""Initializes the FlavourCoordinateGuesser.
|
|
811
811
|
|
|
812
812
|
Parameters
|
|
@@ -819,7 +819,7 @@ class FlavourCoordinateGuesser(CoordinateGuesser):
|
|
|
819
819
|
super().__init__(ds)
|
|
820
820
|
self.flavour = flavour
|
|
821
821
|
|
|
822
|
-
def _match(self, c: xr.DataArray, key: str, attributes: CoordinateAttributes) ->
|
|
822
|
+
def _match(self, c: xr.DataArray, key: str, attributes: CoordinateAttributes) -> dict[str, Any] | None:
|
|
823
823
|
"""Matches the coordinate against the flavour rules.
|
|
824
824
|
|
|
825
825
|
Parameters
|
|
@@ -854,7 +854,7 @@ class FlavourCoordinateGuesser(CoordinateGuesser):
|
|
|
854
854
|
|
|
855
855
|
return None
|
|
856
856
|
|
|
857
|
-
def _is_longitude(self, c: xr.DataArray, attributes: CoordinateAttributes) ->
|
|
857
|
+
def _is_longitude(self, c: xr.DataArray, attributes: CoordinateAttributes) -> LongitudeCoordinate | None:
|
|
858
858
|
"""Checks if the coordinate is a longitude using the flavour rules.
|
|
859
859
|
|
|
860
860
|
Parameters
|
|
@@ -874,7 +874,7 @@ class FlavourCoordinateGuesser(CoordinateGuesser):
|
|
|
874
874
|
|
|
875
875
|
return None
|
|
876
876
|
|
|
877
|
-
def _is_latitude(self, c: xr.DataArray, attributes: CoordinateAttributes) ->
|
|
877
|
+
def _is_latitude(self, c: xr.DataArray, attributes: CoordinateAttributes) -> LatitudeCoordinate | None:
|
|
878
878
|
"""Checks if the coordinate is a latitude using the flavour rules.
|
|
879
879
|
|
|
880
880
|
Parameters
|
|
@@ -894,7 +894,7 @@ class FlavourCoordinateGuesser(CoordinateGuesser):
|
|
|
894
894
|
|
|
895
895
|
return None
|
|
896
896
|
|
|
897
|
-
def _is_x(self, c: xr.DataArray, attributes: CoordinateAttributes) ->
|
|
897
|
+
def _is_x(self, c: xr.DataArray, attributes: CoordinateAttributes) -> XCoordinate | None:
|
|
898
898
|
"""Checks if the coordinate is an x coordinate using the flavour rules.
|
|
899
899
|
|
|
900
900
|
Parameters
|
|
@@ -914,7 +914,7 @@ class FlavourCoordinateGuesser(CoordinateGuesser):
|
|
|
914
914
|
|
|
915
915
|
return None
|
|
916
916
|
|
|
917
|
-
def _is_y(self, c: xr.DataArray, attributes: CoordinateAttributes) ->
|
|
917
|
+
def _is_y(self, c: xr.DataArray, attributes: CoordinateAttributes) -> YCoordinate | None:
|
|
918
918
|
"""Checks if the coordinate is a y coordinate using the flavour rules.
|
|
919
919
|
|
|
920
920
|
Parameters
|
|
@@ -934,7 +934,7 @@ class FlavourCoordinateGuesser(CoordinateGuesser):
|
|
|
934
934
|
|
|
935
935
|
return None
|
|
936
936
|
|
|
937
|
-
def _is_time(self, c: xr.DataArray, attributes: CoordinateAttributes) ->
|
|
937
|
+
def _is_time(self, c: xr.DataArray, attributes: CoordinateAttributes) -> TimeCoordinate | None:
|
|
938
938
|
"""Checks if the coordinate is a time coordinate using the flavour rules.
|
|
939
939
|
|
|
940
940
|
Parameters
|
|
@@ -954,7 +954,7 @@ class FlavourCoordinateGuesser(CoordinateGuesser):
|
|
|
954
954
|
|
|
955
955
|
return None
|
|
956
956
|
|
|
957
|
-
def _is_step(self, c: xr.DataArray, attributes: CoordinateAttributes) ->
|
|
957
|
+
def _is_step(self, c: xr.DataArray, attributes: CoordinateAttributes) -> StepCoordinate | None:
|
|
958
958
|
"""Checks if the coordinate is a step coordinate using the flavour rules.
|
|
959
959
|
|
|
960
960
|
Parameters
|
|
@@ -974,7 +974,7 @@ class FlavourCoordinateGuesser(CoordinateGuesser):
|
|
|
974
974
|
|
|
975
975
|
return None
|
|
976
976
|
|
|
977
|
-
def _is_date(self, c: xr.DataArray, attributes: CoordinateAttributes) ->
|
|
977
|
+
def _is_date(self, c: xr.DataArray, attributes: CoordinateAttributes) -> DateCoordinate | None:
|
|
978
978
|
"""Checks if the coordinate is a date coordinate using the flavour rules.
|
|
979
979
|
|
|
980
980
|
Parameters
|
|
@@ -994,7 +994,7 @@ class FlavourCoordinateGuesser(CoordinateGuesser):
|
|
|
994
994
|
|
|
995
995
|
return None
|
|
996
996
|
|
|
997
|
-
def _is_level(self, c: xr.DataArray, attributes: CoordinateAttributes) ->
|
|
997
|
+
def _is_level(self, c: xr.DataArray, attributes: CoordinateAttributes) -> LevelCoordinate | None:
|
|
998
998
|
"""Checks if the coordinate is a level coordinate using the flavour rules.
|
|
999
999
|
|
|
1000
1000
|
Parameters
|
|
@@ -1039,7 +1039,7 @@ class FlavourCoordinateGuesser(CoordinateGuesser):
|
|
|
1039
1039
|
|
|
1040
1040
|
raise NotImplementedError(f"levtype for {c=}")
|
|
1041
1041
|
|
|
1042
|
-
def _is_number(self, c: xr.DataArray, attributes: CoordinateAttributes) ->
|
|
1042
|
+
def _is_number(self, c: xr.DataArray, attributes: CoordinateAttributes) -> EnsembleCoordinate | None:
|
|
1043
1043
|
"""Checks if the coordinate is an ensemble coordinate using the flavour rules.
|
|
1044
1044
|
|
|
1045
1045
|
Parameters
|
|
@@ -1059,7 +1059,7 @@ class FlavourCoordinateGuesser(CoordinateGuesser):
|
|
|
1059
1059
|
|
|
1060
1060
|
return None
|
|
1061
1061
|
|
|
1062
|
-
def _is_point(self, c: xr.DataArray, attributes: CoordinateAttributes) ->
|
|
1062
|
+
def _is_point(self, c: xr.DataArray, attributes: CoordinateAttributes) -> PointCoordinate | None:
|
|
1063
1063
|
"""Checks if the coordinate is a point coordinate using the flavour rules.
|
|
1064
1064
|
|
|
1065
1065
|
Parameters
|
|
@@ -13,7 +13,6 @@ from abc import ABC
|
|
|
13
13
|
from abc import abstractmethod
|
|
14
14
|
from functools import cached_property
|
|
15
15
|
from typing import Any
|
|
16
|
-
from typing import Tuple
|
|
17
16
|
|
|
18
17
|
import numpy as np
|
|
19
18
|
|
|
@@ -38,7 +37,7 @@ class Grid(ABC):
|
|
|
38
37
|
|
|
39
38
|
@property
|
|
40
39
|
@abstractmethod
|
|
41
|
-
def grid_points(self) ->
|
|
40
|
+
def grid_points(self) -> tuple[Any, Any]:
|
|
42
41
|
"""Get the grid points."""
|
|
43
42
|
pass
|
|
44
43
|
|
|
@@ -85,7 +84,7 @@ class MeshedGrid(LatLonGrid):
|
|
|
85
84
|
"""Grid class for meshed latitude and longitude coordinates."""
|
|
86
85
|
|
|
87
86
|
@cached_property
|
|
88
|
-
def grid_points(self) ->
|
|
87
|
+
def grid_points(self) -> tuple[Any, Any]:
|
|
89
88
|
"""Get the grid points for the meshed grid."""
|
|
90
89
|
|
|
91
90
|
if self.variable_dims == (self.lon.variable.name, self.lat.variable.name):
|
|
@@ -128,7 +127,7 @@ class UnstructuredGrid(LatLonGrid):
|
|
|
128
127
|
assert set(self.variable_dims) == set(self.grid_dims), (self.variable_dims, self.grid_dims)
|
|
129
128
|
|
|
130
129
|
@cached_property
|
|
131
|
-
def grid_points(self) ->
|
|
130
|
+
def grid_points(self) -> tuple[Any, Any]:
|
|
132
131
|
"""Get the grid points for the unstructured grid."""
|
|
133
132
|
assert 1 <= len(self.variable_dims) <= 2
|
|
134
133
|
|
|
@@ -191,7 +190,7 @@ class MeshProjectionGrid(ProjectionGrid):
|
|
|
191
190
|
"""Grid class for meshed projected coordinates."""
|
|
192
191
|
|
|
193
192
|
@cached_property
|
|
194
|
-
def grid_points(self) ->
|
|
193
|
+
def grid_points(self) -> tuple[Any, Any]:
|
|
195
194
|
"""Get the grid points for the mesh projection grid."""
|
|
196
195
|
transformer = self.transformer()
|
|
197
196
|
xv, yv = np.meshgrid(self.x.variable.values, self.y.variable.values) # , indexing="ij")
|
|
@@ -199,10 +198,17 @@ class MeshProjectionGrid(ProjectionGrid):
|
|
|
199
198
|
return lat.flatten(), lon.flatten()
|
|
200
199
|
|
|
201
200
|
|
|
202
|
-
class UnstructuredProjectionGrid(
|
|
201
|
+
class UnstructuredProjectionGrid(ProjectionGrid):
|
|
203
202
|
"""Grid class for unstructured projected coordinates."""
|
|
204
203
|
|
|
205
204
|
@cached_property
|
|
206
|
-
def grid_points(self) ->
|
|
207
|
-
"""Get the grid points for the unstructured
|
|
208
|
-
|
|
205
|
+
def grid_points(self) -> tuple[Any, Any]:
|
|
206
|
+
"""Get the grid points for the unstructured grid."""
|
|
207
|
+
if self.projection == "epsg:4326":
|
|
208
|
+
# WGS84, no transformation needed
|
|
209
|
+
return self.y.variable.values.flatten(), self.x.variable.values.flatten()
|
|
210
|
+
transformer = self.transformer()
|
|
211
|
+
xv, yv = np.meshgrid(self.x.variable.values, self.y.variable.values) # , indexing="ij")
|
|
212
|
+
lon, lat = transformer.transform(xv, yv)
|
|
213
|
+
|
|
214
|
+
return lat.flatten(), lon.flatten()
|