anemoi-datasets 0.5.26__py3-none-any.whl → 0.5.27__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/validate.py +59 -0
- anemoi/datasets/compute/recentre.py +3 -6
- anemoi/datasets/create/__init__.py +22 -25
- anemoi/datasets/create/check.py +10 -12
- anemoi/datasets/create/chunks.py +1 -2
- anemoi/datasets/create/config.py +3 -6
- anemoi/datasets/create/filter.py +1 -2
- anemoi/datasets/create/input/__init__.py +1 -2
- anemoi/datasets/create/input/action.py +3 -5
- anemoi/datasets/create/input/concat.py +5 -8
- anemoi/datasets/create/input/context.py +3 -6
- anemoi/datasets/create/input/data_sources.py +5 -8
- anemoi/datasets/create/input/empty.py +1 -2
- anemoi/datasets/create/input/filter.py +2 -3
- anemoi/datasets/create/input/function.py +1 -2
- anemoi/datasets/create/input/join.py +4 -5
- anemoi/datasets/create/input/misc.py +4 -6
- anemoi/datasets/create/input/repeated_dates.py +13 -18
- anemoi/datasets/create/input/result.py +29 -33
- anemoi/datasets/create/input/step.py +4 -8
- anemoi/datasets/create/input/template.py +3 -4
- 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 +47 -52
- anemoi/datasets/create/sources/accumulations2.py +4 -8
- anemoi/datasets/create/sources/constants.py +1 -3
- anemoi/datasets/create/sources/empty.py +1 -2
- anemoi/datasets/create/sources/fdb.py +133 -0
- anemoi/datasets/create/sources/forcings.py +1 -2
- anemoi/datasets/create/sources/grib.py +6 -10
- anemoi/datasets/create/sources/grib_index.py +13 -15
- anemoi/datasets/create/sources/hindcasts.py +2 -5
- anemoi/datasets/create/sources/legacy.py +1 -1
- anemoi/datasets/create/sources/mars.py +17 -21
- anemoi/datasets/create/sources/netcdf.py +1 -2
- anemoi/datasets/create/sources/opendap.py +1 -3
- anemoi/datasets/create/sources/patterns.py +4 -6
- anemoi/datasets/create/sources/recentre.py +8 -11
- anemoi/datasets/create/sources/source.py +3 -6
- anemoi/datasets/create/sources/tendencies.py +2 -5
- anemoi/datasets/create/sources/xarray.py +4 -6
- anemoi/datasets/create/sources/xarray_support/__init__.py +12 -13
- anemoi/datasets/create/sources/xarray_support/coordinates.py +8 -12
- anemoi/datasets/create/sources/xarray_support/field.py +16 -12
- 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 +1 -2
- anemoi/datasets/create/sources/zenodo.py +3 -5
- 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 +1 -2
- 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 +42 -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 -16
- 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/select.py +13 -16
- anemoi/datasets/data/statistics.py +4 -7
- anemoi/datasets/data/stores.py +16 -21
- 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 +13 -18
- anemoi/datasets/dates/groups.py +7 -10
- anemoi/datasets/grids.py +5 -9
- anemoi/datasets/testing.py +93 -7
- anemoi/datasets/validate.py +598 -0
- {anemoi_datasets-0.5.26.dist-info → anemoi_datasets-0.5.27.dist-info}/METADATA +4 -4
- anemoi_datasets-0.5.27.dist-info/RECORD +134 -0
- anemoi/datasets/utils/__init__.py +0 -8
- anemoi_datasets-0.5.26.dist-info/RECORD +0 -131
- {anemoi_datasets-0.5.26.dist-info → anemoi_datasets-0.5.27.dist-info}/WHEEL +0 -0
- {anemoi_datasets-0.5.26.dist-info → anemoi_datasets-0.5.27.dist-info}/entry_points.txt +0 -0
- {anemoi_datasets-0.5.26.dist-info → anemoi_datasets-0.5.27.dist-info}/licenses/LICENSE +0 -0
- {anemoi_datasets-0.5.26.dist-info → anemoi_datasets-0.5.27.dist-info}/top_level.txt +0 -0
|
@@ -10,10 +10,6 @@
|
|
|
10
10
|
import datetime
|
|
11
11
|
import logging
|
|
12
12
|
from typing import Any
|
|
13
|
-
from typing import Dict
|
|
14
|
-
from typing import List
|
|
15
|
-
from typing import Optional
|
|
16
|
-
from typing import Union
|
|
17
13
|
|
|
18
14
|
import earthkit.data as ekd
|
|
19
15
|
import xarray as xr
|
|
@@ -27,7 +23,7 @@ from .fieldlist import XarrayFieldList
|
|
|
27
23
|
LOG = logging.getLogger(__name__)
|
|
28
24
|
|
|
29
25
|
|
|
30
|
-
def check(what: str, ds: xr.Dataset, paths:
|
|
26
|
+
def check(what: str, ds: xr.Dataset, paths: list[str], **kwargs: Any) -> None:
|
|
31
27
|
"""Checks if the dataset has the expected number of fields.
|
|
32
28
|
|
|
33
29
|
Parameters
|
|
@@ -53,12 +49,12 @@ def check(what: str, ds: xr.Dataset, paths: List[str], **kwargs: Any) -> None:
|
|
|
53
49
|
def load_one(
|
|
54
50
|
emoji: str,
|
|
55
51
|
context: Any,
|
|
56
|
-
dates:
|
|
57
|
-
dataset:
|
|
52
|
+
dates: list[str],
|
|
53
|
+
dataset: str | xr.Dataset,
|
|
58
54
|
*,
|
|
59
|
-
options:
|
|
60
|
-
flavour:
|
|
61
|
-
patch:
|
|
55
|
+
options: dict[str, Any] | None = None,
|
|
56
|
+
flavour: str | None = None,
|
|
57
|
+
patch: Any | None = None,
|
|
62
58
|
**kwargs: Any,
|
|
63
59
|
) -> ekd.FieldList:
|
|
64
60
|
"""Loads a single dataset.
|
|
@@ -97,7 +93,10 @@ def load_one(
|
|
|
97
93
|
# If the dataset is a zarr store, we need to use the zarr engine
|
|
98
94
|
options["engine"] = "zarr"
|
|
99
95
|
|
|
100
|
-
|
|
96
|
+
if isinstance(dataset, xr.Dataset):
|
|
97
|
+
data = dataset
|
|
98
|
+
else:
|
|
99
|
+
data = xr.open_dataset(dataset, **options)
|
|
101
100
|
|
|
102
101
|
fs = XarrayFieldList.from_xarray(data, flavour=flavour, patch=patch)
|
|
103
102
|
|
|
@@ -124,7 +123,7 @@ def load_one(
|
|
|
124
123
|
return result
|
|
125
124
|
|
|
126
125
|
|
|
127
|
-
def load_many(emoji: str, context: Any, dates:
|
|
126
|
+
def load_many(emoji: str, context: Any, dates: list[datetime.datetime], pattern: str, **kwargs: Any) -> ekd.FieldList:
|
|
128
127
|
"""Loads multiple datasets.
|
|
129
128
|
|
|
130
129
|
Parameters
|
|
@@ -154,7 +153,7 @@ def load_many(emoji: str, context: Any, dates: List[datetime.datetime], pattern:
|
|
|
154
153
|
|
|
155
154
|
|
|
156
155
|
@legacy_source("xarray")
|
|
157
|
-
def execute(context: Any, dates:
|
|
156
|
+
def execute(context: Any, dates: list[str], url: str, *args: Any, **kwargs: Any) -> ekd.FieldList:
|
|
158
157
|
"""Executes the loading of datasets.
|
|
159
158
|
|
|
160
159
|
Parameters
|
|
@@ -13,10 +13,6 @@ from __future__ import annotations
|
|
|
13
13
|
import datetime
|
|
14
14
|
import logging
|
|
15
15
|
from typing import Any
|
|
16
|
-
from typing import Dict
|
|
17
|
-
from typing import Optional
|
|
18
|
-
from typing import Tuple
|
|
19
|
-
from typing import Union
|
|
20
16
|
|
|
21
17
|
import numpy as np
|
|
22
18
|
import xarray as xr
|
|
@@ -107,7 +103,7 @@ class Coordinate:
|
|
|
107
103
|
"""
|
|
108
104
|
self.variable = variable
|
|
109
105
|
self.scalar = is_scalar(variable)
|
|
110
|
-
self.kwargs:
|
|
106
|
+
self.kwargs: dict[str, Any] = {} # Used when creating a new coordinate (reduced method)
|
|
111
107
|
|
|
112
108
|
def __len__(self) -> int:
|
|
113
109
|
"""Get the length of the coordinate.
|
|
@@ -127,7 +123,7 @@ class Coordinate:
|
|
|
127
123
|
str
|
|
128
124
|
The string representation of the coordinate.
|
|
129
125
|
"""
|
|
130
|
-
return "
|
|
126
|
+
return "{}[name={},values={},shape={}]".format(
|
|
131
127
|
self.__class__.__name__,
|
|
132
128
|
self.variable.name,
|
|
133
129
|
self.variable.values if self.scalar else len(self),
|
|
@@ -152,7 +148,7 @@ class Coordinate:
|
|
|
152
148
|
**self.kwargs,
|
|
153
149
|
)
|
|
154
150
|
|
|
155
|
-
def index(self, value:
|
|
151
|
+
def index(self, value: Any | list | tuple) -> int | list | None:
|
|
156
152
|
"""Return the index of the value in the coordinate.
|
|
157
153
|
|
|
158
154
|
Parameters
|
|
@@ -172,7 +168,7 @@ class Coordinate:
|
|
|
172
168
|
return self._index_multiple(value)
|
|
173
169
|
return self._index_single(value)
|
|
174
170
|
|
|
175
|
-
def _index_single(self, value: Any) ->
|
|
171
|
+
def _index_single(self, value: Any) -> int | None:
|
|
176
172
|
"""Return the index of a single value in the coordinate.
|
|
177
173
|
|
|
178
174
|
Parameters
|
|
@@ -205,7 +201,7 @@ class Coordinate:
|
|
|
205
201
|
|
|
206
202
|
return None
|
|
207
203
|
|
|
208
|
-
def _index_multiple(self, value: list) ->
|
|
204
|
+
def _index_multiple(self, value: list) -> list | None:
|
|
209
205
|
"""Return the indices of multiple values in the coordinate.
|
|
210
206
|
|
|
211
207
|
Parameters
|
|
@@ -275,7 +271,7 @@ class TimeCoordinate(Coordinate):
|
|
|
275
271
|
is_time = True
|
|
276
272
|
mars_names = ("valid_datetime",)
|
|
277
273
|
|
|
278
|
-
def index(self, time: datetime.datetime) ->
|
|
274
|
+
def index(self, time: datetime.datetime) -> int | None:
|
|
279
275
|
"""Return the index of the time in the coordinate.
|
|
280
276
|
|
|
281
277
|
Parameters
|
|
@@ -297,7 +293,7 @@ class DateCoordinate(Coordinate):
|
|
|
297
293
|
is_date = True
|
|
298
294
|
mars_names = ("date",)
|
|
299
295
|
|
|
300
|
-
def index(self, date: datetime.datetime) ->
|
|
296
|
+
def index(self, date: datetime.datetime) -> int | None:
|
|
301
297
|
"""Return the index of the date in the coordinate.
|
|
302
298
|
|
|
303
299
|
Parameters
|
|
@@ -436,7 +432,7 @@ class ScalarCoordinate(Coordinate):
|
|
|
436
432
|
is_grid = False
|
|
437
433
|
|
|
438
434
|
@property
|
|
439
|
-
def mars_names(self) ->
|
|
435
|
+
def mars_names(self) -> tuple[str, ...]:
|
|
440
436
|
"""Get the MARS names for the coordinate."""
|
|
441
437
|
return (self.variable.name,)
|
|
442
438
|
|
|
@@ -12,9 +12,6 @@ import datetime
|
|
|
12
12
|
import logging
|
|
13
13
|
from functools import cached_property
|
|
14
14
|
from typing import Any
|
|
15
|
-
from typing import Dict
|
|
16
|
-
from typing import Optional
|
|
17
|
-
from typing import Tuple
|
|
18
15
|
|
|
19
16
|
from earthkit.data import Field
|
|
20
17
|
from earthkit.data.core.fieldlist import math
|
|
@@ -80,12 +77,21 @@ class XArrayField(Field):
|
|
|
80
77
|
# Copy the metadata from the owner
|
|
81
78
|
self._md = owner._metadata.copy()
|
|
82
79
|
|
|
80
|
+
aliases = {}
|
|
83
81
|
for coord_name, coord_value in self.selection.coords.items():
|
|
84
82
|
if is_scalar(coord_value):
|
|
85
83
|
# Extract the single value from the scalar dimension
|
|
86
84
|
# and store it in the metadata
|
|
87
85
|
coordinate = owner.by_name[coord_name]
|
|
88
|
-
|
|
86
|
+
normalised = coordinate.normalise(extract_single_value(coord_value))
|
|
87
|
+
self._md[coord_name] = normalised
|
|
88
|
+
for alias in coordinate.mars_names:
|
|
89
|
+
aliases[alias] = normalised
|
|
90
|
+
|
|
91
|
+
# Add metadata aliases (e.g. levelist == level) only if they are not already present
|
|
92
|
+
for alias, value in aliases.items():
|
|
93
|
+
if alias not in self._md:
|
|
94
|
+
self._md[alias] = value
|
|
89
95
|
|
|
90
96
|
# By now, the only dimensions should be latitude and longitude
|
|
91
97
|
self._shape = tuple(list(self.selection.shape)[-2:])
|
|
@@ -93,13 +99,11 @@ class XArrayField(Field):
|
|
|
93
99
|
raise ValueError(f"Invalid shape for selection {self._shape=}, {self.selection.shape=} {self.selection=}")
|
|
94
100
|
|
|
95
101
|
@property
|
|
96
|
-
def shape(self) ->
|
|
102
|
+
def shape(self) -> tuple[int, int]:
|
|
97
103
|
"""Return the shape of the field."""
|
|
98
104
|
return self._shape
|
|
99
105
|
|
|
100
|
-
def to_numpy(
|
|
101
|
-
self, flatten: bool = False, dtype: Optional[type] = None, index: Optional[int] = None
|
|
102
|
-
) -> NDArray[Any]:
|
|
106
|
+
def to_numpy(self, flatten: bool = False, dtype: type | None = None, index: int | None = None) -> NDArray[Any]:
|
|
103
107
|
"""Convert the selection to a numpy array.
|
|
104
108
|
|
|
105
109
|
Returns
|
|
@@ -137,7 +141,7 @@ class XArrayField(Field):
|
|
|
137
141
|
"""Return the grid points of the field."""
|
|
138
142
|
return self.owner.grid_points()
|
|
139
143
|
|
|
140
|
-
def to_latlon(self, flatten: bool = True) ->
|
|
144
|
+
def to_latlon(self, flatten: bool = True) -> dict[str, Any]:
|
|
141
145
|
"""Convert the selection to latitude and longitude coordinates.
|
|
142
146
|
|
|
143
147
|
Returns
|
|
@@ -154,7 +158,7 @@ class XArrayField(Field):
|
|
|
154
158
|
return dict(lat=self.latitudes, lon=self.longitudes)
|
|
155
159
|
|
|
156
160
|
@property
|
|
157
|
-
def resolution(self) ->
|
|
161
|
+
def resolution(self) -> Any | None:
|
|
158
162
|
"""Return the resolution of the field."""
|
|
159
163
|
return None
|
|
160
164
|
|
|
@@ -185,9 +189,9 @@ class XArrayField(Field):
|
|
|
185
189
|
|
|
186
190
|
def __repr__(self) -> str:
|
|
187
191
|
"""Return a string representation of the field."""
|
|
188
|
-
return
|
|
192
|
+
return f"XArrayField({self._metadata})"
|
|
189
193
|
|
|
190
|
-
def _values(self, dtype:
|
|
194
|
+
def _values(self, dtype: type | None = None) -> Any:
|
|
191
195
|
"""Return the values of the selection.
|
|
192
196
|
|
|
193
197
|
Returns
|
|
@@ -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
|