anemoi-datasets 0.5.25__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/grib-index.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 +21 -24
- 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 +6 -24
- 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/planetary_computer.py +44 -0
- 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 +15 -32
- anemoi/datasets/create/sources/xarray_support/coordinates.py +16 -12
- anemoi/datasets/create/sources/xarray_support/field.py +17 -16
- anemoi/datasets/create/sources/xarray_support/fieldlist.py +11 -15
- anemoi/datasets/create/sources/xarray_support/flavour.py +83 -45
- 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 +47 -6
- anemoi/datasets/create/sources/xarray_support/time.py +10 -13
- anemoi/datasets/create/sources/xarray_support/variable.py +27 -23
- 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 +2 -74
- 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 +52 -23
- 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 +30 -28
- 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 +23 -77
- 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 +11 -12
- anemoi/datasets/testing.py +93 -7
- anemoi/datasets/validate.py +598 -0
- {anemoi_datasets-0.5.25.dist-info → anemoi_datasets-0.5.27.dist-info}/METADATA +5 -4
- anemoi_datasets-0.5.27.dist-info/RECORD +134 -0
- anemoi/datasets/create/filters/__init__.py +0 -33
- anemoi/datasets/create/filters/empty.py +0 -37
- anemoi/datasets/create/filters/legacy.py +0 -93
- anemoi/datasets/create/filters/noop.py +0 -37
- anemoi/datasets/create/filters/orog_to_z.py +0 -58
- anemoi/datasets/create/filters/pressure_level_relative_humidity_to_specific_humidity.py +0 -83
- anemoi/datasets/create/filters/pressure_level_specific_humidity_to_relative_humidity.py +0 -84
- anemoi/datasets/create/filters/rename.py +0 -205
- anemoi/datasets/create/filters/rotate_winds.py +0 -105
- anemoi/datasets/create/filters/single_level_dewpoint_to_relative_humidity.py +0 -78
- anemoi/datasets/create/filters/single_level_relative_humidity_to_dewpoint.py +0 -84
- anemoi/datasets/create/filters/single_level_relative_humidity_to_specific_humidity.py +0 -163
- anemoi/datasets/create/filters/single_level_specific_humidity_to_relative_humidity.py +0 -451
- anemoi/datasets/create/filters/speeddir_to_uv.py +0 -95
- anemoi/datasets/create/filters/sum.py +0 -68
- anemoi/datasets/create/filters/transform.py +0 -51
- anemoi/datasets/create/filters/unrotate_winds.py +0 -105
- anemoi/datasets/create/filters/uv_to_speeddir.py +0 -94
- anemoi/datasets/create/filters/wz_to_w.py +0 -98
- anemoi/datasets/utils/__init__.py +0 -8
- anemoi_datasets-0.5.25.dist-info/RECORD +0 -150
- {anemoi_datasets-0.5.25.dist-info → anemoi_datasets-0.5.27.dist-info}/WHEEL +0 -0
- {anemoi_datasets-0.5.25.dist-info → anemoi_datasets-0.5.27.dist-info}/entry_points.txt +0 -0
- {anemoi_datasets-0.5.25.dist-info → anemoi_datasets-0.5.27.dist-info}/licenses/LICENSE +0 -0
- {anemoi_datasets-0.5.25.dist-info → anemoi_datasets-0.5.27.dist-info}/top_level.txt +0 -0
|
@@ -12,8 +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
15
|
|
|
18
16
|
from anemoi.utils.dates import as_datetime
|
|
19
17
|
from earthkit.data.core.geography import Geography
|
|
@@ -23,87 +21,6 @@ from earthkit.data.utils.projections import Projection
|
|
|
23
21
|
LOG = logging.getLogger(__name__)
|
|
24
22
|
|
|
25
23
|
|
|
26
|
-
class _MDMapping:
|
|
27
|
-
"""A class to handle metadata mapping for variables.
|
|
28
|
-
|
|
29
|
-
Attributes
|
|
30
|
-
----------
|
|
31
|
-
variable : Any
|
|
32
|
-
The variable to map.
|
|
33
|
-
time : Any
|
|
34
|
-
The time associated with the variable.
|
|
35
|
-
mapping : Dict[str, str]
|
|
36
|
-
A dictionary mapping keys to variable names.
|
|
37
|
-
"""
|
|
38
|
-
|
|
39
|
-
def __init__(self, variable: Any) -> None:
|
|
40
|
-
"""Initialize the _MDMapping class.
|
|
41
|
-
|
|
42
|
-
Parameters
|
|
43
|
-
----------
|
|
44
|
-
variable : Any
|
|
45
|
-
The variable to map.
|
|
46
|
-
"""
|
|
47
|
-
self.variable = variable
|
|
48
|
-
self.time = variable.time
|
|
49
|
-
self.mapping = dict()
|
|
50
|
-
# Aliases
|
|
51
|
-
|
|
52
|
-
def _from_user(self, key: str) -> str:
|
|
53
|
-
"""Get the internal key corresponding to a user-provided key.
|
|
54
|
-
|
|
55
|
-
Parameters
|
|
56
|
-
----------
|
|
57
|
-
key : str
|
|
58
|
-
The user-provided key.
|
|
59
|
-
|
|
60
|
-
Returns
|
|
61
|
-
-------
|
|
62
|
-
str
|
|
63
|
-
The internal key corresponding to the user-provided key.
|
|
64
|
-
"""
|
|
65
|
-
return self.mapping.get(key, key)
|
|
66
|
-
|
|
67
|
-
def from_user(self, kwargs: Dict[str, Any]) -> Dict[str, Any]:
|
|
68
|
-
"""Convert user-provided keys to internal keys.
|
|
69
|
-
|
|
70
|
-
Parameters
|
|
71
|
-
----------
|
|
72
|
-
kwargs : Dict[str, Any]
|
|
73
|
-
A dictionary of user-provided keys and values.
|
|
74
|
-
|
|
75
|
-
Returns
|
|
76
|
-
-------
|
|
77
|
-
Dict[str, Any]
|
|
78
|
-
A dictionary with internal keys and original values.
|
|
79
|
-
"""
|
|
80
|
-
return {self._from_user(k): v for k, v in kwargs.items()}
|
|
81
|
-
|
|
82
|
-
def __repr__(self) -> str:
|
|
83
|
-
"""Return a string representation of the _MDMapping object.
|
|
84
|
-
|
|
85
|
-
Returns
|
|
86
|
-
-------
|
|
87
|
-
str
|
|
88
|
-
String representation of the _MDMapping object.
|
|
89
|
-
"""
|
|
90
|
-
return f"MDMapping({self.mapping})"
|
|
91
|
-
|
|
92
|
-
def fill_time_metadata(self, field: Any, md: Dict[str, Any]) -> None:
|
|
93
|
-
"""Fill the time metadata for a field.
|
|
94
|
-
|
|
95
|
-
Parameters
|
|
96
|
-
----------
|
|
97
|
-
field : Any
|
|
98
|
-
The field to fill metadata for.
|
|
99
|
-
md : Dict[str, Any]
|
|
100
|
-
The metadata dictionary to update.
|
|
101
|
-
"""
|
|
102
|
-
valid_datetime = self.variable.time.fill_time_metadata(field._md, md)
|
|
103
|
-
if valid_datetime is not None:
|
|
104
|
-
md["valid_datetime"] = as_datetime(valid_datetime).isoformat()
|
|
105
|
-
|
|
106
|
-
|
|
107
24
|
class XArrayMetadata(RawMetadata):
|
|
108
25
|
"""A class to handle metadata for XArray fields.
|
|
109
26
|
|
|
@@ -129,10 +46,16 @@ class XArrayMetadata(RawMetadata):
|
|
|
129
46
|
field : Any
|
|
130
47
|
The field to extract metadata from.
|
|
131
48
|
"""
|
|
49
|
+
from .field import XArrayField
|
|
50
|
+
|
|
51
|
+
assert isinstance(field, XArrayField), type(field)
|
|
132
52
|
self._field = field
|
|
133
53
|
md = field._md.copy()
|
|
134
|
-
|
|
135
|
-
|
|
54
|
+
|
|
55
|
+
valid_datetime = field.owner.time.fill_time_metadata(field._md, md)
|
|
56
|
+
if valid_datetime is not None:
|
|
57
|
+
md["valid_datetime"] = as_datetime(valid_datetime).isoformat()
|
|
58
|
+
|
|
136
59
|
super().__init__(md)
|
|
137
60
|
|
|
138
61
|
@cached_property
|
|
@@ -140,7 +63,7 @@ class XArrayMetadata(RawMetadata):
|
|
|
140
63
|
"""Get the geography information for the field."""
|
|
141
64
|
return XArrayFieldGeography(self._field, self._field.owner.grid)
|
|
142
65
|
|
|
143
|
-
def as_namespace(self, namespace:
|
|
66
|
+
def as_namespace(self, namespace: str | None = None) -> dict[str, Any]:
|
|
144
67
|
"""Get the metadata as a specific namespace.
|
|
145
68
|
|
|
146
69
|
Parameters
|
|
@@ -162,7 +85,7 @@ class XArrayMetadata(RawMetadata):
|
|
|
162
85
|
elif namespace == "mars":
|
|
163
86
|
return self._as_mars()
|
|
164
87
|
|
|
165
|
-
def _as_mars(self) ->
|
|
88
|
+
def _as_mars(self) -> dict[str, Any]:
|
|
166
89
|
"""Get the metadata as MARS namespace.
|
|
167
90
|
|
|
168
91
|
Returns
|
|
@@ -172,7 +95,7 @@ class XArrayMetadata(RawMetadata):
|
|
|
172
95
|
"""
|
|
173
96
|
return {}
|
|
174
97
|
|
|
175
|
-
def _base_datetime(self) ->
|
|
98
|
+
def _base_datetime(self) -> datetime.datetime | None:
|
|
176
99
|
"""Get the base datetime for the field.
|
|
177
100
|
|
|
178
101
|
Returns
|
|
@@ -182,7 +105,7 @@ class XArrayMetadata(RawMetadata):
|
|
|
182
105
|
"""
|
|
183
106
|
return self._field.forecast_reference_time
|
|
184
107
|
|
|
185
|
-
def _valid_datetime(self) ->
|
|
108
|
+
def _valid_datetime(self) -> datetime.datetime | None:
|
|
186
109
|
"""Get the valid datetime for the field.
|
|
187
110
|
|
|
188
111
|
Returns
|
|
@@ -192,38 +115,6 @@ class XArrayMetadata(RawMetadata):
|
|
|
192
115
|
"""
|
|
193
116
|
return self._get("valid_datetime")
|
|
194
117
|
|
|
195
|
-
def get(self, key: str, astype: Optional[type] = None, **kwargs: Any) -> Any:
|
|
196
|
-
"""Get a metadata value by key.
|
|
197
|
-
|
|
198
|
-
Parameters
|
|
199
|
-
----------
|
|
200
|
-
key : str
|
|
201
|
-
The key to get the value for.
|
|
202
|
-
astype : Optional[type]
|
|
203
|
-
The type to cast the value to.
|
|
204
|
-
**kwargs : Any
|
|
205
|
-
Additional keyword arguments.
|
|
206
|
-
|
|
207
|
-
Returns
|
|
208
|
-
-------
|
|
209
|
-
Any
|
|
210
|
-
The value for the specified key, optionally cast to the specified type.
|
|
211
|
-
"""
|
|
212
|
-
|
|
213
|
-
if key == "levelist":
|
|
214
|
-
# Special case for levelist, for compatibility with GRIB
|
|
215
|
-
if key not in self._d and "level" in self._d:
|
|
216
|
-
key = "level"
|
|
217
|
-
|
|
218
|
-
if key in self._d:
|
|
219
|
-
if astype is not None:
|
|
220
|
-
return astype(self._d[key])
|
|
221
|
-
return self._d[key]
|
|
222
|
-
|
|
223
|
-
key = self._mapping._from_user(key)
|
|
224
|
-
|
|
225
|
-
return super().get(key, astype=astype, **kwargs)
|
|
226
|
-
|
|
227
118
|
|
|
228
119
|
class XArrayFieldGeography(Geography):
|
|
229
120
|
"""A class to handle geography information for XArray fields.
|
|
@@ -280,7 +171,7 @@ class XArrayFieldGeography(Geography):
|
|
|
280
171
|
"""
|
|
281
172
|
raise NotImplementedError()
|
|
282
173
|
|
|
283
|
-
def latitudes(self, dtype:
|
|
174
|
+
def latitudes(self, dtype: type | None = None) -> Any:
|
|
284
175
|
"""Get the latitudes for the field.
|
|
285
176
|
|
|
286
177
|
Parameters
|
|
@@ -298,7 +189,7 @@ class XArrayFieldGeography(Geography):
|
|
|
298
189
|
return result.astype(dtype)
|
|
299
190
|
return result
|
|
300
191
|
|
|
301
|
-
def longitudes(self, dtype:
|
|
192
|
+
def longitudes(self, dtype: type | None = None) -> Any:
|
|
302
193
|
"""Get the longitudes for the field.
|
|
303
194
|
|
|
304
195
|
Parameters
|
|
@@ -316,7 +207,7 @@ class XArrayFieldGeography(Geography):
|
|
|
316
207
|
return result.astype(dtype)
|
|
317
208
|
return result
|
|
318
209
|
|
|
319
|
-
def resolution(self) ->
|
|
210
|
+
def resolution(self) -> Any | None:
|
|
320
211
|
"""Get the resolution for the field.
|
|
321
212
|
|
|
322
213
|
Returns
|
|
@@ -327,7 +218,7 @@ class XArrayFieldGeography(Geography):
|
|
|
327
218
|
# TODO: implement resolution
|
|
328
219
|
return None
|
|
329
220
|
|
|
330
|
-
def mars_grid(self) ->
|
|
221
|
+
def mars_grid(self) -> Any | None:
|
|
331
222
|
"""Get the MARS grid for the field.
|
|
332
223
|
|
|
333
224
|
Returns
|
|
@@ -338,7 +229,7 @@ class XArrayFieldGeography(Geography):
|
|
|
338
229
|
# TODO: implement mars_grid
|
|
339
230
|
return None
|
|
340
231
|
|
|
341
|
-
def mars_area(self) ->
|
|
232
|
+
def mars_area(self) -> Any | None:
|
|
342
233
|
"""Get the MARS area for the field.
|
|
343
234
|
|
|
344
235
|
Returns
|
|
@@ -350,7 +241,7 @@ class XArrayFieldGeography(Geography):
|
|
|
350
241
|
# return [self.north, self.west, self.south, self.east]
|
|
351
242
|
return None
|
|
352
243
|
|
|
353
|
-
def x(self, dtype:
|
|
244
|
+
def x(self, dtype: type | None = None) -> None:
|
|
354
245
|
"""Get the x-coordinates for the field.
|
|
355
246
|
|
|
356
247
|
Parameters
|
|
@@ -365,7 +256,7 @@ class XArrayFieldGeography(Geography):
|
|
|
365
256
|
"""
|
|
366
257
|
raise NotImplementedError()
|
|
367
258
|
|
|
368
|
-
def y(self, dtype:
|
|
259
|
+
def y(self, dtype: type | None = None) -> None:
|
|
369
260
|
"""Get the y-coordinates for the field.
|
|
370
261
|
|
|
371
262
|
Parameters
|
|
@@ -10,15 +10,13 @@
|
|
|
10
10
|
|
|
11
11
|
import logging
|
|
12
12
|
from typing import Any
|
|
13
|
-
from typing import Dict
|
|
14
|
-
from typing import List
|
|
15
13
|
|
|
16
14
|
import xarray as xr
|
|
17
15
|
|
|
18
16
|
LOG = logging.getLogger(__name__)
|
|
19
17
|
|
|
20
18
|
|
|
21
|
-
def patch_attributes(ds: xr.Dataset, attributes:
|
|
19
|
+
def patch_attributes(ds: xr.Dataset, attributes: dict[str, dict[str, Any]]) -> Any:
|
|
22
20
|
"""Patch the attributes of the dataset.
|
|
23
21
|
|
|
24
22
|
Parameters
|
|
@@ -40,7 +38,7 @@ def patch_attributes(ds: xr.Dataset, attributes: Dict[str, Dict[str, Any]]) -> A
|
|
|
40
38
|
return ds
|
|
41
39
|
|
|
42
40
|
|
|
43
|
-
def patch_coordinates(ds: xr.Dataset, coordinates:
|
|
41
|
+
def patch_coordinates(ds: xr.Dataset, coordinates: list[str]) -> Any:
|
|
44
42
|
"""Patch the coordinates of the dataset.
|
|
45
43
|
|
|
46
44
|
Parameters
|
|
@@ -61,13 +59,54 @@ def patch_coordinates(ds: xr.Dataset, coordinates: List[str]) -> Any:
|
|
|
61
59
|
return ds
|
|
62
60
|
|
|
63
61
|
|
|
62
|
+
def patch_rename(ds: xr.Dataset, renames: dict[str, str]) -> Any:
|
|
63
|
+
"""Rename variables in the dataset.
|
|
64
|
+
|
|
65
|
+
Parameters
|
|
66
|
+
----------
|
|
67
|
+
ds : xr.Dataset
|
|
68
|
+
The dataset to patch.
|
|
69
|
+
renames : dict[str, str]
|
|
70
|
+
Mapping from old variable names to new variable names.
|
|
71
|
+
|
|
72
|
+
Returns
|
|
73
|
+
-------
|
|
74
|
+
Any
|
|
75
|
+
The patched dataset.
|
|
76
|
+
"""
|
|
77
|
+
return ds.rename(renames)
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def patch_sort_coordinate(ds: xr.Dataset, sort_coordinates: list[str]) -> Any:
|
|
81
|
+
"""Sort the coordinates of the dataset.
|
|
82
|
+
|
|
83
|
+
Parameters
|
|
84
|
+
----------
|
|
85
|
+
ds : xr.Dataset
|
|
86
|
+
The dataset to patch.
|
|
87
|
+
sort_coordinates : List[str]
|
|
88
|
+
The coordinates to sort.
|
|
89
|
+
|
|
90
|
+
Returns
|
|
91
|
+
-------
|
|
92
|
+
Any
|
|
93
|
+
The patched dataset.
|
|
94
|
+
"""
|
|
95
|
+
|
|
96
|
+
for name in sort_coordinates:
|
|
97
|
+
ds = ds.sortby(name)
|
|
98
|
+
return ds
|
|
99
|
+
|
|
100
|
+
|
|
64
101
|
PATCHES = {
|
|
65
102
|
"attributes": patch_attributes,
|
|
66
103
|
"coordinates": patch_coordinates,
|
|
104
|
+
"rename": patch_rename,
|
|
105
|
+
"sort_coordinates": patch_sort_coordinate,
|
|
67
106
|
}
|
|
68
107
|
|
|
69
108
|
|
|
70
|
-
def patch_dataset(ds: xr.Dataset, patch:
|
|
109
|
+
def patch_dataset(ds: xr.Dataset, patch: dict[str, dict[str, Any]]) -> Any:
|
|
71
110
|
"""Patch the dataset.
|
|
72
111
|
|
|
73
112
|
Parameters
|
|
@@ -82,7 +121,9 @@ def patch_dataset(ds: xr.Dataset, patch: Dict[str, Dict[str, Any]]) -> Any:
|
|
|
82
121
|
Any
|
|
83
122
|
The patched dataset.
|
|
84
123
|
"""
|
|
85
|
-
|
|
124
|
+
|
|
125
|
+
ORDER = ["coordinates", "attributes", "rename", "sort_coordinates"]
|
|
126
|
+
for what, values in sorted(patch.items(), key=lambda x: ORDER.index(x[0])):
|
|
86
127
|
if what not in PATCHES:
|
|
87
128
|
raise ValueError(f"Unknown patch type {what!r}")
|
|
88
129
|
|
|
@@ -13,9 +13,6 @@ import logging
|
|
|
13
13
|
from abc import ABC
|
|
14
14
|
from abc import abstractmethod
|
|
15
15
|
from typing import Any
|
|
16
|
-
from typing import Dict
|
|
17
|
-
from typing import List
|
|
18
|
-
from typing import Optional
|
|
19
16
|
|
|
20
17
|
from anemoi.utils.dates import as_datetime
|
|
21
18
|
|
|
@@ -29,7 +26,7 @@ class Time(ABC):
|
|
|
29
26
|
"""Base class for different time representations."""
|
|
30
27
|
|
|
31
28
|
@classmethod
|
|
32
|
-
def from_coordinates(cls, coordinates:
|
|
29
|
+
def from_coordinates(cls, coordinates: list[Coordinate]) -> "Time":
|
|
33
30
|
"""Create a Time instance from a list of coordinates.
|
|
34
31
|
|
|
35
32
|
Returns
|
|
@@ -85,7 +82,7 @@ class Time(ABC):
|
|
|
85
82
|
raise NotImplementedError(f"{len(date_coordinate)=} {len(time_coordinate)=} {len(step_coordinate)=}")
|
|
86
83
|
|
|
87
84
|
@abstractmethod
|
|
88
|
-
def select_valid_datetime(self, variable: Variable) ->
|
|
85
|
+
def select_valid_datetime(self, variable: Variable) -> str | None:
|
|
89
86
|
"""Select the valid datetime for a given variable.
|
|
90
87
|
|
|
91
88
|
Parameters
|
|
@@ -101,7 +98,7 @@ class Time(ABC):
|
|
|
101
98
|
pass
|
|
102
99
|
|
|
103
100
|
@abstractmethod
|
|
104
|
-
def fill_time_metadata(self, coords_values:
|
|
101
|
+
def fill_time_metadata(self, coords_values: dict[str, Any], metadata: dict[str, Any]) -> None:
|
|
105
102
|
"""Fill metadata with time information.
|
|
106
103
|
|
|
107
104
|
Args
|
|
@@ -118,7 +115,7 @@ class Time(ABC):
|
|
|
118
115
|
class Constant(Time):
|
|
119
116
|
"""Represents a constant time."""
|
|
120
117
|
|
|
121
|
-
def fill_time_metadata(self, coords_values:
|
|
118
|
+
def fill_time_metadata(self, coords_values: dict[str, Any], metadata: dict[str, Any]) -> None:
|
|
122
119
|
"""Fill metadata with time information.
|
|
123
120
|
|
|
124
121
|
Parameters
|
|
@@ -154,7 +151,7 @@ class Analysis(Time):
|
|
|
154
151
|
"""
|
|
155
152
|
self.time_coordinate_name = time_coordinate.variable.name
|
|
156
153
|
|
|
157
|
-
def fill_time_metadata(self, coords_values:
|
|
154
|
+
def fill_time_metadata(self, coords_values: dict[str, Any], metadata: dict[str, Any]) -> Any:
|
|
158
155
|
"""Fill metadata with time information.
|
|
159
156
|
|
|
160
157
|
Parameters
|
|
@@ -197,7 +194,7 @@ class ForecastFromValidTimeAndStep(Time):
|
|
|
197
194
|
"""Represents a forecast time derived from valid time and step."""
|
|
198
195
|
|
|
199
196
|
def __init__(
|
|
200
|
-
self, time_coordinate: Coordinate, step_coordinate: Coordinate, date_coordinate:
|
|
197
|
+
self, time_coordinate: Coordinate, step_coordinate: Coordinate, date_coordinate: Coordinate | None = None
|
|
201
198
|
) -> None:
|
|
202
199
|
"""Initialize ForecastFromValidTimeAndStep with time, step, and optional date coordinates.
|
|
203
200
|
|
|
@@ -214,7 +211,7 @@ class ForecastFromValidTimeAndStep(Time):
|
|
|
214
211
|
self.step_coordinate_name = step_coordinate.variable.name
|
|
215
212
|
self.date_coordinate_name = date_coordinate.variable.name if date_coordinate else None
|
|
216
213
|
|
|
217
|
-
def fill_time_metadata(self, coords_values:
|
|
214
|
+
def fill_time_metadata(self, coords_values: dict[str, Any], metadata: dict[str, Any]) -> Any:
|
|
218
215
|
"""Fill metadata with time information.
|
|
219
216
|
|
|
220
217
|
Returns
|
|
@@ -285,7 +282,7 @@ class ForecastFromValidTimeAndBaseTime(Time):
|
|
|
285
282
|
self.date_coordinate_name = date_coordinate.name
|
|
286
283
|
self.time_coordinate_name = time_coordinate.name
|
|
287
284
|
|
|
288
|
-
def fill_time_metadata(self, coords_values:
|
|
285
|
+
def fill_time_metadata(self, coords_values: dict[str, Any], metadata: dict[str, Any]) -> Any:
|
|
289
286
|
"""Fill metadata with time information.
|
|
290
287
|
|
|
291
288
|
Returns
|
|
@@ -346,7 +343,7 @@ class ForecastFromBaseTimeAndDate(Time):
|
|
|
346
343
|
self.date_coordinate_name = date_coordinate.name
|
|
347
344
|
self.step_coordinate_name = step_coordinate.name
|
|
348
345
|
|
|
349
|
-
def fill_time_metadata(self, coords_values:
|
|
346
|
+
def fill_time_metadata(self, coords_values: dict[str, Any], metadata: dict[str, Any]) -> Any:
|
|
350
347
|
"""Fill metadata with time information.
|
|
351
348
|
|
|
352
349
|
Returns
|
|
@@ -375,7 +372,7 @@ class ForecastFromBaseTimeAndDate(Time):
|
|
|
375
372
|
|
|
376
373
|
return date + step
|
|
377
374
|
|
|
378
|
-
def select_valid_datetime(self, variable: Variable) ->
|
|
375
|
+
def select_valid_datetime(self, variable: Variable) -> str | None:
|
|
379
376
|
"""Select the valid datetime for a given variable.
|
|
380
377
|
|
|
381
378
|
Parameters
|
|
@@ -12,10 +12,7 @@ import logging
|
|
|
12
12
|
import math
|
|
13
13
|
from functools import cached_property
|
|
14
14
|
from typing import Any
|
|
15
|
-
from typing import Dict
|
|
16
|
-
from typing import List
|
|
17
15
|
from typing import Optional
|
|
18
|
-
from typing import Tuple
|
|
19
16
|
|
|
20
17
|
import numpy as np
|
|
21
18
|
import xarray as xr
|
|
@@ -49,10 +46,10 @@ class Variable:
|
|
|
49
46
|
*,
|
|
50
47
|
ds: xr.Dataset,
|
|
51
48
|
variable: xr.DataArray,
|
|
52
|
-
coordinates:
|
|
49
|
+
coordinates: list[Any],
|
|
53
50
|
grid: Any,
|
|
54
51
|
time: Any,
|
|
55
|
-
metadata:
|
|
52
|
+
metadata: dict[str, Any],
|
|
56
53
|
):
|
|
57
54
|
"""Initialize the Variable object.
|
|
58
55
|
|
|
@@ -82,8 +79,12 @@ class Variable:
|
|
|
82
79
|
|
|
83
80
|
self.time = time
|
|
84
81
|
|
|
85
|
-
self.shape = tuple(
|
|
86
|
-
|
|
82
|
+
self.shape = tuple(
|
|
83
|
+
len(c.variable) for c in coordinates if c.is_dim and not c.scalar and not c.is_grid and not c.is_point
|
|
84
|
+
)
|
|
85
|
+
self.names = {
|
|
86
|
+
c.variable.name: c for c in coordinates if c.is_dim and not c.scalar and not c.is_grid and not c.is_point
|
|
87
|
+
}
|
|
87
88
|
self.by_name = {c.variable.name: c for c in coordinates}
|
|
88
89
|
|
|
89
90
|
# We need that alias for the time dimension
|
|
@@ -107,7 +108,7 @@ class Variable:
|
|
|
107
108
|
return self.length
|
|
108
109
|
|
|
109
110
|
@property
|
|
110
|
-
def grid_mapping(self) ->
|
|
111
|
+
def grid_mapping(self) -> dict[str, Any] | None:
|
|
111
112
|
"""Return the grid mapping of the variable."""
|
|
112
113
|
grid_mapping = self.variable.attrs.get("grid_mapping", None)
|
|
113
114
|
if grid_mapping is None:
|
|
@@ -142,7 +143,7 @@ class Variable:
|
|
|
142
143
|
str
|
|
143
144
|
A string representation of the variable.
|
|
144
145
|
"""
|
|
145
|
-
return "Variable[name
|
|
146
|
+
return "Variable[name={},coordinates={},metadata={}]".format(
|
|
146
147
|
self.variable.name,
|
|
147
148
|
self.coordinates,
|
|
148
149
|
self._metadata,
|
|
@@ -173,7 +174,7 @@ class Variable:
|
|
|
173
174
|
kwargs = {k: v for k, v in zip(self.names, coords)}
|
|
174
175
|
return XArrayField(self, self.variable.isel(kwargs))
|
|
175
176
|
|
|
176
|
-
def sel(self, missing:
|
|
177
|
+
def sel(self, missing: dict[str, Any], **kwargs: Any) -> Optional["Variable"]:
|
|
177
178
|
"""Select a subset of the variable based on the given coordinates.
|
|
178
179
|
|
|
179
180
|
Parameters
|
|
@@ -217,23 +218,26 @@ class Variable:
|
|
|
217
218
|
LOG.warning(f"Could not find {k}={v} in {c}")
|
|
218
219
|
return None
|
|
219
220
|
|
|
220
|
-
|
|
221
|
+
if c.scalar and i == 0:
|
|
222
|
+
variable = self
|
|
223
|
+
else:
|
|
224
|
+
coordinates = [x.reduced(i) if c is x else x for x in self.coordinates]
|
|
221
225
|
|
|
222
|
-
|
|
223
|
-
|
|
226
|
+
metadata = self._metadata.copy()
|
|
227
|
+
metadata.update({k: v})
|
|
224
228
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
229
|
+
variable = Variable(
|
|
230
|
+
ds=self.ds,
|
|
231
|
+
variable=self.variable.isel({k: i}),
|
|
232
|
+
coordinates=coordinates,
|
|
233
|
+
grid=self.grid,
|
|
234
|
+
time=self.time,
|
|
235
|
+
metadata=metadata,
|
|
236
|
+
)
|
|
233
237
|
|
|
234
238
|
return variable.sel(missing, **kwargs)
|
|
235
239
|
|
|
236
|
-
def match(self, **kwargs: Any) ->
|
|
240
|
+
def match(self, **kwargs: Any) -> tuple[bool, dict[str, Any] | None]:
|
|
237
241
|
"""Match the variable based on the given metadata.
|
|
238
242
|
|
|
239
243
|
Parameters
|
|
@@ -285,7 +289,7 @@ class FilteredVariable:
|
|
|
285
289
|
self.kwargs = kwargs
|
|
286
290
|
|
|
287
291
|
@cached_property
|
|
288
|
-
def fields(self) ->
|
|
292
|
+
def fields(self) -> list["XArrayField"]:
|
|
289
293
|
"""Filter the fields of a variable based on metadata."""
|
|
290
294
|
return [
|
|
291
295
|
field
|
|
@@ -8,7 +8,6 @@
|
|
|
8
8
|
# nor does it submit to any jurisdiction.
|
|
9
9
|
|
|
10
10
|
from typing import Any
|
|
11
|
-
from typing import List
|
|
12
11
|
|
|
13
12
|
import earthkit.data as ekd
|
|
14
13
|
|
|
@@ -17,7 +16,7 @@ from .xarray import load_many
|
|
|
17
16
|
|
|
18
17
|
|
|
19
18
|
@legacy_source(__file__)
|
|
20
|
-
def execute(context: Any, dates:
|
|
19
|
+
def execute(context: Any, dates: list[str], url: str, *args: Any, **kwargs: Any) -> ekd.FieldList:
|
|
21
20
|
"""Execute the data loading process.
|
|
22
21
|
|
|
23
22
|
Parameters
|
|
@@ -9,8 +9,6 @@
|
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
from typing import Any
|
|
12
|
-
from typing import Dict
|
|
13
|
-
from typing import List
|
|
14
12
|
|
|
15
13
|
import earthkit.data as ekd
|
|
16
14
|
from earthkit.data.core.fieldlist import MultiFieldList
|
|
@@ -47,15 +45,15 @@ def execute(context: Any, dates: Any, record_id: str, file_key: str, *args: Any,
|
|
|
47
45
|
"""
|
|
48
46
|
import requests
|
|
49
47
|
|
|
50
|
-
result:
|
|
48
|
+
result: list[Any] = []
|
|
51
49
|
|
|
52
50
|
URLPATTERN = "https://zenodo.org/api/records/{record_id}"
|
|
53
51
|
url = URLPATTERN.format(record_id=record_id)
|
|
54
52
|
r = requests.get(url)
|
|
55
53
|
r.raise_for_status()
|
|
56
|
-
record:
|
|
54
|
+
record: dict[str, Any] = r.json()
|
|
57
55
|
|
|
58
|
-
urls:
|
|
56
|
+
urls: dict[str, str] = {}
|
|
59
57
|
for file in record["files"]:
|
|
60
58
|
urls[file["key"]] = file["links"]["self"]
|
|
61
59
|
|
|
@@ -17,9 +17,6 @@ import pickle
|
|
|
17
17
|
import shutil
|
|
18
18
|
import socket
|
|
19
19
|
from typing import Any
|
|
20
|
-
from typing import List
|
|
21
|
-
from typing import Optional
|
|
22
|
-
from typing import Union
|
|
23
20
|
|
|
24
21
|
import numpy as np
|
|
25
22
|
import tqdm
|
|
@@ -77,7 +74,7 @@ def default_statistics_dates(dates: list[datetime.datetime]) -> tuple[datetime.d
|
|
|
77
74
|
return dates[0], end
|
|
78
75
|
|
|
79
76
|
|
|
80
|
-
def to_datetime(date:
|
|
77
|
+
def to_datetime(date: str | datetime.datetime) -> np.datetime64:
|
|
81
78
|
"""Convert a date to numpy datetime64 format.
|
|
82
79
|
|
|
83
80
|
Parameters
|
|
@@ -97,7 +94,7 @@ def to_datetime(date: Union[str, datetime.datetime]) -> np.datetime64:
|
|
|
97
94
|
return date
|
|
98
95
|
|
|
99
96
|
|
|
100
|
-
def to_datetimes(dates: list[
|
|
97
|
+
def to_datetimes(dates: list[str | datetime.datetime]) -> list[np.datetime64]:
|
|
101
98
|
"""Convert a list of dates to numpy datetime64 format.
|
|
102
99
|
|
|
103
100
|
Parameters
|
|
@@ -221,7 +218,7 @@ def check_variance(
|
|
|
221
218
|
|
|
222
219
|
|
|
223
220
|
def compute_statistics(
|
|
224
|
-
array: NDArray[Any], check_variables_names:
|
|
221
|
+
array: NDArray[Any], check_variables_names: list[str] | None = None, allow_nans: bool = False
|
|
225
222
|
) -> dict[str, np.ndarray]:
|
|
226
223
|
"""Compute statistics for a given array, provides minimum, maximum, sum, squares, count and has_nans as a dictionary.
|
|
227
224
|
|