anemoi-datasets 0.5.27__py3-none-any.whl → 0.5.28__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/_version.py +2 -2
- anemoi/datasets/commands/recipe/__init__.py +93 -0
- anemoi/datasets/commands/recipe/format.py +55 -0
- anemoi/datasets/commands/recipe/migrate.py +555 -0
- anemoi/datasets/create/__init__.py +42 -1
- anemoi/datasets/create/config.py +2 -0
- anemoi/datasets/create/input/__init__.py +43 -63
- anemoi/datasets/create/input/action.py +296 -236
- anemoi/datasets/create/input/context/__init__.py +71 -0
- anemoi/datasets/create/input/context/field.py +54 -0
- anemoi/datasets/create/input/data_sources.py +2 -1
- anemoi/datasets/create/input/misc.py +0 -71
- anemoi/datasets/create/input/repeated_dates.py +0 -114
- anemoi/datasets/create/input/result/__init__.py +17 -0
- anemoi/datasets/create/input/{result.py → result/field.py} +9 -89
- anemoi/datasets/create/sources/accumulations.py +74 -94
- anemoi/datasets/create/sources/accumulations2.py +16 -45
- anemoi/datasets/create/sources/anemoi_dataset.py +46 -42
- anemoi/datasets/create/sources/constants.py +39 -38
- anemoi/datasets/create/sources/empty.py +26 -22
- anemoi/datasets/create/sources/forcings.py +29 -28
- anemoi/datasets/create/sources/grib.py +92 -72
- anemoi/datasets/create/sources/grib_index.py +46 -42
- anemoi/datasets/create/sources/hindcasts.py +56 -55
- anemoi/datasets/create/sources/legacy.py +10 -62
- anemoi/datasets/create/sources/mars.py +107 -131
- anemoi/datasets/create/sources/netcdf.py +28 -24
- anemoi/datasets/create/sources/opendap.py +28 -24
- anemoi/datasets/create/sources/recentre.py +42 -41
- anemoi/datasets/create/sources/repeated_dates.py +44 -0
- anemoi/datasets/create/sources/source.py +26 -48
- anemoi/datasets/create/sources/tendencies.py +67 -94
- anemoi/datasets/create/sources/xarray_support/__init__.py +29 -24
- anemoi/datasets/create/sources/xarray_support/field.py +4 -4
- anemoi/datasets/create/sources/xarray_zarr.py +28 -24
- anemoi/datasets/create/sources/zenodo.py +43 -39
- anemoi/datasets/create/utils.py +0 -42
- anemoi/datasets/data/dataset.py +6 -0
- anemoi/datasets/data/grids.py +0 -152
- anemoi/datasets/data/rolling_average.py +141 -0
- anemoi/datasets/data/stores.py +7 -9
- anemoi/datasets/dates/__init__.py +2 -0
- anemoi/datasets/dumper.py +76 -0
- anemoi/datasets/grids.py +1 -178
- anemoi/datasets/schemas/recipe.json +131 -0
- {anemoi_datasets-0.5.27.dist-info → anemoi_datasets-0.5.28.dist-info}/METADATA +5 -2
- {anemoi_datasets-0.5.27.dist-info → anemoi_datasets-0.5.28.dist-info}/RECORD +51 -51
- anemoi/datasets/create/filter.py +0 -47
- anemoi/datasets/create/input/concat.py +0 -161
- anemoi/datasets/create/input/context.py +0 -86
- anemoi/datasets/create/input/empty.py +0 -53
- anemoi/datasets/create/input/filter.py +0 -117
- anemoi/datasets/create/input/function.py +0 -232
- anemoi/datasets/create/input/join.py +0 -129
- anemoi/datasets/create/input/pipe.py +0 -66
- anemoi/datasets/create/input/step.py +0 -173
- anemoi/datasets/create/input/template.py +0 -161
- {anemoi_datasets-0.5.27.dist-info → anemoi_datasets-0.5.28.dist-info}/WHEEL +0 -0
- {anemoi_datasets-0.5.27.dist-info → anemoi_datasets-0.5.28.dist-info}/entry_points.txt +0 -0
- {anemoi_datasets-0.5.27.dist-info → anemoi_datasets-0.5.28.dist-info}/licenses/LICENSE +0 -0
- {anemoi_datasets-0.5.27.dist-info → anemoi_datasets-0.5.28.dist-info}/top_level.txt +0 -0
|
@@ -17,7 +17,7 @@ from ...dates.groups import GroupOfDates
|
|
|
17
17
|
from .action import Action
|
|
18
18
|
from .action import action_factory
|
|
19
19
|
from .misc import _tidy
|
|
20
|
-
from .result import Result
|
|
20
|
+
from .result.field import Result
|
|
21
21
|
|
|
22
22
|
LOG = logging.getLogger(__name__)
|
|
23
23
|
|
|
@@ -55,6 +55,7 @@ class DataSourcesAction(Action):
|
|
|
55
55
|
|
|
56
56
|
self.sources = [action_factory(config, context, ["data_sources"] + [a_path]) for a_path, config in configs]
|
|
57
57
|
self.input = action_factory(input, context, ["input"])
|
|
58
|
+
self.names = [a_path for a_path, config in configs]
|
|
58
59
|
|
|
59
60
|
def select(self, group_of_dates: GroupOfDates) -> "DataSourcesResult":
|
|
60
61
|
"""Selects the data sources result for the given group of dates.
|
|
@@ -8,9 +8,6 @@
|
|
|
8
8
|
# nor does it submit to any jurisdiction.
|
|
9
9
|
|
|
10
10
|
import logging
|
|
11
|
-
from collections.abc import Callable
|
|
12
|
-
from functools import wraps
|
|
13
|
-
from typing import Any
|
|
14
11
|
|
|
15
12
|
from earthkit.data import FieldList
|
|
16
13
|
from earthkit.data.core.fieldlist import MultiFieldList
|
|
@@ -18,74 +15,6 @@ from earthkit.data.core.fieldlist import MultiFieldList
|
|
|
18
15
|
LOG = logging.getLogger(__name__)
|
|
19
16
|
|
|
20
17
|
|
|
21
|
-
def parse_function_name(name: str) -> tuple[str, int | None]:
|
|
22
|
-
"""Parses a function name to extract the base name and an optional time delta.
|
|
23
|
-
|
|
24
|
-
Parameters
|
|
25
|
-
----------
|
|
26
|
-
name : str
|
|
27
|
-
The function name to parse.
|
|
28
|
-
|
|
29
|
-
Returns
|
|
30
|
-
-------
|
|
31
|
-
tuple of (str, int or None)
|
|
32
|
-
The base name and an optional time delta.
|
|
33
|
-
"""
|
|
34
|
-
if name.endswith("h") and name[:-1].isdigit():
|
|
35
|
-
|
|
36
|
-
if "-" in name:
|
|
37
|
-
name, delta = name.split("-")
|
|
38
|
-
sign = -1
|
|
39
|
-
|
|
40
|
-
elif "+" in name:
|
|
41
|
-
name, delta = name.split("+")
|
|
42
|
-
sign = 1
|
|
43
|
-
|
|
44
|
-
else:
|
|
45
|
-
return name, None
|
|
46
|
-
|
|
47
|
-
assert delta[-1] == "h", (name, delta)
|
|
48
|
-
delta = sign * int(delta[:-1])
|
|
49
|
-
return name, delta
|
|
50
|
-
|
|
51
|
-
return name, None
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
def assert_fieldlist(method: Callable[..., Any]) -> Callable[..., Any]:
|
|
55
|
-
"""Decorator to assert that the result of a method is an instance of FieldList.
|
|
56
|
-
|
|
57
|
-
Parameters
|
|
58
|
-
----------
|
|
59
|
-
method : Callable[..., Any]
|
|
60
|
-
The method to decorate.
|
|
61
|
-
|
|
62
|
-
Returns
|
|
63
|
-
-------
|
|
64
|
-
Callable[..., Any]
|
|
65
|
-
The decorated method.
|
|
66
|
-
"""
|
|
67
|
-
|
|
68
|
-
@wraps(method)
|
|
69
|
-
def wrapper(self: Any, *args: Any, **kwargs: Any) -> Any:
|
|
70
|
-
|
|
71
|
-
result = method(self, *args, **kwargs)
|
|
72
|
-
assert isinstance(result, FieldList), type(result)
|
|
73
|
-
return result
|
|
74
|
-
|
|
75
|
-
return wrapper
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
def assert_is_fieldlist(obj: object) -> None:
|
|
79
|
-
"""Asserts that the given object is an instance of FieldList.
|
|
80
|
-
|
|
81
|
-
Parameters
|
|
82
|
-
----------
|
|
83
|
-
obj : object
|
|
84
|
-
The object to check.
|
|
85
|
-
"""
|
|
86
|
-
assert isinstance(obj, FieldList), type(obj)
|
|
87
|
-
|
|
88
|
-
|
|
89
18
|
def _flatten(ds: MultiFieldList | FieldList) -> list:
|
|
90
19
|
"""Flattens a MultiFieldList or FieldList into a list of FieldList objects.
|
|
91
20
|
|
|
@@ -14,17 +14,9 @@ from collections.abc import Generator
|
|
|
14
14
|
from typing import Any
|
|
15
15
|
|
|
16
16
|
import numpy as np
|
|
17
|
-
from anemoi.transform.fields import new_field_with_valid_datetime
|
|
18
|
-
from anemoi.transform.fields import new_fieldlist_from_list
|
|
19
17
|
from anemoi.utils.dates import as_datetime
|
|
20
18
|
from anemoi.utils.dates import frequency_to_timedelta
|
|
21
19
|
|
|
22
|
-
from .action import Action
|
|
23
|
-
from .action import action_factory
|
|
24
|
-
from .join import JoinResult
|
|
25
|
-
from .result import Result
|
|
26
|
-
from .trace import trace_select
|
|
27
|
-
|
|
28
20
|
LOG = logging.getLogger(__name__)
|
|
29
21
|
|
|
30
22
|
|
|
@@ -276,109 +268,3 @@ class DateMapperConstant(DateMapper):
|
|
|
276
268
|
group_of_dates,
|
|
277
269
|
)
|
|
278
270
|
]
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
class DateMapperResult(Result):
|
|
282
|
-
"""A Result implementation that updates the valid datetime of the datasource."""
|
|
283
|
-
|
|
284
|
-
def __init__(
|
|
285
|
-
self,
|
|
286
|
-
context: Any,
|
|
287
|
-
action_path: list[str],
|
|
288
|
-
group_of_dates: Any,
|
|
289
|
-
source_result: Any,
|
|
290
|
-
mapper: DateMapper,
|
|
291
|
-
original_group_of_dates: Any,
|
|
292
|
-
) -> None:
|
|
293
|
-
"""Initialize DateMapperResult.
|
|
294
|
-
|
|
295
|
-
Parameters
|
|
296
|
-
----------
|
|
297
|
-
context : Any
|
|
298
|
-
The context.
|
|
299
|
-
action_path : list of str
|
|
300
|
-
The action path.
|
|
301
|
-
group_of_dates : Any
|
|
302
|
-
The group of dates.
|
|
303
|
-
source_result : Any
|
|
304
|
-
The source result.
|
|
305
|
-
mapper : DateMapper
|
|
306
|
-
The date mapper.
|
|
307
|
-
original_group_of_dates : Any
|
|
308
|
-
The original group of dates.
|
|
309
|
-
"""
|
|
310
|
-
super().__init__(context, action_path, group_of_dates)
|
|
311
|
-
|
|
312
|
-
self.source_results: Any = source_result
|
|
313
|
-
self.mapper: DateMapper = mapper
|
|
314
|
-
self.original_group_of_dates: Any = original_group_of_dates
|
|
315
|
-
|
|
316
|
-
@property
|
|
317
|
-
def datasource(self) -> Any:
|
|
318
|
-
"""Get the datasource with updated valid datetime."""
|
|
319
|
-
result: list = []
|
|
320
|
-
|
|
321
|
-
for field in self.source_results.datasource:
|
|
322
|
-
for date in self.original_group_of_dates:
|
|
323
|
-
result.append(new_field_with_valid_datetime(field, date))
|
|
324
|
-
|
|
325
|
-
if not result:
|
|
326
|
-
raise ValueError("repeated_dates: no input data found")
|
|
327
|
-
|
|
328
|
-
return new_fieldlist_from_list(result)
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
class RepeatedDatesAction(Action):
|
|
332
|
-
"""An Action implementation that selects and transforms a group of dates."""
|
|
333
|
-
|
|
334
|
-
def __init__(self, context: Any, action_path: list[str], source: Any, mode: str, **kwargs: Any) -> None:
|
|
335
|
-
"""Initialize RepeatedDatesAction.
|
|
336
|
-
|
|
337
|
-
Args:
|
|
338
|
-
context (Any): The context.
|
|
339
|
-
action_path (List[str]): The action path.
|
|
340
|
-
source (Any): The data source.
|
|
341
|
-
mode (str): The mode for date mapping.
|
|
342
|
-
**kwargs (Any): Additional arguments.
|
|
343
|
-
"""
|
|
344
|
-
super().__init__(context, action_path, source, mode, **kwargs)
|
|
345
|
-
|
|
346
|
-
self.source: Any = action_factory(source, context, action_path + ["source"])
|
|
347
|
-
self.mapper: DateMapper = DateMapper.from_mode(mode, self.source, kwargs)
|
|
348
|
-
|
|
349
|
-
@trace_select
|
|
350
|
-
def select(self, group_of_dates: Any) -> JoinResult:
|
|
351
|
-
"""Select and transform the group of dates.
|
|
352
|
-
|
|
353
|
-
Args:
|
|
354
|
-
group_of_dates (Any): The group of dates to select.
|
|
355
|
-
|
|
356
|
-
Returns
|
|
357
|
-
-------
|
|
358
|
-
JoinResult
|
|
359
|
-
The result of the join operation.
|
|
360
|
-
"""
|
|
361
|
-
results: list = []
|
|
362
|
-
for one_date_group, many_dates_group in self.mapper.transform(group_of_dates):
|
|
363
|
-
results.append(
|
|
364
|
-
DateMapperResult(
|
|
365
|
-
self.context,
|
|
366
|
-
self.action_path,
|
|
367
|
-
one_date_group,
|
|
368
|
-
self.source.select(one_date_group),
|
|
369
|
-
self.mapper,
|
|
370
|
-
many_dates_group,
|
|
371
|
-
)
|
|
372
|
-
)
|
|
373
|
-
|
|
374
|
-
return JoinResult(self.context, self.action_path, group_of_dates, results)
|
|
375
|
-
|
|
376
|
-
def __repr__(self) -> str:
|
|
377
|
-
"""Get the string representation of the action.
|
|
378
|
-
|
|
379
|
-
Returns
|
|
380
|
-
-------
|
|
381
|
-
str
|
|
382
|
-
The string representation.
|
|
383
|
-
"""
|
|
384
|
-
return f"MultiDateMatchAction({self.source}, {self.mapper})"
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# (C) Copyright 2025 Anemoi contributors.
|
|
2
|
+
#
|
|
3
|
+
# This software is licensed under the terms of the Apache Licence Version 2.0
|
|
4
|
+
# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
|
|
5
|
+
#
|
|
6
|
+
# In applying this licence, ECMWF does not waive the privileges and immunities
|
|
7
|
+
# granted to it by virtue of its status as an intergovernmental organisation
|
|
8
|
+
# nor does it submit to any jurisdiction.
|
|
9
|
+
|
|
10
|
+
import logging
|
|
11
|
+
from abc import ABC
|
|
12
|
+
|
|
13
|
+
LOG = logging.getLogger(__name__)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class Result(ABC):
|
|
17
|
+
pass
|
|
@@ -22,9 +22,7 @@ from anemoi.utils.humanize import seconds_to_human
|
|
|
22
22
|
from anemoi.utils.humanize import shorten_list
|
|
23
23
|
from earthkit.data.core.order import build_remapping
|
|
24
24
|
|
|
25
|
-
from .
|
|
26
|
-
from .trace import trace
|
|
27
|
-
from .trace import trace_datasource
|
|
25
|
+
from . import Result
|
|
28
26
|
|
|
29
27
|
LOG = logging.getLogger(__name__)
|
|
30
28
|
|
|
@@ -278,40 +276,22 @@ def _data_request(data: Any) -> dict[str, Any]:
|
|
|
278
276
|
return dict(param_level=params_levels, param_step=params_steps, area=area, grid=grid)
|
|
279
277
|
|
|
280
278
|
|
|
281
|
-
class Result:
|
|
279
|
+
class FieldResult(Result):
|
|
282
280
|
"""Class to represent the result of an action in the dataset creation process."""
|
|
283
281
|
|
|
284
282
|
empty: bool = False
|
|
285
283
|
_coords_already_built: bool = False
|
|
286
284
|
|
|
287
|
-
def __init__(self, context:
|
|
288
|
-
"""Initialize a Result instance.
|
|
285
|
+
def __init__(self, context: Any, datasource: Any) -> None:
|
|
289
286
|
|
|
290
|
-
Parameters
|
|
291
|
-
----------
|
|
292
|
-
context : ActionContext
|
|
293
|
-
The context in which the result exists.
|
|
294
|
-
action_path : list of str
|
|
295
|
-
The action path.
|
|
296
|
-
dates : Any
|
|
297
|
-
The dates associated with the result.
|
|
298
|
-
"""
|
|
299
287
|
from anemoi.datasets.dates.groups import GroupOfDates
|
|
300
288
|
|
|
301
|
-
assert isinstance(dates, GroupOfDates), dates
|
|
302
|
-
|
|
303
|
-
assert isinstance(context, ActionContext), type(context)
|
|
304
|
-
assert isinstance(action_path, list), action_path
|
|
305
|
-
|
|
306
289
|
self.context: Any = context
|
|
307
|
-
self.
|
|
308
|
-
self.
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
def datasource(self) -> Any:
|
|
313
|
-
"""Retrieve the data source for the result."""
|
|
314
|
-
self._raise_not_implemented()
|
|
290
|
+
self.datasource = datasource
|
|
291
|
+
self.group_of_dates = context.argument
|
|
292
|
+
assert isinstance(
|
|
293
|
+
self.group_of_dates, GroupOfDates
|
|
294
|
+
), f"Expected group_of_dates to be a GroupOfDates, got {type(self.group_of_dates)}: {self.group_of_dates}"
|
|
315
295
|
|
|
316
296
|
@property
|
|
317
297
|
def data_request(self) -> dict[str, Any]:
|
|
@@ -326,7 +306,7 @@ class Result:
|
|
|
326
306
|
Any
|
|
327
307
|
The data cube.
|
|
328
308
|
"""
|
|
329
|
-
|
|
309
|
+
|
|
330
310
|
ds: Any = self.datasource
|
|
331
311
|
|
|
332
312
|
remapping: Any = self.context.remapping
|
|
@@ -519,66 +499,6 @@ class Result:
|
|
|
519
499
|
print()
|
|
520
500
|
exit(1)
|
|
521
501
|
|
|
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])
|
|
540
|
-
more += ",".join([f"{k}={v}"[:5000] for k, v in kwargs.items()])
|
|
541
|
-
|
|
542
|
-
dates: str = " no-dates"
|
|
543
|
-
if self.group_of_dates is not None:
|
|
544
|
-
dates = f" {len(self.group_of_dates)} dates"
|
|
545
|
-
dates += " ("
|
|
546
|
-
dates += "/".join(d.strftime("%Y-%m-%dT%H:%M") for d in self.group_of_dates)
|
|
547
|
-
if len(dates) > 100:
|
|
548
|
-
dates = dates[:100] + "..."
|
|
549
|
-
dates += ")"
|
|
550
|
-
|
|
551
|
-
more = more[:5000]
|
|
552
|
-
txt: str = f"{self.__class__.__name__}:{dates}{_indent_}{more}"
|
|
553
|
-
if _indent_:
|
|
554
|
-
txt = txt.replace("\n", "\n ")
|
|
555
|
-
return txt
|
|
556
|
-
|
|
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."""
|
|
563
|
-
raise NotImplementedError(f"Not implemented in {self.__class__.__name__}")
|
|
564
|
-
|
|
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
|
-
"""
|
|
580
|
-
return f"{self.__class__.__name__}({self.group_of_dates})"
|
|
581
|
-
|
|
582
502
|
def build_coords(self) -> None:
|
|
583
503
|
"""Build the coordinates for the result."""
|
|
584
504
|
if self._coords_already_built:
|
|
@@ -20,12 +20,13 @@ from earthkit.data.core.temporary import temp_file
|
|
|
20
20
|
from earthkit.data.readers.grib.output import new_grib_output
|
|
21
21
|
from numpy.typing import NDArray
|
|
22
22
|
|
|
23
|
-
from anemoi.datasets.create.
|
|
23
|
+
from anemoi.datasets.create.sources import source_registry
|
|
24
24
|
|
|
25
|
-
from .legacy import
|
|
25
|
+
from .legacy import LegacySource
|
|
26
26
|
from .mars import mars
|
|
27
27
|
|
|
28
28
|
LOG = logging.getLogger(__name__)
|
|
29
|
+
MISSING_VALUE = 1e-38
|
|
29
30
|
|
|
30
31
|
|
|
31
32
|
def _member(field: Any) -> int:
|
|
@@ -168,6 +169,7 @@ class Accumulation:
|
|
|
168
169
|
# are used to store the end step
|
|
169
170
|
|
|
170
171
|
edition = template.metadata("edition")
|
|
172
|
+
assert np.all(self.values != MISSING_VALUE)
|
|
171
173
|
|
|
172
174
|
if edition == 1 and self.endStep > 254:
|
|
173
175
|
self.out.write(
|
|
@@ -176,6 +178,7 @@ class Accumulation:
|
|
|
176
178
|
stepType="instant",
|
|
177
179
|
step=self.endStep,
|
|
178
180
|
check_nans=True,
|
|
181
|
+
missing_value=MISSING_VALUE,
|
|
179
182
|
)
|
|
180
183
|
else:
|
|
181
184
|
self.out.write(
|
|
@@ -185,6 +188,7 @@ class Accumulation:
|
|
|
185
188
|
startStep=self.startStep,
|
|
186
189
|
endStep=self.endStep,
|
|
187
190
|
check_nans=True,
|
|
191
|
+
missing_value=MISSING_VALUE,
|
|
188
192
|
)
|
|
189
193
|
self.values = None
|
|
190
194
|
self.done = True
|
|
@@ -205,9 +209,6 @@ class Accumulation:
|
|
|
205
209
|
if step not in self.steps:
|
|
206
210
|
return
|
|
207
211
|
|
|
208
|
-
if not np.all(values >= 0):
|
|
209
|
-
warnings.warn(f"Negative values for {field}: {np.nanmin(values)} {np.nanmax(values)}")
|
|
210
|
-
|
|
211
212
|
assert not self.done, (self.key, step)
|
|
212
213
|
assert step not in self.seen, (self.key, step)
|
|
213
214
|
|
|
@@ -966,97 +967,76 @@ def _scda(request: dict[str, Any]) -> dict[str, Any]:
|
|
|
966
967
|
return request
|
|
967
968
|
|
|
968
969
|
|
|
969
|
-
@
|
|
970
|
-
|
|
971
|
-
context: Any, dates: list[datetime.datetime], use_cdsapi_dataset: str | None = None, **request: Any
|
|
972
|
-
) -> Any:
|
|
973
|
-
"""Computes accumulations based on the provided context, dates, and request parameters.
|
|
974
|
-
|
|
975
|
-
Parameters
|
|
976
|
-
----------
|
|
977
|
-
context : Any
|
|
978
|
-
Context for the computation.
|
|
979
|
-
dates : List[datetime.datetime]
|
|
980
|
-
List of dates.
|
|
981
|
-
use_cdsapi_dataset : Optional[str], optional
|
|
982
|
-
CDSAPI dataset to use. Defaults to None.
|
|
983
|
-
**request : Any
|
|
984
|
-
Additional request parameters.
|
|
985
|
-
|
|
986
|
-
Returns
|
|
987
|
-
-------
|
|
988
|
-
Any
|
|
989
|
-
The computed accumulations.
|
|
990
|
-
"""
|
|
991
|
-
|
|
992
|
-
if (
|
|
993
|
-
request.get("class") == "ea"
|
|
994
|
-
and request.get("stream", "oper") == "oper"
|
|
995
|
-
and request.get("accumulation_period") == 24
|
|
996
|
-
):
|
|
997
|
-
from .accumulations2 import accumulations as accumulations2
|
|
998
|
-
|
|
999
|
-
LOG.warning(
|
|
1000
|
-
"🧪️ Experimental features: Using accumulations2, because class=ea stream=oper and accumulation_period=24"
|
|
1001
|
-
)
|
|
1002
|
-
return accumulations2(context, dates, **request)
|
|
1003
|
-
|
|
1004
|
-
_to_list(request["param"])
|
|
1005
|
-
class_ = request.get("class", "od")
|
|
1006
|
-
stream = request.get("stream", "oper")
|
|
1007
|
-
|
|
1008
|
-
user_accumulation_period = request.pop("accumulation_period", 6)
|
|
1009
|
-
accumulations_reset_frequency = request.pop("accumulations_reset_frequency", None)
|
|
1010
|
-
user_date = request.pop("date", None)
|
|
1011
|
-
|
|
1012
|
-
# If `data_accumulation_period` is not set, this means that the accumulations are from the start
|
|
1013
|
-
# of the forecast.
|
|
1014
|
-
|
|
1015
|
-
KWARGS = {
|
|
1016
|
-
("od", "oper"): dict(patch=_scda),
|
|
1017
|
-
("od", "elda"): dict(base_times=(6, 18)),
|
|
1018
|
-
("od", "enfo"): dict(base_times=(0, 6, 12, 18)),
|
|
1019
|
-
("ea", "oper"): dict(data_accumulation_period=1, base_times=(6, 18)),
|
|
1020
|
-
("ea", "enda"): dict(data_accumulation_period=3, base_times=(6, 18)),
|
|
1021
|
-
("rr", "oper"): dict(base_times=(0, 3, 6, 9, 12, 15, 18, 21)),
|
|
1022
|
-
("l5", "oper"): dict(data_accumulation_period=1, base_times=(0,)),
|
|
1023
|
-
}
|
|
1024
|
-
|
|
1025
|
-
kwargs = KWARGS.get((class_, stream), {})
|
|
1026
|
-
|
|
1027
|
-
context.trace("🌧️", f"accumulations {request} {user_accumulation_period} {kwargs}")
|
|
1028
|
-
|
|
1029
|
-
return _compute_accumulations(
|
|
1030
|
-
context,
|
|
1031
|
-
dates,
|
|
1032
|
-
request,
|
|
1033
|
-
user_accumulation_period=user_accumulation_period,
|
|
1034
|
-
accumulations_reset_frequency=accumulations_reset_frequency,
|
|
1035
|
-
use_cdsapi_dataset=use_cdsapi_dataset,
|
|
1036
|
-
user_date=user_date,
|
|
1037
|
-
**kwargs,
|
|
1038
|
-
)
|
|
970
|
+
@source_registry.register("accumulations")
|
|
971
|
+
class AccumulationsSource(LegacySource):
|
|
1039
972
|
|
|
973
|
+
@staticmethod
|
|
974
|
+
def _execute(
|
|
975
|
+
context: Any, dates: list[datetime.datetime], use_cdsapi_dataset: str | None = None, **request: Any
|
|
976
|
+
) -> Any:
|
|
977
|
+
"""Computes accumulations based on the provided context, dates, and request parameters.
|
|
1040
978
|
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
979
|
+
Parameters
|
|
980
|
+
----------
|
|
981
|
+
context : Any
|
|
982
|
+
Context for the computation.
|
|
983
|
+
dates : List[datetime.datetime]
|
|
984
|
+
List of dates.
|
|
985
|
+
use_cdsapi_dataset : Optional[str], optional
|
|
986
|
+
CDSAPI dataset to use. Defaults to None.
|
|
987
|
+
**request : Any
|
|
988
|
+
Additional request parameters.
|
|
1045
989
|
|
|
1046
|
-
|
|
990
|
+
Returns
|
|
991
|
+
-------
|
|
992
|
+
Any
|
|
993
|
+
The computed accumulations.
|
|
1047
994
|
"""
|
|
1048
|
-
class: ea
|
|
1049
|
-
expver: '0001'
|
|
1050
|
-
grid: 20./20.
|
|
1051
|
-
levtype: sfc
|
|
1052
|
-
# number: [0, 1]
|
|
1053
|
-
# stream: enda
|
|
1054
|
-
param: [cp, tp]
|
|
1055
|
-
# accumulation_period: 6h
|
|
1056
|
-
"""
|
|
1057
|
-
)
|
|
1058
|
-
dates = yaml.safe_load("[2022-12-30 18:00, 2022-12-31 00:00, 2022-12-31 06:00, 2022-12-31 12:00]")
|
|
1059
|
-
dates = to_datetime_list(dates)
|
|
1060
995
|
|
|
1061
|
-
|
|
1062
|
-
|
|
996
|
+
if (
|
|
997
|
+
request.get("class") == "ea"
|
|
998
|
+
and request.get("stream", "oper") == "oper"
|
|
999
|
+
and request.get("accumulation_period") == 24
|
|
1000
|
+
):
|
|
1001
|
+
from .accumulations2 import Accumulations2Source
|
|
1002
|
+
|
|
1003
|
+
LOG.warning(
|
|
1004
|
+
"🧪️ Experimental features: Using accumulations2, because class=ea stream=oper and accumulation_period=24"
|
|
1005
|
+
)
|
|
1006
|
+
return Accumulations2Source._execute(context, dates, **request)
|
|
1007
|
+
|
|
1008
|
+
_to_list(request["param"])
|
|
1009
|
+
class_ = request.get("class", "od")
|
|
1010
|
+
stream = request.get("stream", "oper")
|
|
1011
|
+
|
|
1012
|
+
user_accumulation_period = request.pop("accumulation_period", 6)
|
|
1013
|
+
accumulations_reset_frequency = request.pop("accumulations_reset_frequency", None)
|
|
1014
|
+
user_date = request.pop("date", None)
|
|
1015
|
+
|
|
1016
|
+
# If `data_accumulation_period` is not set, this means that the accumulations are from the start
|
|
1017
|
+
# of the forecast.
|
|
1018
|
+
|
|
1019
|
+
KWARGS = {
|
|
1020
|
+
("od", "oper"): dict(patch=_scda),
|
|
1021
|
+
("od", "elda"): dict(base_times=(6, 18)),
|
|
1022
|
+
("od", "enfo"): dict(base_times=(0, 6, 12, 18)),
|
|
1023
|
+
("ea", "oper"): dict(data_accumulation_period=1, base_times=(6, 18)),
|
|
1024
|
+
("ea", "enda"): dict(data_accumulation_period=3, base_times=(6, 18)),
|
|
1025
|
+
("rr", "oper"): dict(base_times=(0, 3, 6, 9, 12, 15, 18, 21)),
|
|
1026
|
+
("l5", "oper"): dict(data_accumulation_period=1, base_times=(0,)),
|
|
1027
|
+
}
|
|
1028
|
+
|
|
1029
|
+
kwargs = KWARGS.get((class_, stream), {})
|
|
1030
|
+
|
|
1031
|
+
context.trace("🌧️", f"accumulations {request} {user_accumulation_period} {kwargs}")
|
|
1032
|
+
|
|
1033
|
+
return _compute_accumulations(
|
|
1034
|
+
context,
|
|
1035
|
+
dates,
|
|
1036
|
+
request,
|
|
1037
|
+
user_accumulation_period=user_accumulation_period,
|
|
1038
|
+
accumulations_reset_frequency=accumulations_reset_frequency,
|
|
1039
|
+
use_cdsapi_dataset=use_cdsapi_dataset,
|
|
1040
|
+
user_date=user_date,
|
|
1041
|
+
**kwargs,
|
|
1042
|
+
)
|
|
@@ -18,10 +18,10 @@ import numpy as np
|
|
|
18
18
|
from earthkit.data.core.temporary import temp_file
|
|
19
19
|
from earthkit.data.readers.grib.output import new_grib_output
|
|
20
20
|
|
|
21
|
+
from anemoi.datasets.create.sources import source_registry
|
|
21
22
|
from anemoi.datasets.create.sources.mars import mars
|
|
22
|
-
from anemoi.datasets.create.utils import to_datetime_list
|
|
23
23
|
|
|
24
|
-
from .legacy import
|
|
24
|
+
from .legacy import LegacySource
|
|
25
25
|
|
|
26
26
|
LOG = logging.getLogger(__name__)
|
|
27
27
|
|
|
@@ -599,49 +599,20 @@ def _scda(request: dict[str, Any]) -> dict[str, Any]:
|
|
|
599
599
|
return request
|
|
600
600
|
|
|
601
601
|
|
|
602
|
-
@
|
|
603
|
-
|
|
604
|
-
_to_list(request["param"])
|
|
605
|
-
user_accumulation_period = request.pop("accumulation_period", 6)
|
|
606
|
-
user_accumulation_period = datetime.timedelta(hours=user_accumulation_period)
|
|
602
|
+
@source_registry.register("accumulations2")
|
|
603
|
+
class Accumulations2Source(LegacySource):
|
|
607
604
|
|
|
608
|
-
|
|
605
|
+
@staticmethod
|
|
606
|
+
def _execute(context, dates, **request):
|
|
607
|
+
_to_list(request["param"])
|
|
608
|
+
user_accumulation_period = request.pop("accumulation_period", 6)
|
|
609
|
+
user_accumulation_period = datetime.timedelta(hours=user_accumulation_period)
|
|
609
610
|
|
|
610
|
-
|
|
611
|
-
context,
|
|
612
|
-
dates,
|
|
613
|
-
request,
|
|
614
|
-
user_accumulation_period=user_accumulation_period,
|
|
615
|
-
)
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
execute = accumulations
|
|
619
|
-
|
|
620
|
-
if __name__ == "__main__":
|
|
621
|
-
import yaml
|
|
622
|
-
|
|
623
|
-
config = yaml.safe_load(
|
|
624
|
-
"""
|
|
625
|
-
class: ea
|
|
626
|
-
expver: '0001'
|
|
627
|
-
grid: 20./20.
|
|
628
|
-
levtype: sfc
|
|
629
|
-
# number: [0, 1]
|
|
630
|
-
# stream: enda
|
|
631
|
-
param: [cp, tp]
|
|
632
|
-
# accumulation_period: 6h
|
|
633
|
-
accumulation_period: 2
|
|
634
|
-
"""
|
|
635
|
-
)
|
|
636
|
-
dates = yaml.safe_load("[2022-12-31 00:00, 2022-12-31 06:00]")
|
|
637
|
-
# dates = yaml.safe_load("[2022-12-30 18:00, 2022-12-31 00:00, 2022-12-31 06:00, 2022-12-31 12:00]")
|
|
638
|
-
dates = to_datetime_list(dates)
|
|
639
|
-
|
|
640
|
-
class Context:
|
|
641
|
-
use_grib_paramid = True
|
|
611
|
+
context.trace("🌧️", f"accumulations {request} {user_accumulation_period}")
|
|
642
612
|
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
613
|
+
return _compute_accumulations(
|
|
614
|
+
context,
|
|
615
|
+
dates,
|
|
616
|
+
request,
|
|
617
|
+
user_accumulation_period=user_accumulation_period,
|
|
618
|
+
)
|