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.
Files changed (126) hide show
  1. anemoi/datasets/__init__.py +1 -2
  2. anemoi/datasets/_version.py +16 -3
  3. anemoi/datasets/commands/check.py +1 -1
  4. anemoi/datasets/commands/copy.py +1 -2
  5. anemoi/datasets/commands/create.py +1 -1
  6. anemoi/datasets/commands/grib-index.py +1 -1
  7. anemoi/datasets/commands/inspect.py +27 -35
  8. anemoi/datasets/commands/validate.py +59 -0
  9. anemoi/datasets/compute/recentre.py +3 -6
  10. anemoi/datasets/create/__init__.py +22 -25
  11. anemoi/datasets/create/check.py +10 -12
  12. anemoi/datasets/create/chunks.py +1 -2
  13. anemoi/datasets/create/config.py +3 -6
  14. anemoi/datasets/create/filter.py +21 -24
  15. anemoi/datasets/create/input/__init__.py +1 -2
  16. anemoi/datasets/create/input/action.py +3 -5
  17. anemoi/datasets/create/input/concat.py +5 -8
  18. anemoi/datasets/create/input/context.py +3 -6
  19. anemoi/datasets/create/input/data_sources.py +5 -8
  20. anemoi/datasets/create/input/empty.py +1 -2
  21. anemoi/datasets/create/input/filter.py +2 -3
  22. anemoi/datasets/create/input/function.py +1 -2
  23. anemoi/datasets/create/input/join.py +4 -5
  24. anemoi/datasets/create/input/misc.py +4 -6
  25. anemoi/datasets/create/input/repeated_dates.py +13 -18
  26. anemoi/datasets/create/input/result.py +29 -33
  27. anemoi/datasets/create/input/step.py +6 -24
  28. anemoi/datasets/create/input/template.py +3 -4
  29. anemoi/datasets/create/input/trace.py +1 -1
  30. anemoi/datasets/create/patch.py +1 -2
  31. anemoi/datasets/create/persistent.py +3 -5
  32. anemoi/datasets/create/size.py +1 -3
  33. anemoi/datasets/create/sources/accumulations.py +47 -52
  34. anemoi/datasets/create/sources/accumulations2.py +4 -8
  35. anemoi/datasets/create/sources/constants.py +1 -3
  36. anemoi/datasets/create/sources/empty.py +1 -2
  37. anemoi/datasets/create/sources/fdb.py +133 -0
  38. anemoi/datasets/create/sources/forcings.py +1 -2
  39. anemoi/datasets/create/sources/grib.py +6 -10
  40. anemoi/datasets/create/sources/grib_index.py +13 -15
  41. anemoi/datasets/create/sources/hindcasts.py +2 -5
  42. anemoi/datasets/create/sources/legacy.py +1 -1
  43. anemoi/datasets/create/sources/mars.py +17 -21
  44. anemoi/datasets/create/sources/netcdf.py +1 -2
  45. anemoi/datasets/create/sources/opendap.py +1 -3
  46. anemoi/datasets/create/sources/patterns.py +4 -6
  47. anemoi/datasets/create/sources/planetary_computer.py +44 -0
  48. anemoi/datasets/create/sources/recentre.py +8 -11
  49. anemoi/datasets/create/sources/source.py +3 -6
  50. anemoi/datasets/create/sources/tendencies.py +2 -5
  51. anemoi/datasets/create/sources/xarray.py +4 -6
  52. anemoi/datasets/create/sources/xarray_support/__init__.py +15 -32
  53. anemoi/datasets/create/sources/xarray_support/coordinates.py +16 -12
  54. anemoi/datasets/create/sources/xarray_support/field.py +17 -16
  55. anemoi/datasets/create/sources/xarray_support/fieldlist.py +11 -15
  56. anemoi/datasets/create/sources/xarray_support/flavour.py +83 -45
  57. anemoi/datasets/create/sources/xarray_support/grid.py +15 -9
  58. anemoi/datasets/create/sources/xarray_support/metadata.py +19 -128
  59. anemoi/datasets/create/sources/xarray_support/patch.py +47 -6
  60. anemoi/datasets/create/sources/xarray_support/time.py +10 -13
  61. anemoi/datasets/create/sources/xarray_support/variable.py +27 -23
  62. anemoi/datasets/create/sources/xarray_zarr.py +1 -2
  63. anemoi/datasets/create/sources/zenodo.py +3 -5
  64. anemoi/datasets/create/statistics/__init__.py +3 -6
  65. anemoi/datasets/create/testing.py +2 -74
  66. anemoi/datasets/create/typing.py +1 -2
  67. anemoi/datasets/create/utils.py +1 -2
  68. anemoi/datasets/create/zarr.py +7 -2
  69. anemoi/datasets/data/__init__.py +15 -6
  70. anemoi/datasets/data/complement.py +52 -23
  71. anemoi/datasets/data/concat.py +5 -8
  72. anemoi/datasets/data/dataset.py +42 -47
  73. anemoi/datasets/data/debug.py +7 -9
  74. anemoi/datasets/data/ensemble.py +4 -6
  75. anemoi/datasets/data/fill_missing.py +7 -10
  76. anemoi/datasets/data/forwards.py +30 -28
  77. anemoi/datasets/data/grids.py +12 -16
  78. anemoi/datasets/data/indexing.py +9 -12
  79. anemoi/datasets/data/interpolate.py +7 -15
  80. anemoi/datasets/data/join.py +8 -12
  81. anemoi/datasets/data/masked.py +6 -11
  82. anemoi/datasets/data/merge.py +5 -9
  83. anemoi/datasets/data/misc.py +41 -45
  84. anemoi/datasets/data/missing.py +11 -16
  85. anemoi/datasets/data/observations/__init__.py +8 -14
  86. anemoi/datasets/data/padded.py +3 -5
  87. anemoi/datasets/data/records/backends/__init__.py +2 -2
  88. anemoi/datasets/data/rescale.py +5 -12
  89. anemoi/datasets/data/select.py +13 -16
  90. anemoi/datasets/data/statistics.py +4 -7
  91. anemoi/datasets/data/stores.py +23 -77
  92. anemoi/datasets/data/subset.py +8 -11
  93. anemoi/datasets/data/unchecked.py +7 -11
  94. anemoi/datasets/data/xy.py +25 -21
  95. anemoi/datasets/dates/__init__.py +13 -18
  96. anemoi/datasets/dates/groups.py +7 -10
  97. anemoi/datasets/grids.py +11 -12
  98. anemoi/datasets/testing.py +93 -7
  99. anemoi/datasets/validate.py +598 -0
  100. {anemoi_datasets-0.5.25.dist-info → anemoi_datasets-0.5.27.dist-info}/METADATA +5 -4
  101. anemoi_datasets-0.5.27.dist-info/RECORD +134 -0
  102. anemoi/datasets/create/filters/__init__.py +0 -33
  103. anemoi/datasets/create/filters/empty.py +0 -37
  104. anemoi/datasets/create/filters/legacy.py +0 -93
  105. anemoi/datasets/create/filters/noop.py +0 -37
  106. anemoi/datasets/create/filters/orog_to_z.py +0 -58
  107. anemoi/datasets/create/filters/pressure_level_relative_humidity_to_specific_humidity.py +0 -83
  108. anemoi/datasets/create/filters/pressure_level_specific_humidity_to_relative_humidity.py +0 -84
  109. anemoi/datasets/create/filters/rename.py +0 -205
  110. anemoi/datasets/create/filters/rotate_winds.py +0 -105
  111. anemoi/datasets/create/filters/single_level_dewpoint_to_relative_humidity.py +0 -78
  112. anemoi/datasets/create/filters/single_level_relative_humidity_to_dewpoint.py +0 -84
  113. anemoi/datasets/create/filters/single_level_relative_humidity_to_specific_humidity.py +0 -163
  114. anemoi/datasets/create/filters/single_level_specific_humidity_to_relative_humidity.py +0 -451
  115. anemoi/datasets/create/filters/speeddir_to_uv.py +0 -95
  116. anemoi/datasets/create/filters/sum.py +0 -68
  117. anemoi/datasets/create/filters/transform.py +0 -51
  118. anemoi/datasets/create/filters/unrotate_winds.py +0 -105
  119. anemoi/datasets/create/filters/uv_to_speeddir.py +0 -94
  120. anemoi/datasets/create/filters/wz_to_w.py +0 -98
  121. anemoi/datasets/utils/__init__.py +0 -8
  122. anemoi_datasets-0.5.25.dist-info/RECORD +0 -150
  123. {anemoi_datasets-0.5.25.dist-info → anemoi_datasets-0.5.27.dist-info}/WHEEL +0 -0
  124. {anemoi_datasets-0.5.25.dist-info → anemoi_datasets-0.5.27.dist-info}/entry_points.txt +0 -0
  125. {anemoi_datasets-0.5.25.dist-info → anemoi_datasets-0.5.27.dist-info}/licenses/LICENSE +0 -0
  126. {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
- self._mapping = _MDMapping(field.owner)
135
- self._mapping.fill_time_metadata(field, md)
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: Optional[str] = None) -> Dict[str, Any]:
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) -> Dict[str, Any]:
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) -> Optional[datetime.datetime]:
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) -> Optional[datetime.datetime]:
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: Optional[type] = None) -> Any:
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: Optional[type] = None) -> Any:
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) -> Optional[Any]:
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) -> Optional[Any]:
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) -> Optional[Any]:
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: Optional[type] = None) -> None:
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: Optional[type] = None) -> None:
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: Dict[str, Dict[str, Any]]) -> Any:
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: List[str]) -> Any:
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: Dict[str, Dict[str, Any]]) -> Any:
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
- for what, values in patch.items():
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: List[Coordinate]) -> "Time":
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) -> Optional[str]:
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: Dict[str, Any], metadata: Dict[str, Any]) -> None:
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: Dict[str, Any], metadata: Dict[str, Any]) -> None:
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: Dict[str, Any], metadata: Dict[str, Any]) -> Any:
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: Optional[Coordinate] = None
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: Dict[str, Any], metadata: Dict[str, Any]) -> Any:
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: Dict[str, Any], metadata: Dict[str, Any]) -> Any:
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: Dict[str, Any], metadata: Dict[str, Any]) -> Any:
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) -> Optional[str]:
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: List[Any],
49
+ coordinates: list[Any],
53
50
  grid: Any,
54
51
  time: Any,
55
- metadata: Dict[str, Any],
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(len(c.variable) for c in coordinates if c.is_dim and not c.scalar and not c.is_grid)
86
- self.names = {c.variable.name: c for c in coordinates if c.is_dim and not c.scalar and not c.is_grid}
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) -> Optional[Dict[str, Any]]:
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=%s,coordinates=%s,metadata=%s]" % (
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: Dict[str, Any], **kwargs: Any) -> Optional["Variable"]:
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
- coordinates = [x.reduced(i) if c is x else x for x in self.coordinates]
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
- metadata = self._metadata.copy()
223
- metadata.update({k: v})
226
+ metadata = self._metadata.copy()
227
+ metadata.update({k: v})
224
228
 
225
- variable = Variable(
226
- ds=self.ds,
227
- variable=self.variable.isel({k: i}),
228
- coordinates=coordinates,
229
- grid=self.grid,
230
- time=self.time,
231
- metadata=metadata,
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) -> Tuple[bool, Optional[Dict[str, 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) -> List["XArrayField"]:
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: List[str], url: str, *args: Any, **kwargs: Any) -> ekd.FieldList:
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: List[Any] = []
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: Dict[str, Any] = r.json()
54
+ record: dict[str, Any] = r.json()
57
55
 
58
- urls: Dict[str, str] = {}
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: Union[str, datetime.datetime]) -> np.datetime64:
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[Union[str, datetime.datetime]]) -> list[np.datetime64]:
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: Optional[List[str]] = None, allow_nans: bool = False
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