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
|
@@ -10,9 +10,6 @@
|
|
|
10
10
|
import datetime
|
|
11
11
|
from collections import defaultdict
|
|
12
12
|
from typing import Any
|
|
13
|
-
from typing import Dict
|
|
14
|
-
from typing import List
|
|
15
|
-
from typing import Tuple
|
|
16
13
|
|
|
17
14
|
from earthkit.data.core.temporary import temp_file
|
|
18
15
|
from earthkit.data.readers.grib.output import new_grib_output
|
|
@@ -63,7 +60,7 @@ def normalise_time_delta(t: Any) -> datetime.timedelta:
|
|
|
63
60
|
return t
|
|
64
61
|
|
|
65
62
|
|
|
66
|
-
def group_by_field(ds: Any) ->
|
|
63
|
+
def group_by_field(ds: Any) -> dict[tuple, list[Any]]:
|
|
67
64
|
"""Groups fields by their metadata excluding 'date', 'time', and 'step'.
|
|
68
65
|
|
|
69
66
|
Parameters
|
|
@@ -87,7 +84,7 @@ def group_by_field(ds: Any) -> Dict[Tuple, List[Any]]:
|
|
|
87
84
|
|
|
88
85
|
|
|
89
86
|
@legacy_source(__file__)
|
|
90
|
-
def tendencies(dates:
|
|
87
|
+
def tendencies(dates: list[datetime.datetime], time_increment: Any, **kwargs: Any) -> Any:
|
|
91
88
|
"""Computes tendencies for the given dates and time increment.
|
|
92
89
|
|
|
93
90
|
Parameters
|
|
@@ -8,8 +8,6 @@
|
|
|
8
8
|
# nor does it submit to any jurisdiction.
|
|
9
9
|
|
|
10
10
|
from typing import Any
|
|
11
|
-
from typing import Dict
|
|
12
|
-
from typing import Optional
|
|
13
11
|
|
|
14
12
|
import earthkit.data as ekd
|
|
15
13
|
|
|
@@ -28,11 +26,11 @@ class XarraySourceBase(Source):
|
|
|
28
26
|
|
|
29
27
|
emoji = "✖️" # For tracing
|
|
30
28
|
|
|
31
|
-
options:
|
|
32
|
-
flavour:
|
|
33
|
-
patch:
|
|
29
|
+
options: dict[str, Any] | None = None
|
|
30
|
+
flavour: dict[str, Any] | None = None
|
|
31
|
+
patch: dict[str, Any] | None = None
|
|
34
32
|
|
|
35
|
-
path_or_url:
|
|
33
|
+
path_or_url: str | None = None
|
|
36
34
|
|
|
37
35
|
def __init__(self, context: Any, path: str = None, url: str = None, *args: Any, **kwargs: Any):
|
|
38
36
|
"""Initialise the source.
|
|
@@ -10,17 +10,12 @@
|
|
|
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
|
|
20
16
|
from earthkit.data.core.fieldlist import MultiFieldList
|
|
21
17
|
|
|
22
18
|
from anemoi.datasets.create.sources.patterns import iterate_patterns
|
|
23
|
-
from anemoi.datasets.data.stores import name_to_zarr_store
|
|
24
19
|
|
|
25
20
|
from ..legacy import legacy_source
|
|
26
21
|
from .fieldlist import XarrayFieldList
|
|
@@ -28,7 +23,7 @@ from .fieldlist import XarrayFieldList
|
|
|
28
23
|
LOG = logging.getLogger(__name__)
|
|
29
24
|
|
|
30
25
|
|
|
31
|
-
def check(what: str, ds: xr.Dataset, paths:
|
|
26
|
+
def check(what: str, ds: xr.Dataset, paths: list[str], **kwargs: Any) -> None:
|
|
32
27
|
"""Checks if the dataset has the expected number of fields.
|
|
33
28
|
|
|
34
29
|
Parameters
|
|
@@ -54,12 +49,12 @@ def check(what: str, ds: xr.Dataset, paths: List[str], **kwargs: Any) -> None:
|
|
|
54
49
|
def load_one(
|
|
55
50
|
emoji: str,
|
|
56
51
|
context: Any,
|
|
57
|
-
dates:
|
|
58
|
-
dataset:
|
|
52
|
+
dates: list[str],
|
|
53
|
+
dataset: str | xr.Dataset,
|
|
59
54
|
*,
|
|
60
|
-
options:
|
|
61
|
-
flavour:
|
|
62
|
-
patch:
|
|
55
|
+
options: dict[str, Any] | None = None,
|
|
56
|
+
flavour: str | None = None,
|
|
57
|
+
patch: Any | None = None,
|
|
63
58
|
**kwargs: Any,
|
|
64
59
|
) -> ekd.FieldList:
|
|
65
60
|
"""Loads a single dataset.
|
|
@@ -89,28 +84,17 @@ def load_one(
|
|
|
89
84
|
The loaded dataset.
|
|
90
85
|
"""
|
|
91
86
|
|
|
92
|
-
"""
|
|
93
|
-
We manage the S3 client ourselves, bypassing fsspec and s3fs layers, because sometimes something on the stack
|
|
94
|
-
zarr/fsspec/s3fs/boto3 (?) seem to flags files as missing when they actually are not (maybe when S3 reports some sort of
|
|
95
|
-
connection error). In that case, Zarr will silently fill the chunks that could not be downloaded with NaNs.
|
|
96
|
-
See https://github.com/pydata/xarray/issues/8842
|
|
97
|
-
|
|
98
|
-
We have seen this bug triggered when we run many clients in parallel, for example, when we create a new dataset using `xarray-zarr`.
|
|
99
|
-
"""
|
|
100
|
-
|
|
101
87
|
if options is None:
|
|
102
88
|
options = {}
|
|
103
89
|
|
|
104
90
|
context.trace(emoji, dataset, options, kwargs)
|
|
105
91
|
|
|
106
|
-
if isinstance(dataset, str) and ".zarr"
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
if "filename_or_obj" in store:
|
|
113
|
-
data = xr.open_dataset(**store)
|
|
92
|
+
if isinstance(dataset, str) and dataset.endswith(".zarr"):
|
|
93
|
+
# If the dataset is a zarr store, we need to use the zarr engine
|
|
94
|
+
options["engine"] = "zarr"
|
|
95
|
+
|
|
96
|
+
if isinstance(dataset, xr.Dataset):
|
|
97
|
+
data = dataset
|
|
114
98
|
else:
|
|
115
99
|
data = xr.open_dataset(dataset, **options)
|
|
116
100
|
|
|
@@ -119,7 +103,6 @@ def load_one(
|
|
|
119
103
|
if len(dates) == 0:
|
|
120
104
|
result = fs.sel(**kwargs)
|
|
121
105
|
else:
|
|
122
|
-
print("dates", dates, kwargs)
|
|
123
106
|
result = MultiFieldList([fs.sel(valid_datetime=date, **kwargs) for date in dates])
|
|
124
107
|
|
|
125
108
|
if len(result) == 0:
|
|
@@ -130,7 +113,7 @@ def load_one(
|
|
|
130
113
|
a = ["valid_datetime", k.metadata("valid_datetime", default=None)]
|
|
131
114
|
for n in kwargs.keys():
|
|
132
115
|
a.extend([n, k.metadata(n, default=None)])
|
|
133
|
-
|
|
116
|
+
LOG.warning(f"{[str(x) for x in a]}")
|
|
134
117
|
|
|
135
118
|
if i > 16:
|
|
136
119
|
break
|
|
@@ -140,7 +123,7 @@ def load_one(
|
|
|
140
123
|
return result
|
|
141
124
|
|
|
142
125
|
|
|
143
|
-
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:
|
|
144
127
|
"""Loads multiple datasets.
|
|
145
128
|
|
|
146
129
|
Parameters
|
|
@@ -170,7 +153,7 @@ def load_many(emoji: str, context: Any, dates: List[datetime.datetime], pattern:
|
|
|
170
153
|
|
|
171
154
|
|
|
172
155
|
@legacy_source("xarray")
|
|
173
|
-
def execute(context: Any, dates:
|
|
156
|
+
def execute(context: Any, dates: list[str], url: str, *args: Any, **kwargs: Any) -> ekd.FieldList:
|
|
174
157
|
"""Executes the loading of datasets.
|
|
175
158
|
|
|
176
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
|
|
@@ -95,6 +91,7 @@ class Coordinate:
|
|
|
95
91
|
is_member = False
|
|
96
92
|
is_x = False
|
|
97
93
|
is_y = False
|
|
94
|
+
is_point = False
|
|
98
95
|
|
|
99
96
|
def __init__(self, variable: xr.DataArray) -> None:
|
|
100
97
|
"""Initialize the coordinate.
|
|
@@ -106,7 +103,7 @@ class Coordinate:
|
|
|
106
103
|
"""
|
|
107
104
|
self.variable = variable
|
|
108
105
|
self.scalar = is_scalar(variable)
|
|
109
|
-
self.kwargs:
|
|
106
|
+
self.kwargs: dict[str, Any] = {} # Used when creating a new coordinate (reduced method)
|
|
110
107
|
|
|
111
108
|
def __len__(self) -> int:
|
|
112
109
|
"""Get the length of the coordinate.
|
|
@@ -126,7 +123,7 @@ class Coordinate:
|
|
|
126
123
|
str
|
|
127
124
|
The string representation of the coordinate.
|
|
128
125
|
"""
|
|
129
|
-
return "
|
|
126
|
+
return "{}[name={},values={},shape={}]".format(
|
|
130
127
|
self.__class__.__name__,
|
|
131
128
|
self.variable.name,
|
|
132
129
|
self.variable.values if self.scalar else len(self),
|
|
@@ -151,7 +148,7 @@ class Coordinate:
|
|
|
151
148
|
**self.kwargs,
|
|
152
149
|
)
|
|
153
150
|
|
|
154
|
-
def index(self, value:
|
|
151
|
+
def index(self, value: Any | list | tuple) -> int | list | None:
|
|
155
152
|
"""Return the index of the value in the coordinate.
|
|
156
153
|
|
|
157
154
|
Parameters
|
|
@@ -171,7 +168,7 @@ class Coordinate:
|
|
|
171
168
|
return self._index_multiple(value)
|
|
172
169
|
return self._index_single(value)
|
|
173
170
|
|
|
174
|
-
def _index_single(self, value: Any) ->
|
|
171
|
+
def _index_single(self, value: Any) -> int | None:
|
|
175
172
|
"""Return the index of a single value in the coordinate.
|
|
176
173
|
|
|
177
174
|
Parameters
|
|
@@ -204,7 +201,7 @@ class Coordinate:
|
|
|
204
201
|
|
|
205
202
|
return None
|
|
206
203
|
|
|
207
|
-
def _index_multiple(self, value: list) ->
|
|
204
|
+
def _index_multiple(self, value: list) -> list | None:
|
|
208
205
|
"""Return the indices of multiple values in the coordinate.
|
|
209
206
|
|
|
210
207
|
Parameters
|
|
@@ -274,7 +271,7 @@ class TimeCoordinate(Coordinate):
|
|
|
274
271
|
is_time = True
|
|
275
272
|
mars_names = ("valid_datetime",)
|
|
276
273
|
|
|
277
|
-
def index(self, time: datetime.datetime) ->
|
|
274
|
+
def index(self, time: datetime.datetime) -> int | None:
|
|
278
275
|
"""Return the index of the time in the coordinate.
|
|
279
276
|
|
|
280
277
|
Parameters
|
|
@@ -296,7 +293,7 @@ class DateCoordinate(Coordinate):
|
|
|
296
293
|
is_date = True
|
|
297
294
|
mars_names = ("date",)
|
|
298
295
|
|
|
299
|
-
def index(self, date: datetime.datetime) ->
|
|
296
|
+
def index(self, date: datetime.datetime) -> int | None:
|
|
300
297
|
"""Return the index of the date in the coordinate.
|
|
301
298
|
|
|
302
299
|
Parameters
|
|
@@ -390,6 +387,13 @@ class EnsembleCoordinate(Coordinate):
|
|
|
390
387
|
return value
|
|
391
388
|
|
|
392
389
|
|
|
390
|
+
class PointCoordinate(Coordinate):
|
|
391
|
+
"""Coordinate class for point data."""
|
|
392
|
+
|
|
393
|
+
is_point = True
|
|
394
|
+
mars_names = ("point",)
|
|
395
|
+
|
|
396
|
+
|
|
393
397
|
class LongitudeCoordinate(Coordinate):
|
|
394
398
|
"""Coordinate class for longitude."""
|
|
395
399
|
|
|
@@ -428,7 +432,7 @@ class ScalarCoordinate(Coordinate):
|
|
|
428
432
|
is_grid = False
|
|
429
433
|
|
|
430
434
|
@property
|
|
431
|
-
def mars_names(self) ->
|
|
435
|
+
def mars_names(self) -> tuple[str, ...]:
|
|
432
436
|
"""Get the MARS names for the coordinate."""
|
|
433
437
|
return (self.variable.name,)
|
|
434
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,29 +77,33 @@ 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
|
-
# print(values.ndim, values.shape, selection.dims)
|
|
91
96
|
# By now, the only dimensions should be latitude and longitude
|
|
92
97
|
self._shape = tuple(list(self.selection.shape)[-2:])
|
|
93
98
|
if math.prod(self._shape) != math.prod(self.selection.shape):
|
|
94
|
-
|
|
95
|
-
print(self.selection)
|
|
96
|
-
raise ValueError("Invalid shape for selection")
|
|
99
|
+
raise ValueError(f"Invalid shape for selection {self._shape=}, {self.selection.shape=} {self.selection=}")
|
|
97
100
|
|
|
98
101
|
@property
|
|
99
|
-
def shape(self) ->
|
|
102
|
+
def shape(self) -> tuple[int, int]:
|
|
100
103
|
"""Return the shape of the field."""
|
|
101
104
|
return self._shape
|
|
102
105
|
|
|
103
|
-
def to_numpy(
|
|
104
|
-
self, flatten: bool = False, dtype: Optional[type] = None, index: Optional[int] = None
|
|
105
|
-
) -> NDArray[Any]:
|
|
106
|
+
def to_numpy(self, flatten: bool = False, dtype: type | None = None, index: int | None = None) -> NDArray[Any]:
|
|
106
107
|
"""Convert the selection to a numpy array.
|
|
107
108
|
|
|
108
109
|
Returns
|
|
@@ -140,7 +141,7 @@ class XArrayField(Field):
|
|
|
140
141
|
"""Return the grid points of the field."""
|
|
141
142
|
return self.owner.grid_points()
|
|
142
143
|
|
|
143
|
-
def to_latlon(self, flatten: bool = True) ->
|
|
144
|
+
def to_latlon(self, flatten: bool = True) -> dict[str, Any]:
|
|
144
145
|
"""Convert the selection to latitude and longitude coordinates.
|
|
145
146
|
|
|
146
147
|
Returns
|
|
@@ -157,7 +158,7 @@ class XArrayField(Field):
|
|
|
157
158
|
return dict(lat=self.latitudes, lon=self.longitudes)
|
|
158
159
|
|
|
159
160
|
@property
|
|
160
|
-
def resolution(self) ->
|
|
161
|
+
def resolution(self) -> Any | None:
|
|
161
162
|
"""Return the resolution of the field."""
|
|
162
163
|
return None
|
|
163
164
|
|
|
@@ -188,9 +189,9 @@ class XArrayField(Field):
|
|
|
188
189
|
|
|
189
190
|
def __repr__(self) -> str:
|
|
190
191
|
"""Return a string representation of the field."""
|
|
191
|
-
return
|
|
192
|
+
return f"XArrayField({self._metadata})"
|
|
192
193
|
|
|
193
|
-
def _values(self, dtype:
|
|
194
|
+
def _values(self, dtype: type | None = None) -> Any:
|
|
194
195
|
"""Return the values of the selection.
|
|
195
196
|
|
|
196
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
|