anemoi-datasets 0.5.16__py3-none-any.whl → 0.5.18__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 (155) hide show
  1. anemoi/datasets/__init__.py +4 -1
  2. anemoi/datasets/__main__.py +12 -2
  3. anemoi/datasets/_version.py +9 -4
  4. anemoi/datasets/commands/cleanup.py +17 -2
  5. anemoi/datasets/commands/compare.py +18 -2
  6. anemoi/datasets/commands/copy.py +196 -14
  7. anemoi/datasets/commands/create.py +50 -7
  8. anemoi/datasets/commands/finalise-additions.py +17 -2
  9. anemoi/datasets/commands/finalise.py +17 -2
  10. anemoi/datasets/commands/init-additions.py +17 -2
  11. anemoi/datasets/commands/init.py +16 -2
  12. anemoi/datasets/commands/inspect.py +283 -62
  13. anemoi/datasets/commands/load-additions.py +16 -2
  14. anemoi/datasets/commands/load.py +16 -2
  15. anemoi/datasets/commands/patch.py +17 -2
  16. anemoi/datasets/commands/publish.py +17 -2
  17. anemoi/datasets/commands/scan.py +31 -3
  18. anemoi/datasets/compute/recentre.py +47 -11
  19. anemoi/datasets/create/__init__.py +612 -85
  20. anemoi/datasets/create/check.py +142 -20
  21. anemoi/datasets/create/chunks.py +64 -4
  22. anemoi/datasets/create/config.py +185 -21
  23. anemoi/datasets/create/filter.py +50 -0
  24. anemoi/datasets/create/filters/__init__.py +33 -0
  25. anemoi/datasets/create/filters/empty.py +37 -0
  26. anemoi/datasets/create/filters/legacy.py +93 -0
  27. anemoi/datasets/create/filters/noop.py +37 -0
  28. anemoi/datasets/create/filters/orog_to_z.py +58 -0
  29. anemoi/datasets/create/{functions/filters → filters}/pressure_level_relative_humidity_to_specific_humidity.py +33 -10
  30. anemoi/datasets/create/{functions/filters → filters}/pressure_level_specific_humidity_to_relative_humidity.py +32 -8
  31. anemoi/datasets/create/filters/rename.py +205 -0
  32. anemoi/datasets/create/{functions/filters → filters}/rotate_winds.py +43 -28
  33. anemoi/datasets/create/{functions/filters → filters}/single_level_dewpoint_to_relative_humidity.py +32 -9
  34. anemoi/datasets/create/{functions/filters → filters}/single_level_relative_humidity_to_dewpoint.py +33 -9
  35. anemoi/datasets/create/{functions/filters → filters}/single_level_relative_humidity_to_specific_humidity.py +55 -7
  36. anemoi/datasets/create/{functions/filters → filters}/single_level_specific_humidity_to_relative_humidity.py +98 -37
  37. anemoi/datasets/create/filters/speeddir_to_uv.py +95 -0
  38. anemoi/datasets/create/{functions/filters → filters}/sum.py +24 -27
  39. anemoi/datasets/create/filters/transform.py +53 -0
  40. anemoi/datasets/create/{functions/filters → filters}/unrotate_winds.py +27 -18
  41. anemoi/datasets/create/filters/uv_to_speeddir.py +94 -0
  42. anemoi/datasets/create/{functions/filters → filters}/wz_to_w.py +51 -33
  43. anemoi/datasets/create/input/__init__.py +76 -5
  44. anemoi/datasets/create/input/action.py +149 -13
  45. anemoi/datasets/create/input/concat.py +81 -10
  46. anemoi/datasets/create/input/context.py +39 -4
  47. anemoi/datasets/create/input/data_sources.py +72 -6
  48. anemoi/datasets/create/input/empty.py +21 -3
  49. anemoi/datasets/create/input/filter.py +60 -12
  50. anemoi/datasets/create/input/function.py +154 -37
  51. anemoi/datasets/create/input/join.py +86 -14
  52. anemoi/datasets/create/input/misc.py +67 -17
  53. anemoi/datasets/create/input/pipe.py +33 -6
  54. anemoi/datasets/create/input/repeated_dates.py +189 -41
  55. anemoi/datasets/create/input/result.py +202 -87
  56. anemoi/datasets/create/input/step.py +119 -22
  57. anemoi/datasets/create/input/template.py +100 -13
  58. anemoi/datasets/create/input/trace.py +62 -7
  59. anemoi/datasets/create/patch.py +52 -4
  60. anemoi/datasets/create/persistent.py +134 -17
  61. anemoi/datasets/create/size.py +15 -1
  62. anemoi/datasets/create/source.py +51 -0
  63. anemoi/datasets/create/sources/__init__.py +36 -0
  64. anemoi/datasets/create/{functions/sources → sources}/accumulations.py +296 -30
  65. anemoi/datasets/create/{functions/sources → sources}/constants.py +27 -2
  66. anemoi/datasets/create/{functions/sources → sources}/eccc_fstd.py +7 -3
  67. anemoi/datasets/create/sources/empty.py +37 -0
  68. anemoi/datasets/create/{functions/sources → sources}/forcings.py +25 -1
  69. anemoi/datasets/create/sources/grib.py +297 -0
  70. anemoi/datasets/create/{functions/sources → sources}/hindcasts.py +38 -4
  71. anemoi/datasets/create/sources/legacy.py +93 -0
  72. anemoi/datasets/create/{functions/sources → sources}/mars.py +168 -20
  73. anemoi/datasets/create/sources/netcdf.py +42 -0
  74. anemoi/datasets/create/sources/opendap.py +43 -0
  75. anemoi/datasets/create/{functions/sources/__init__.py → sources/patterns.py} +35 -4
  76. anemoi/datasets/create/sources/recentre.py +150 -0
  77. anemoi/datasets/create/{functions/sources → sources}/source.py +27 -5
  78. anemoi/datasets/create/{functions/sources → sources}/tendencies.py +64 -7
  79. anemoi/datasets/create/sources/xarray.py +92 -0
  80. anemoi/datasets/create/sources/xarray_kerchunk.py +36 -0
  81. anemoi/datasets/create/sources/xarray_support/README.md +1 -0
  82. anemoi/datasets/create/{functions/sources/xarray → sources/xarray_support}/__init__.py +109 -8
  83. anemoi/datasets/create/sources/xarray_support/coordinates.py +442 -0
  84. anemoi/datasets/create/{functions/sources/xarray → sources/xarray_support}/field.py +94 -16
  85. anemoi/datasets/create/{functions/sources/xarray → sources/xarray_support}/fieldlist.py +90 -25
  86. anemoi/datasets/create/sources/xarray_support/flavour.py +1036 -0
  87. anemoi/datasets/create/{functions/sources/xarray → sources/xarray_support}/grid.py +92 -31
  88. anemoi/datasets/create/sources/xarray_support/metadata.py +395 -0
  89. anemoi/datasets/create/sources/xarray_support/patch.py +91 -0
  90. anemoi/datasets/create/sources/xarray_support/time.py +391 -0
  91. anemoi/datasets/create/sources/xarray_support/variable.py +331 -0
  92. anemoi/datasets/create/sources/xarray_zarr.py +41 -0
  93. anemoi/datasets/create/{functions/sources → sources}/zenodo.py +34 -5
  94. anemoi/datasets/create/statistics/__init__.py +233 -44
  95. anemoi/datasets/create/statistics/summary.py +52 -6
  96. anemoi/datasets/create/testing.py +76 -0
  97. anemoi/datasets/create/{functions/filters/noop.py → typing.py} +6 -3
  98. anemoi/datasets/create/utils.py +97 -6
  99. anemoi/datasets/create/writer.py +26 -4
  100. anemoi/datasets/create/zarr.py +170 -23
  101. anemoi/datasets/data/__init__.py +51 -4
  102. anemoi/datasets/data/complement.py +191 -40
  103. anemoi/datasets/data/concat.py +141 -16
  104. anemoi/datasets/data/dataset.py +558 -62
  105. anemoi/datasets/data/debug.py +197 -26
  106. anemoi/datasets/data/ensemble.py +93 -8
  107. anemoi/datasets/data/fill_missing.py +165 -18
  108. anemoi/datasets/data/forwards.py +428 -56
  109. anemoi/datasets/data/grids.py +323 -97
  110. anemoi/datasets/data/indexing.py +112 -19
  111. anemoi/datasets/data/interpolate.py +92 -12
  112. anemoi/datasets/data/join.py +158 -19
  113. anemoi/datasets/data/masked.py +129 -15
  114. anemoi/datasets/data/merge.py +137 -23
  115. anemoi/datasets/data/misc.py +172 -16
  116. anemoi/datasets/data/missing.py +233 -29
  117. anemoi/datasets/data/rescale.py +111 -10
  118. anemoi/datasets/data/select.py +168 -26
  119. anemoi/datasets/data/statistics.py +67 -6
  120. anemoi/datasets/data/stores.py +149 -64
  121. anemoi/datasets/data/subset.py +159 -25
  122. anemoi/datasets/data/unchecked.py +168 -57
  123. anemoi/datasets/data/xy.py +168 -25
  124. anemoi/datasets/dates/__init__.py +191 -16
  125. anemoi/datasets/dates/groups.py +189 -47
  126. anemoi/datasets/grids.py +270 -31
  127. anemoi/datasets/testing.py +28 -1
  128. {anemoi_datasets-0.5.16.dist-info → anemoi_datasets-0.5.18.dist-info}/METADATA +9 -6
  129. anemoi_datasets-0.5.18.dist-info/RECORD +137 -0
  130. {anemoi_datasets-0.5.16.dist-info → anemoi_datasets-0.5.18.dist-info}/WHEEL +1 -1
  131. anemoi/datasets/create/functions/__init__.py +0 -66
  132. anemoi/datasets/create/functions/filters/__init__.py +0 -9
  133. anemoi/datasets/create/functions/filters/empty.py +0 -17
  134. anemoi/datasets/create/functions/filters/orog_to_z.py +0 -58
  135. anemoi/datasets/create/functions/filters/rename.py +0 -79
  136. anemoi/datasets/create/functions/filters/speeddir_to_uv.py +0 -78
  137. anemoi/datasets/create/functions/filters/uv_to_speeddir.py +0 -56
  138. anemoi/datasets/create/functions/sources/empty.py +0 -15
  139. anemoi/datasets/create/functions/sources/grib.py +0 -150
  140. anemoi/datasets/create/functions/sources/netcdf.py +0 -15
  141. anemoi/datasets/create/functions/sources/opendap.py +0 -15
  142. anemoi/datasets/create/functions/sources/recentre.py +0 -60
  143. anemoi/datasets/create/functions/sources/xarray/coordinates.py +0 -255
  144. anemoi/datasets/create/functions/sources/xarray/flavour.py +0 -472
  145. anemoi/datasets/create/functions/sources/xarray/metadata.py +0 -148
  146. anemoi/datasets/create/functions/sources/xarray/patch.py +0 -44
  147. anemoi/datasets/create/functions/sources/xarray/time.py +0 -177
  148. anemoi/datasets/create/functions/sources/xarray/variable.py +0 -188
  149. anemoi/datasets/create/functions/sources/xarray_kerchunk.py +0 -42
  150. anemoi/datasets/create/functions/sources/xarray_zarr.py +0 -15
  151. anemoi/datasets/utils/fields.py +0 -47
  152. anemoi_datasets-0.5.16.dist-info/RECORD +0 -129
  153. {anemoi_datasets-0.5.16.dist-info → anemoi_datasets-0.5.18.dist-info}/entry_points.txt +0 -0
  154. {anemoi_datasets-0.5.16.dist-info → anemoi_datasets-0.5.18.dist-info/licenses}/LICENSE +0 -0
  155. {anemoi_datasets-0.5.16.dist-info → anemoi_datasets-0.5.18.dist-info}/top_level.txt +0 -0
@@ -13,22 +13,44 @@ import math
13
13
  import time
14
14
  from collections import defaultdict
15
15
  from functools import cached_property
16
+ from typing import Any
17
+ from typing import DefaultDict
18
+ from typing import Dict
19
+ from typing import List
20
+ from typing import Optional
21
+ from typing import Tuple
16
22
 
17
23
  import numpy as np
24
+ from anemoi.utils.dates import as_timedelta
18
25
  from anemoi.utils.humanize import seconds_to_human
19
26
  from anemoi.utils.humanize import shorten_list
20
27
  from earthkit.data.core.order import build_remapping
21
28
 
29
+ from .action import ActionContext
22
30
  from .trace import trace
23
31
  from .trace import trace_datasource
24
32
 
25
33
  LOG = logging.getLogger(__name__)
26
34
 
27
35
 
28
- def _fields_metatata(variables, cube):
36
+ def _fields_metatata(variables: Tuple[str, ...], cube: Any) -> Dict[str, Any]:
37
+ """Retrieve metadata for the given variables and cube.
38
+
39
+ Parameters
40
+ ----------
41
+ variables : tuple of str
42
+ The variables to retrieve metadata for.
43
+ cube : Any
44
+ The data cube.
45
+
46
+ Returns
47
+ -------
48
+ dict
49
+ The metadata dictionary.
50
+ """
29
51
  assert isinstance(variables, tuple), variables
30
52
 
31
- KNOWN = {
53
+ KNOWN: Dict[str, Dict[str, bool]] = {
32
54
  "cos_julian_day": dict(computed_forcing=True, constant_in_time=False),
33
55
  "cos_latitude": dict(computed_forcing=True, constant_in_time=True),
34
56
  "cos_local_time": dict(computed_forcing=True, constant_in_time=False),
@@ -43,9 +65,9 @@ def _fields_metatata(variables, cube):
43
65
  "sin_longitude": dict(computed_forcing=True, constant_in_time=True),
44
66
  }
45
67
 
46
- def _merge(md1, md2):
68
+ def _merge(md1: Dict[str, Any], md2: Dict[str, Any]) -> Dict[str, Any]:
47
69
  assert set(md1.keys()) == set(md2.keys()), (set(md1.keys()), set(md2.keys()))
48
- result = {}
70
+ result: Dict[str, Any] = {}
49
71
  for k in md1.keys():
50
72
  v1 = md1[k]
51
73
  v2 = md2[k]
@@ -68,10 +90,10 @@ def _fields_metatata(variables, cube):
68
90
 
69
91
  return result
70
92
 
71
- mars = {}
72
- other = defaultdict(dict)
73
- i = -1
74
- date = None
93
+ mars: Dict[str, Any] = {}
94
+ other: DefaultDict[str, Dict[str, Any]] = defaultdict(dict)
95
+ i: int = -1
96
+ date: Optional[str] = None
75
97
  for c in cube.iterate_cubelets():
76
98
 
77
99
  if date is None:
@@ -97,10 +119,12 @@ def _fields_metatata(variables, cube):
97
119
  # assert md['param'] != 'unknown', (md, f.metadata('param'))
98
120
 
99
121
  startStep = f.metadata("startStep", default=None)
100
- assert startStep is None or isinstance(startStep, int), (startStep, type(f))
122
+ if startStep is not None:
123
+ startStep = as_timedelta(startStep)
101
124
 
102
125
  endStep = f.metadata("endStep", default=None)
103
- assert endStep is None or isinstance(endStep, int), endStep
126
+ if endStep is not None:
127
+ endStep = as_timedelta(endStep)
104
128
 
105
129
  stepTypeForConversion = f.metadata("stepTypeForConversion", default=None)
106
130
  typeOfStatisticalProcessing = f.metadata("typeOfStatisticalProcessing", default=None)
@@ -112,7 +136,7 @@ def _fields_metatata(variables, cube):
112
136
 
113
137
  if startStep != endStep:
114
138
  # https://codes.ecmwf.int/grib/format/grib2/ctables/4/10/
115
- TYPE_OF_STATISTICAL_PROCESSING = {
139
+ TYPE_OF_STATISTICAL_PROCESSING: Dict[Optional[int], Optional[str]] = {
116
140
  None: None,
117
141
  0: "average",
118
142
  1: "accumulation",
@@ -132,12 +156,12 @@ def _fields_metatata(variables, cube):
132
156
 
133
157
  # https://codes.ecmwf.int/grib/format/grib1/ctable/5/
134
158
 
135
- TIME_RANGE_INDICATOR = {
159
+ TIME_RANGE_INDICATOR: Dict[int, str] = {
136
160
  4: "accumulation",
137
161
  3: "average",
138
162
  }
139
163
 
140
- STEP_TYPE_FOR_CONVERSION = {
164
+ STEP_TYPE_FOR_CONVERSION: Dict[str, str] = {
141
165
  "min": "minimum",
142
166
  "max": "maximum",
143
167
  "accum": "accumulation",
@@ -147,7 +171,7 @@ def _fields_metatata(variables, cube):
147
171
  # A few patches
148
172
  #
149
173
 
150
- PATCHES = {
174
+ PATCHES: Dict[str, str] = {
151
175
  "10fg6": "maximum",
152
176
  "mntpr3": "minimum", # Not in param db
153
177
  "mntpr6": "minimum", # Not in param db
@@ -186,7 +210,7 @@ def _fields_metatata(variables, cube):
186
210
  else:
187
211
  mars[variables[i]] = md
188
212
 
189
- result = {}
213
+ result: Dict[str, Dict[str, Any]] = {}
190
214
  for k, v in mars.items():
191
215
  result[k] = dict(mars=v) if v else {}
192
216
  result[k].update(other[k])
@@ -197,12 +221,25 @@ def _fields_metatata(variables, cube):
197
221
  return result
198
222
 
199
223
 
200
- def _data_request(data):
201
- date = None
202
- params_levels = defaultdict(set)
203
- params_steps = defaultdict(set)
224
+ def _data_request(data: Any) -> Dict[str, Any]:
225
+ """Build a data request dictionary from the given data.
226
+
227
+ Parameters
228
+ ----------
229
+ data : Any
230
+ The data to build the request from.
204
231
 
205
- area = grid = None
232
+ Returns
233
+ -------
234
+ dict
235
+ The data request dictionary.
236
+ """
237
+ date: Optional[Any] = None
238
+ params_levels: DefaultDict[str, set] = defaultdict(set)
239
+ params_steps: DefaultDict[str, set] = defaultdict(set)
240
+
241
+ area: Optional[Any] = None
242
+ grid: Optional[Any] = None
206
243
 
207
244
  for field in data:
208
245
  try:
@@ -232,8 +269,8 @@ def _data_request(data):
232
269
  except Exception:
233
270
  LOG.error(f"Error in retrieving metadata (cannot build data request info) for {field}", exc_info=True)
234
271
 
235
- def sort(old_dic):
236
- new_dic = {}
272
+ def sort(old_dic: DefaultDict[str, set]) -> Dict[str, List[Any]]:
273
+ new_dic: Dict[str, List[Any]] = {}
237
274
  for k, v in old_dic.items():
238
275
  new_dic[k] = sorted(list(v))
239
276
  return new_dic
@@ -245,48 +282,67 @@ def _data_request(data):
245
282
 
246
283
 
247
284
  class Result:
248
- empty = False
249
- _coords_already_built = False
250
-
251
- def __init__(self, context, action_path, dates):
285
+ """Class to represent the result of an action in the dataset creation process."""
286
+
287
+ empty: bool = False
288
+ _coords_already_built: bool = False
289
+
290
+ def __init__(self, context: ActionContext, action_path: List[str], dates: Any) -> None:
291
+ """Initialize a Result instance.
292
+
293
+ Parameters
294
+ ----------
295
+ context : ActionContext
296
+ The context in which the result exists.
297
+ action_path : list of str
298
+ The action path.
299
+ dates : Any
300
+ The dates associated with the result.
301
+ """
252
302
  from anemoi.datasets.dates.groups import GroupOfDates
253
303
 
254
- from .action import ActionContext
255
-
256
304
  assert isinstance(dates, GroupOfDates), dates
257
305
 
258
306
  assert isinstance(context, ActionContext), type(context)
259
307
  assert isinstance(action_path, list), action_path
260
308
 
261
- self.context = context
262
- self.group_of_dates = dates
263
- self.action_path = action_path
309
+ self.context: Any = context
310
+ self.group_of_dates: Any = dates
311
+ self.action_path: List[str] = action_path
264
312
 
265
313
  @property
266
314
  @trace_datasource
267
- def datasource(self):
315
+ def datasource(self) -> Any:
316
+ """Retrieve the data source for the result."""
268
317
  self._raise_not_implemented()
269
318
 
270
319
  @property
271
- def data_request(self):
320
+ def data_request(self) -> Dict[str, Any]:
272
321
  """Returns a dictionary with the parameters needed to retrieve the data."""
273
322
  return _data_request(self.datasource)
274
323
 
275
- def get_cube(self):
324
+ def get_cube(self) -> Any:
325
+ """Retrieve the data cube for the result.
326
+
327
+ Returns
328
+ -------
329
+ Any
330
+ The data cube.
331
+ """
276
332
  trace("🧊", f"getting cube from {self.__class__.__name__}")
277
- ds = self.datasource
333
+ ds: Any = self.datasource
278
334
 
279
- remapping = self.context.remapping
280
- order_by = self.context.order_by
281
- flatten_grid = self.context.flatten_grid
282
- start = time.time()
335
+ remapping: Any = self.context.remapping
336
+ order_by: Any = self.context.order_by
337
+ flatten_grid: Any = self.context.flatten_grid
338
+ start: float = time.time()
283
339
  LOG.debug("Sorting dataset %s %s", dict(order_by), remapping)
284
340
  assert order_by, order_by
285
341
 
286
- patches = {"number": {None: 0}}
342
+ patches: Dict[str, Dict[Optional[Any], int]] = {"number": {None: 0}}
287
343
 
288
344
  try:
289
- cube = ds.cube(
345
+ cube: Any = ds.cube(
290
346
  order_by,
291
347
  remapping=remapping,
292
348
  flatten_values=flatten_grid,
@@ -306,9 +362,21 @@ class Result:
306
362
 
307
363
  return cube
308
364
 
309
- def explain(self, ds, *args, remapping, patches):
310
-
311
- METADATA = (
365
+ def explain(self, ds: Any, *args: Any, remapping: Any, patches: Any) -> None:
366
+ """Explain the data cube creation process.
367
+
368
+ Parameters
369
+ ----------
370
+ ds : Any
371
+ The data source.
372
+ args : Any
373
+ Additional arguments.
374
+ remapping : Any
375
+ The remapping configuration.
376
+ patches : Any
377
+ The patches configuration.
378
+ """
379
+ METADATA: Tuple[str, ...] = (
312
380
  "date",
313
381
  "time",
314
382
  "step",
@@ -333,7 +401,7 @@ class Result:
333
401
  # print("Executing", self.action_path)
334
402
  # print("Dates:", compress_dates(self.dates))
335
403
 
336
- names = []
404
+ names: List[str] = []
337
405
  for a in args:
338
406
  if isinstance(a, str):
339
407
  names.append(a)
@@ -349,7 +417,7 @@ class Result:
349
417
  for k, v in user_coords.items():
350
418
  print(f" {k:20}:", len(v), shorten_list(v, max_length=10))
351
419
  print()
352
- user_shape = tuple(len(v) for k, v in user_coords.items())
420
+ user_shape: Tuple[int, ...] = tuple(len(v) for k, v in user_coords.items())
353
421
  print("Shape of the hypercube :", user_shape)
354
422
  print(
355
423
  "Number of expected fields :", math.prod(user_shape), "=", " x ".join([str(i) for i in user_shape])
@@ -451,11 +519,27 @@ class Result:
451
519
  print()
452
520
  exit(1)
453
521
 
454
- def __repr__(self, *args, _indent_="\n", **kwargs):
455
- more = ",".join([str(a)[:5000] for a in args])
522
+ def _repr(self, *args: Any, _indent_: str = "\n", **kwargs: Any) -> str:
523
+ """Return the string representation of the Result instance.
524
+
525
+ Parameters
526
+ ----------
527
+ args : Any
528
+ Additional positional arguments.
529
+ _indent_ : str
530
+ Indentation string.
531
+ kwargs : Any
532
+ Additional keyword arguments.
533
+
534
+ Returns
535
+ -------
536
+ str
537
+ The string representation.
538
+ """
539
+ more: str = ",".join([str(a)[:5000] for a in args])
456
540
  more += ",".join([f"{k}={v}"[:5000] for k, v in kwargs.items()])
457
541
 
458
- dates = " no-dates"
542
+ dates: str = " no-dates"
459
543
  if self.group_of_dates is not None:
460
544
  dates = f" {len(self.group_of_dates)} dates"
461
545
  dates += " ("
@@ -465,32 +549,52 @@ class Result:
465
549
  dates += ")"
466
550
 
467
551
  more = more[:5000]
468
- txt = f"{self.__class__.__name__}:{dates}{_indent_}{more}"
552
+ txt: str = f"{self.__class__.__name__}:{dates}{_indent_}{more}"
469
553
  if _indent_:
470
554
  txt = txt.replace("\n", "\n ")
471
555
  return txt
472
556
 
473
- def _raise_not_implemented(self):
557
+ def __repr__(self) -> str:
558
+ """Return the string representation of the Result instance."""
559
+ return self._repr()
560
+
561
+ def _raise_not_implemented(self) -> None:
562
+ """Raise a NotImplementedError indicating the method is not implemented."""
474
563
  raise NotImplementedError(f"Not implemented in {self.__class__.__name__}")
475
564
 
476
- def _trace_datasource(self, *args, **kwargs):
565
+ def _trace_datasource(self, *args: Any, **kwargs: Any) -> str:
566
+ """Trace the data source for the result.
567
+
568
+ Parameters
569
+ ----------
570
+ args : Any
571
+ Additional positional arguments.
572
+ kwargs : Any
573
+ Additional keyword arguments.
574
+
575
+ Returns
576
+ -------
577
+ str
578
+ The trace string.
579
+ """
477
580
  return f"{self.__class__.__name__}({self.group_of_dates})"
478
581
 
479
- def build_coords(self):
582
+ def build_coords(self) -> None:
583
+ """Build the coordinates for the result."""
480
584
  if self._coords_already_built:
481
585
  return
482
586
 
483
- cube = self.get_cube()
587
+ cube: Any = self.get_cube()
484
588
 
485
- from_data = cube.user_coords
486
- from_config = self.context.order_by
589
+ from_data: Any = cube.user_coords
590
+ from_config: Any = self.context.order_by
487
591
 
488
- keys_from_config = list(from_config.keys())
489
- keys_from_data = list(from_data.keys())
592
+ keys_from_config: list = list(from_config.keys())
593
+ keys_from_data: list = list(from_data.keys())
490
594
  assert keys_from_data == keys_from_config, f"Critical error: {keys_from_data=} != {keys_from_config=}. {self=}"
491
595
 
492
- variables_key = list(from_config.keys())[1]
493
- ensembles_key = list(from_config.keys())[2]
596
+ variables_key: str = list(from_config.keys())[1]
597
+ ensembles_key: str = list(from_config.keys())[2]
494
598
 
495
599
  if isinstance(from_config[variables_key], (list, tuple)):
496
600
  assert all([v == w for v, w in zip(from_data[variables_key], from_config[variables_key])]), (
@@ -498,21 +602,22 @@ class Result:
498
602
  from_config[variables_key],
499
603
  )
500
604
 
501
- self._variables = from_data[variables_key] # "param_level"
502
- self._ensembles = from_data[ensembles_key] # "number"
605
+ self._variables: Any = from_data[variables_key] # "param_level"
606
+ self._ensembles: Any = from_data[ensembles_key] # "number"
503
607
 
504
- first_field = self.datasource[0]
505
- grid_points = first_field.grid_points()
608
+ first_field: Any = self.datasource[0]
609
+ grid_points: Any = first_field.grid_points()
506
610
 
507
- lats, lons = grid_points
611
+ lats: Any = grid_points[0]
612
+ lons: Any = grid_points[1]
508
613
 
509
614
  assert len(lats) == len(lons), (len(lats), len(lons), first_field)
510
615
  assert len(lats) == math.prod(first_field.shape), (len(lats), first_field.shape, first_field)
511
616
 
512
- north = np.amax(lats)
513
- south = np.amin(lats)
514
- east = np.amax(lons)
515
- west = np.amin(lons)
617
+ north: float = np.amax(lats)
618
+ south: float = np.amin(lats)
619
+ east: float = np.amax(lons)
620
+ west: float = np.amin(lons)
516
621
 
517
622
  assert -90 <= south <= north <= 90, (south, north, first_field)
518
623
  assert (-180 <= west <= east <= 180) or (0 <= west <= east <= 360), (
@@ -521,59 +626,68 @@ class Result:
521
626
  first_field,
522
627
  )
523
628
 
524
- grid_values = list(range(len(grid_points[0])))
629
+ grid_values: list = list(range(len(grid_points[0])))
525
630
 
526
- self._grid_points = grid_points
527
- self._resolution = first_field.resolution
528
- self._grid_values = grid_values
529
- self._field_shape = first_field.shape
530
- self._proj_string = first_field.proj_string if hasattr(first_field, "proj_string") else None
631
+ self._grid_points: Any = grid_points
632
+ self._resolution: Any = first_field.resolution
633
+ self._grid_values: Any = grid_values
634
+ self._field_shape: Any = first_field.shape
635
+ self._proj_string: Any = first_field.proj_string if hasattr(first_field, "proj_string") else None
531
636
 
532
- self._cube = cube
637
+ self._cube: Any = cube
533
638
 
534
- self._coords_already_built = True
639
+ self._coords_already_built: bool = True
535
640
 
536
641
  @property
537
- def variables(self):
642
+ def variables(self) -> List[str]:
643
+ """Retrieve the variables for the result."""
538
644
  self.build_coords()
539
645
  return self._variables
540
646
 
541
647
  @property
542
- def variables_metadata(self):
648
+ def variables_metadata(self) -> Dict[str, Any]:
649
+ """Retrieve the metadata for the variables."""
543
650
  return _fields_metatata(self.variables, self._cube)
544
651
 
545
652
  @property
546
- def ensembles(self):
653
+ def ensembles(self) -> Any:
654
+ """Retrieve the ensembles for the result."""
547
655
  self.build_coords()
548
656
  return self._ensembles
549
657
 
550
658
  @property
551
- def resolution(self):
659
+ def resolution(self) -> Any:
660
+ """Retrieve the resolution for the result."""
552
661
  self.build_coords()
553
662
  return self._resolution
554
663
 
555
664
  @property
556
- def grid_values(self):
665
+ def grid_values(self) -> Any:
666
+ """Retrieve the grid values for the result."""
557
667
  self.build_coords()
558
668
  return self._grid_values
559
669
 
560
670
  @property
561
- def grid_points(self):
671
+ def grid_points(self) -> Any:
672
+ """Retrieve the grid points for the result."""
562
673
  self.build_coords()
563
674
  return self._grid_points
564
675
 
565
676
  @property
566
- def field_shape(self):
677
+ def field_shape(self) -> Any:
678
+ """Retrieve the field shape for the result."""
567
679
  self.build_coords()
568
680
  return self._field_shape
569
681
 
570
682
  @property
571
- def proj_string(self):
683
+ def proj_string(self) -> Any:
684
+ """Retrieve the projection string for the result."""
572
685
  self.build_coords()
573
686
  return self._proj_string
574
687
 
575
688
  @cached_property
576
- def shape(self):
689
+ def shape(self) -> List[int]:
690
+ """Retrieve the shape of the result."""
577
691
  return [
578
692
  len(self.group_of_dates),
579
693
  len(self.variables),
@@ -582,7 +696,8 @@ class Result:
582
696
  ]
583
697
 
584
698
  @cached_property
585
- def coords(self):
699
+ def coords(self) -> Dict[str, Any]:
700
+ """Retrieve the coordinates of the result."""
586
701
  return {
587
702
  "dates": list(self.group_of_dates),
588
703
  "variables": self.variables,