anemoi-datasets 0.5.26__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/__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/inspect.py +27 -35
- 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/commands/validate.py +59 -0
- anemoi/datasets/compute/recentre.py +3 -6
- anemoi/datasets/create/__init__.py +64 -26
- anemoi/datasets/create/check.py +10 -12
- anemoi/datasets/create/chunks.py +1 -2
- anemoi/datasets/create/config.py +5 -6
- anemoi/datasets/create/input/__init__.py +44 -65
- anemoi/datasets/create/input/action.py +296 -238
- 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 +7 -9
- anemoi/datasets/create/input/misc.py +2 -75
- anemoi/datasets/create/input/repeated_dates.py +11 -130
- anemoi/datasets/{utils → create/input/result}/__init__.py +10 -1
- anemoi/datasets/create/input/{result.py → result/field.py} +36 -120
- 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 +120 -145
- anemoi/datasets/create/sources/accumulations2.py +20 -53
- anemoi/datasets/create/sources/anemoi_dataset.py +46 -42
- anemoi/datasets/create/sources/constants.py +39 -40
- anemoi/datasets/create/sources/empty.py +22 -19
- anemoi/datasets/create/sources/fdb.py +133 -0
- anemoi/datasets/create/sources/forcings.py +29 -29
- anemoi/datasets/create/sources/grib.py +94 -78
- anemoi/datasets/create/sources/grib_index.py +57 -55
- anemoi/datasets/create/sources/hindcasts.py +57 -59
- anemoi/datasets/create/sources/legacy.py +10 -62
- anemoi/datasets/create/sources/mars.py +121 -149
- anemoi/datasets/create/sources/netcdf.py +28 -25
- anemoi/datasets/create/sources/opendap.py +28 -26
- anemoi/datasets/create/sources/patterns.py +4 -6
- anemoi/datasets/create/sources/recentre.py +46 -48
- anemoi/datasets/create/sources/repeated_dates.py +44 -0
- anemoi/datasets/create/sources/source.py +26 -51
- anemoi/datasets/create/sources/tendencies.py +68 -98
- anemoi/datasets/create/sources/xarray.py +4 -6
- anemoi/datasets/create/sources/xarray_support/__init__.py +40 -36
- anemoi/datasets/create/sources/xarray_support/coordinates.py +8 -12
- anemoi/datasets/create/sources/xarray_support/field.py +20 -16
- anemoi/datasets/create/sources/xarray_support/fieldlist.py +11 -15
- anemoi/datasets/create/sources/xarray_support/flavour.py +42 -42
- 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 +4 -6
- anemoi/datasets/create/sources/xarray_support/time.py +10 -13
- anemoi/datasets/create/sources/xarray_support/variable.py +21 -21
- anemoi/datasets/create/sources/xarray_zarr.py +28 -25
- anemoi/datasets/create/sources/zenodo.py +43 -41
- anemoi/datasets/create/statistics/__init__.py +3 -6
- anemoi/datasets/create/testing.py +4 -0
- anemoi/datasets/create/typing.py +1 -2
- anemoi/datasets/create/utils.py +0 -43
- anemoi/datasets/create/zarr.py +7 -2
- anemoi/datasets/data/__init__.py +15 -6
- anemoi/datasets/data/complement.py +7 -12
- anemoi/datasets/data/concat.py +5 -8
- anemoi/datasets/data/dataset.py +48 -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 +22 -26
- anemoi/datasets/data/grids.py +12 -168
- 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/rolling_average.py +141 -0
- anemoi/datasets/data/select.py +13 -16
- anemoi/datasets/data/statistics.py +4 -7
- anemoi/datasets/data/stores.py +22 -29
- 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 +15 -18
- anemoi/datasets/dates/groups.py +7 -10
- anemoi/datasets/dumper.py +76 -0
- anemoi/datasets/grids.py +4 -185
- anemoi/datasets/schemas/recipe.json +131 -0
- anemoi/datasets/testing.py +93 -7
- anemoi/datasets/validate.py +598 -0
- {anemoi_datasets-0.5.26.dist-info → anemoi_datasets-0.5.28.dist-info}/METADATA +7 -4
- anemoi_datasets-0.5.28.dist-info/RECORD +134 -0
- anemoi/datasets/create/filter.py +0 -48
- anemoi/datasets/create/input/concat.py +0 -164
- anemoi/datasets/create/input/context.py +0 -89
- anemoi/datasets/create/input/empty.py +0 -54
- anemoi/datasets/create/input/filter.py +0 -118
- anemoi/datasets/create/input/function.py +0 -233
- anemoi/datasets/create/input/join.py +0 -130
- anemoi/datasets/create/input/pipe.py +0 -66
- anemoi/datasets/create/input/step.py +0 -177
- anemoi/datasets/create/input/template.py +0 -162
- anemoi_datasets-0.5.26.dist-info/RECORD +0 -131
- {anemoi_datasets-0.5.26.dist-info → anemoi_datasets-0.5.28.dist-info}/WHEEL +0 -0
- {anemoi_datasets-0.5.26.dist-info → anemoi_datasets-0.5.28.dist-info}/entry_points.txt +0 -0
- {anemoi_datasets-0.5.26.dist-info → anemoi_datasets-0.5.28.dist-info}/licenses/LICENSE +0 -0
- {anemoi_datasets-0.5.26.dist-info → anemoi_datasets-0.5.28.dist-info}/top_level.txt +0 -0
|
@@ -8,46 +8,45 @@
|
|
|
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 List
|
|
13
11
|
|
|
14
12
|
from earthkit.data import from_source
|
|
15
13
|
|
|
16
|
-
from .
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
"
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
14
|
+
from . import source_registry
|
|
15
|
+
from .legacy import LegacySource
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@source_registry.register("constants")
|
|
19
|
+
class ConstantsSource(LegacySource):
|
|
20
|
+
|
|
21
|
+
@staticmethod
|
|
22
|
+
def _execute(context: Any, dates: list[str], template: dict[str, Any], param: str) -> Any:
|
|
23
|
+
"""Deprecated function to retrieve constants data.
|
|
24
|
+
|
|
25
|
+
Parameters
|
|
26
|
+
----------
|
|
27
|
+
context : Any
|
|
28
|
+
The context object for tracing.
|
|
29
|
+
dates : list of str
|
|
30
|
+
List of dates for which data is required.
|
|
31
|
+
template : dict of str to Any
|
|
32
|
+
Template dictionary for the data source.
|
|
33
|
+
param : str
|
|
34
|
+
Parameter to retrieve.
|
|
35
|
+
|
|
36
|
+
Returns
|
|
37
|
+
-------
|
|
38
|
+
Any
|
|
39
|
+
Data retrieved from the source.
|
|
40
|
+
"""
|
|
41
|
+
from warnings import warn
|
|
42
|
+
|
|
43
|
+
warn(
|
|
44
|
+
"The source `constants` is deprecated, use `forcings` instead.",
|
|
45
|
+
DeprecationWarning,
|
|
46
|
+
stacklevel=2,
|
|
47
|
+
)
|
|
48
|
+
context.trace("✅", f"from_source(constants, {template}, {param}")
|
|
49
|
+
if len(template) == 0:
|
|
50
|
+
raise ValueError("Forcings template is empty.")
|
|
51
|
+
|
|
52
|
+
return from_source("forcings", source_or_dataset=template, date=list(dates), param=param)
|
|
@@ -9,29 +9,32 @@
|
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
from typing import Any
|
|
12
|
-
from typing import List
|
|
13
12
|
|
|
14
13
|
import earthkit.data as ekd
|
|
15
14
|
|
|
16
|
-
from .
|
|
15
|
+
from . import source_registry
|
|
16
|
+
from .legacy import LegacySource
|
|
17
17
|
|
|
18
18
|
|
|
19
|
-
@
|
|
20
|
-
|
|
21
|
-
"""Executes the loading of an empty data source.
|
|
19
|
+
@source_registry.register("empty")
|
|
20
|
+
class EmptySource(LegacySource):
|
|
22
21
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
The context in which the function is executed.
|
|
27
|
-
dates : list
|
|
28
|
-
List of dates for which data is to be loaded.
|
|
29
|
-
**kwargs : dict
|
|
30
|
-
Additional keyword arguments.
|
|
22
|
+
@staticmethod
|
|
23
|
+
def _execute(context: Any, dates: list[str], **kwargs: Any) -> ekd.FieldList:
|
|
24
|
+
"""Executes the loading of an empty data source.
|
|
31
25
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
26
|
+
Parameters
|
|
27
|
+
----------
|
|
28
|
+
context : object
|
|
29
|
+
The context in which the function is executed.
|
|
30
|
+
dates : list
|
|
31
|
+
List of dates for which data is to be loaded.
|
|
32
|
+
**kwargs : dict
|
|
33
|
+
Additional keyword arguments.
|
|
34
|
+
|
|
35
|
+
Returns
|
|
36
|
+
-------
|
|
37
|
+
ekd.FieldList
|
|
38
|
+
Loaded empty data source.
|
|
39
|
+
"""
|
|
40
|
+
return ekd.from_source("empty")
|
|
@@ -0,0 +1,133 @@
|
|
|
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
|
+
from datetime import datetime
|
|
11
|
+
from typing import Any
|
|
12
|
+
|
|
13
|
+
import earthkit.data as ekd
|
|
14
|
+
from anemoi.transform.fields import new_field_from_grid
|
|
15
|
+
from anemoi.transform.fields import new_fieldlist_from_list
|
|
16
|
+
from anemoi.transform.flavour import RuleBasedFlavour
|
|
17
|
+
from anemoi.transform.grids import grid_registry
|
|
18
|
+
|
|
19
|
+
from anemoi.datasets.create.typing import DateList
|
|
20
|
+
|
|
21
|
+
from ..source import Source
|
|
22
|
+
from . import source_registry
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@source_registry.register("fdb")
|
|
26
|
+
class FdbSource(Source):
|
|
27
|
+
"""FDB data source."""
|
|
28
|
+
|
|
29
|
+
emoji = "💽"
|
|
30
|
+
|
|
31
|
+
def __init__(
|
|
32
|
+
self,
|
|
33
|
+
context,
|
|
34
|
+
fdb_request: dict[str, Any],
|
|
35
|
+
fdb_config: dict | None = None,
|
|
36
|
+
fdb_userconfig: dict | None = None,
|
|
37
|
+
flavour: dict[str, Any] | None = None,
|
|
38
|
+
grid_definition: str | None = None,
|
|
39
|
+
**kwargs: dict[str, Any],
|
|
40
|
+
):
|
|
41
|
+
"""Initialise the FDB input.
|
|
42
|
+
|
|
43
|
+
Parameters
|
|
44
|
+
----------
|
|
45
|
+
context : dict
|
|
46
|
+
The context.
|
|
47
|
+
fdb_request: dict
|
|
48
|
+
The FDB request parameters.
|
|
49
|
+
fdb_config : dict, optional
|
|
50
|
+
The FDB config to use.
|
|
51
|
+
fdb_userconfig : dict, optional
|
|
52
|
+
The FDB userconfig to use.
|
|
53
|
+
flavour: dict, optional
|
|
54
|
+
The flavour configuration, see `anemoi.transform.flavour.RuleBasedFlavour`.
|
|
55
|
+
grid_definition : str, optional
|
|
56
|
+
The grid definition to use, see `anemoi.transform.grids.grid_registry`.
|
|
57
|
+
kwargs : dict, optional
|
|
58
|
+
Additional keyword arguments.
|
|
59
|
+
"""
|
|
60
|
+
super().__init__(context)
|
|
61
|
+
self.request = fdb_request.copy()
|
|
62
|
+
self.configs = {"config": fdb_config, "userconfig": fdb_userconfig}
|
|
63
|
+
|
|
64
|
+
self.flavour = RuleBasedFlavour(flavour) if flavour else None
|
|
65
|
+
if grid_definition is not None:
|
|
66
|
+
self.grid = grid_registry.from_config(grid_definition)
|
|
67
|
+
else:
|
|
68
|
+
self.grid = None
|
|
69
|
+
|
|
70
|
+
if "step" not in self.request:
|
|
71
|
+
self.request["step"] = 0
|
|
72
|
+
|
|
73
|
+
self.request["param"] = _shortname_to_paramid(fdb_request["param"], kwargs.pop("param_id_map", None))
|
|
74
|
+
|
|
75
|
+
# temporary workarounds for FDB use at MeteoSwiss (adoption is ongoing)
|
|
76
|
+
# thus not documented
|
|
77
|
+
self.offset_from_date = kwargs.pop("offset_from_date", None)
|
|
78
|
+
|
|
79
|
+
def execute(self, dates: DateList) -> ekd.FieldList:
|
|
80
|
+
"""Execute the FDB source.
|
|
81
|
+
|
|
82
|
+
Parameters
|
|
83
|
+
----------
|
|
84
|
+
dates : DateList
|
|
85
|
+
The input dates.
|
|
86
|
+
|
|
87
|
+
Returns
|
|
88
|
+
-------
|
|
89
|
+
ekd.FieldList
|
|
90
|
+
The output data.
|
|
91
|
+
"""
|
|
92
|
+
|
|
93
|
+
requests = []
|
|
94
|
+
for date in dates:
|
|
95
|
+
time_request = _time_request_keys(date, self.offset_from_date)
|
|
96
|
+
requests.append(self.request | time_request)
|
|
97
|
+
|
|
98
|
+
# in some cases (e.g. repeated_dates 'constant' mode), we might have a fully
|
|
99
|
+
# defined request already and an empty dates list
|
|
100
|
+
requests = requests or [self.request]
|
|
101
|
+
|
|
102
|
+
fl = ekd.from_source("empty")
|
|
103
|
+
for request in requests:
|
|
104
|
+
fl += ekd.from_source("fdb", request, **self.configs, read_all=True)
|
|
105
|
+
|
|
106
|
+
if self.grid is not None:
|
|
107
|
+
fl = new_fieldlist_from_list([new_field_from_grid(f, self.grid) for f in fl])
|
|
108
|
+
|
|
109
|
+
if self.flavour:
|
|
110
|
+
fl = self.flavour.map(fl)
|
|
111
|
+
|
|
112
|
+
return fl
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def _time_request_keys(dt: datetime, offset_from_date: bool | None = None) -> str:
|
|
116
|
+
"""Defines the time-related keys for the FDB request."""
|
|
117
|
+
out = {}
|
|
118
|
+
out["date"] = dt.strftime("%Y%m%d")
|
|
119
|
+
if offset_from_date:
|
|
120
|
+
out["time"] = "0000"
|
|
121
|
+
out["step"] = int((dt - dt.replace(hour=0, minute=0)).total_seconds() // 3600)
|
|
122
|
+
else:
|
|
123
|
+
out["time"] = dt.strftime("%H%M")
|
|
124
|
+
return out
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def _shortname_to_paramid(shortname: list[str], param_id_map: dict[str, int] | None = None) -> list[int]:
|
|
128
|
+
from anemoi.datasets.create.sources.mars import use_grib_paramid
|
|
129
|
+
|
|
130
|
+
"""Convert a shortname to a parameter ID."""
|
|
131
|
+
if param_id_map is None:
|
|
132
|
+
return use_grib_paramid(shortname)
|
|
133
|
+
return [param_id_map[s] for s in shortname]
|
|
@@ -8,35 +8,35 @@
|
|
|
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
|
from earthkit.data import from_source
|
|
14
13
|
|
|
15
|
-
from .
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
14
|
+
from . import source_registry
|
|
15
|
+
from .legacy import LegacySource
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@source_registry.register("forcings")
|
|
19
|
+
class ForcingsSource(LegacySource):
|
|
20
|
+
|
|
21
|
+
@staticmethod
|
|
22
|
+
def _execute(context: Any, dates: list[str], template: str, param: str) -> Any:
|
|
23
|
+
"""Loads forcing data from a specified source.
|
|
24
|
+
|
|
25
|
+
Parameters
|
|
26
|
+
----------
|
|
27
|
+
context : object
|
|
28
|
+
The context in which the function is executed.
|
|
29
|
+
dates : list
|
|
30
|
+
List of dates for which data is to be loaded.
|
|
31
|
+
template : FieldList
|
|
32
|
+
Template for the data source.
|
|
33
|
+
param : str
|
|
34
|
+
Parameter for the data source.
|
|
35
|
+
|
|
36
|
+
Returns
|
|
37
|
+
-------
|
|
38
|
+
object
|
|
39
|
+
Loaded forcing data.
|
|
40
|
+
"""
|
|
41
|
+
context.trace("✅", f"from_source(forcings, {template}, {param}")
|
|
42
|
+
return from_source("forcings", source_or_dataset=template, date=list(dates), param=param)
|
|
@@ -11,10 +11,6 @@
|
|
|
11
11
|
import glob
|
|
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 earthkit.data as ekd
|
|
20
16
|
from anemoi.transform.fields import new_field_from_grid
|
|
@@ -24,12 +20,13 @@ from anemoi.transform.grids import grid_registry
|
|
|
24
20
|
from earthkit.data import from_source
|
|
25
21
|
from earthkit.data.utils.patterns import Pattern
|
|
26
22
|
|
|
27
|
-
from .
|
|
23
|
+
from . import source_registry
|
|
24
|
+
from .legacy import LegacySource
|
|
28
25
|
|
|
29
26
|
LOG = logging.getLogger(__name__)
|
|
30
27
|
|
|
31
28
|
|
|
32
|
-
def check(ds: Any, paths:
|
|
29
|
+
def check(ds: Any, paths: list[str], **kwargs: Any) -> None:
|
|
33
30
|
"""Check if the dataset matches the expected number of fields.
|
|
34
31
|
|
|
35
32
|
Parameters
|
|
@@ -51,11 +48,19 @@ def check(ds: Any, paths: List[str], **kwargs: Any) -> None:
|
|
|
51
48
|
if isinstance(v, (tuple, list)):
|
|
52
49
|
count *= len(v)
|
|
53
50
|
|
|
51
|
+
# in the case of static data (e.g repeated dates) dates might be empty
|
|
52
|
+
if len(ds) != count and kwargs.get("dates", []) == []:
|
|
53
|
+
LOG.warning(
|
|
54
|
+
f"Expected {count} fields, got {len(ds)} (kwargs={kwargs}, paths={paths})"
|
|
55
|
+
f" Received empty dates - assuming this is static data."
|
|
56
|
+
)
|
|
57
|
+
return
|
|
58
|
+
|
|
54
59
|
if len(ds) != count:
|
|
55
60
|
raise ValueError(f"Expected {count} fields, got {len(ds)} (kwargs={kwargs}, paths={paths})")
|
|
56
61
|
|
|
57
62
|
|
|
58
|
-
def _expand(paths:
|
|
63
|
+
def _expand(paths: list[str]) -> Any:
|
|
59
64
|
"""Expand the given paths using glob.
|
|
60
65
|
|
|
61
66
|
Parameters
|
|
@@ -77,74 +82,85 @@ def _expand(paths: List[str]) -> Any:
|
|
|
77
82
|
yield path
|
|
78
83
|
|
|
79
84
|
|
|
80
|
-
@
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
for
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
85
|
+
@source_registry.register("grib")
|
|
86
|
+
class GribSource(LegacySource):
|
|
87
|
+
|
|
88
|
+
@staticmethod
|
|
89
|
+
def _execute(
|
|
90
|
+
context: Any,
|
|
91
|
+
dates: list[Any],
|
|
92
|
+
path: str | list[str],
|
|
93
|
+
flavour: str | dict[str, Any] | None = None,
|
|
94
|
+
grid_definition: dict[str, Any] | None = None,
|
|
95
|
+
*args: Any,
|
|
96
|
+
**kwargs: Any,
|
|
97
|
+
) -> ekd.FieldList:
|
|
98
|
+
"""Executes the function to load data from GRIB files.
|
|
99
|
+
|
|
100
|
+
Parameters
|
|
101
|
+
----------
|
|
102
|
+
context : Any
|
|
103
|
+
The context in which the function is executed.
|
|
104
|
+
dates : list of Any
|
|
105
|
+
List of dates.
|
|
106
|
+
path : str or list of str
|
|
107
|
+
Path or list of paths to the GRIB files.
|
|
108
|
+
flavour : str or dict of str to Any, optional
|
|
109
|
+
Flavour information, by default None.
|
|
110
|
+
grid_definition : dict of str to Any, optional
|
|
111
|
+
Grid definition configuration to create a Grid object, by default None.
|
|
112
|
+
*args : Any
|
|
113
|
+
Additional positional arguments.
|
|
114
|
+
**kwargs : Any
|
|
115
|
+
Additional keyword arguments.
|
|
116
|
+
|
|
117
|
+
Returns
|
|
118
|
+
-------
|
|
119
|
+
Any
|
|
120
|
+
The loaded dataset.
|
|
121
|
+
"""
|
|
122
|
+
given_paths = path if isinstance(path, list) else [path]
|
|
123
|
+
if flavour is not None:
|
|
124
|
+
flavour = RuleBasedFlavour(flavour)
|
|
125
|
+
|
|
126
|
+
if grid_definition is not None:
|
|
127
|
+
grid = grid_registry.from_config(grid_definition)
|
|
128
|
+
else:
|
|
129
|
+
grid = None
|
|
130
|
+
|
|
131
|
+
ds = from_source("empty")
|
|
132
|
+
dates = [d.isoformat() for d in dates]
|
|
133
|
+
|
|
134
|
+
for path in given_paths:
|
|
135
|
+
|
|
136
|
+
# do not substitute if not needed
|
|
137
|
+
if "{" not in path:
|
|
138
|
+
paths = [path]
|
|
139
|
+
else:
|
|
140
|
+
paths = Pattern(path).substitute(*args, date=dates, allow_extra=True, **kwargs)
|
|
141
|
+
|
|
142
|
+
for name in ("grid", "area", "rotation", "frame", "resol", "bitmap"):
|
|
143
|
+
if name in kwargs:
|
|
144
|
+
raise ValueError(f"MARS interpolation parameter '{name}' not supported")
|
|
145
|
+
|
|
146
|
+
for path in _expand(paths):
|
|
147
|
+
context.trace("📁", "PATH", path)
|
|
148
|
+
s = from_source("file", path)
|
|
149
|
+
if flavour is not None:
|
|
150
|
+
s = flavour.map(s)
|
|
151
|
+
sel_kwargs = kwargs.copy()
|
|
152
|
+
if dates != []:
|
|
153
|
+
sel_kwargs["valid_datetime"] = dates
|
|
154
|
+
s = s.sel(**sel_kwargs)
|
|
155
|
+
ds = ds + s
|
|
156
|
+
|
|
157
|
+
if kwargs and not context.partial_ok:
|
|
158
|
+
check(ds, given_paths, valid_datetime=dates, **kwargs)
|
|
159
|
+
|
|
160
|
+
if grid is not None:
|
|
161
|
+
ds = new_fieldlist_from_list([new_field_from_grid(f, grid) for f in ds])
|
|
162
|
+
|
|
163
|
+
if len(ds) == 0:
|
|
164
|
+
LOG.warning(f"No fields found for {dates} in {given_paths} (kwargs={kwargs})")
|
|
165
|
+
|
|
166
|
+
return ds
|