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.
Files changed (116) 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/inspect.py +27 -35
  7. anemoi/datasets/commands/recipe/__init__.py +93 -0
  8. anemoi/datasets/commands/recipe/format.py +55 -0
  9. anemoi/datasets/commands/recipe/migrate.py +555 -0
  10. anemoi/datasets/commands/validate.py +59 -0
  11. anemoi/datasets/compute/recentre.py +3 -6
  12. anemoi/datasets/create/__init__.py +64 -26
  13. anemoi/datasets/create/check.py +10 -12
  14. anemoi/datasets/create/chunks.py +1 -2
  15. anemoi/datasets/create/config.py +5 -6
  16. anemoi/datasets/create/input/__init__.py +44 -65
  17. anemoi/datasets/create/input/action.py +296 -238
  18. anemoi/datasets/create/input/context/__init__.py +71 -0
  19. anemoi/datasets/create/input/context/field.py +54 -0
  20. anemoi/datasets/create/input/data_sources.py +7 -9
  21. anemoi/datasets/create/input/misc.py +2 -75
  22. anemoi/datasets/create/input/repeated_dates.py +11 -130
  23. anemoi/datasets/{utils → create/input/result}/__init__.py +10 -1
  24. anemoi/datasets/create/input/{result.py → result/field.py} +36 -120
  25. anemoi/datasets/create/input/trace.py +1 -1
  26. anemoi/datasets/create/patch.py +1 -2
  27. anemoi/datasets/create/persistent.py +3 -5
  28. anemoi/datasets/create/size.py +1 -3
  29. anemoi/datasets/create/sources/accumulations.py +120 -145
  30. anemoi/datasets/create/sources/accumulations2.py +20 -53
  31. anemoi/datasets/create/sources/anemoi_dataset.py +46 -42
  32. anemoi/datasets/create/sources/constants.py +39 -40
  33. anemoi/datasets/create/sources/empty.py +22 -19
  34. anemoi/datasets/create/sources/fdb.py +133 -0
  35. anemoi/datasets/create/sources/forcings.py +29 -29
  36. anemoi/datasets/create/sources/grib.py +94 -78
  37. anemoi/datasets/create/sources/grib_index.py +57 -55
  38. anemoi/datasets/create/sources/hindcasts.py +57 -59
  39. anemoi/datasets/create/sources/legacy.py +10 -62
  40. anemoi/datasets/create/sources/mars.py +121 -149
  41. anemoi/datasets/create/sources/netcdf.py +28 -25
  42. anemoi/datasets/create/sources/opendap.py +28 -26
  43. anemoi/datasets/create/sources/patterns.py +4 -6
  44. anemoi/datasets/create/sources/recentre.py +46 -48
  45. anemoi/datasets/create/sources/repeated_dates.py +44 -0
  46. anemoi/datasets/create/sources/source.py +26 -51
  47. anemoi/datasets/create/sources/tendencies.py +68 -98
  48. anemoi/datasets/create/sources/xarray.py +4 -6
  49. anemoi/datasets/create/sources/xarray_support/__init__.py +40 -36
  50. anemoi/datasets/create/sources/xarray_support/coordinates.py +8 -12
  51. anemoi/datasets/create/sources/xarray_support/field.py +20 -16
  52. anemoi/datasets/create/sources/xarray_support/fieldlist.py +11 -15
  53. anemoi/datasets/create/sources/xarray_support/flavour.py +42 -42
  54. anemoi/datasets/create/sources/xarray_support/grid.py +15 -9
  55. anemoi/datasets/create/sources/xarray_support/metadata.py +19 -128
  56. anemoi/datasets/create/sources/xarray_support/patch.py +4 -6
  57. anemoi/datasets/create/sources/xarray_support/time.py +10 -13
  58. anemoi/datasets/create/sources/xarray_support/variable.py +21 -21
  59. anemoi/datasets/create/sources/xarray_zarr.py +28 -25
  60. anemoi/datasets/create/sources/zenodo.py +43 -41
  61. anemoi/datasets/create/statistics/__init__.py +3 -6
  62. anemoi/datasets/create/testing.py +4 -0
  63. anemoi/datasets/create/typing.py +1 -2
  64. anemoi/datasets/create/utils.py +0 -43
  65. anemoi/datasets/create/zarr.py +7 -2
  66. anemoi/datasets/data/__init__.py +15 -6
  67. anemoi/datasets/data/complement.py +7 -12
  68. anemoi/datasets/data/concat.py +5 -8
  69. anemoi/datasets/data/dataset.py +48 -47
  70. anemoi/datasets/data/debug.py +7 -9
  71. anemoi/datasets/data/ensemble.py +4 -6
  72. anemoi/datasets/data/fill_missing.py +7 -10
  73. anemoi/datasets/data/forwards.py +22 -26
  74. anemoi/datasets/data/grids.py +12 -168
  75. anemoi/datasets/data/indexing.py +9 -12
  76. anemoi/datasets/data/interpolate.py +7 -15
  77. anemoi/datasets/data/join.py +8 -12
  78. anemoi/datasets/data/masked.py +6 -11
  79. anemoi/datasets/data/merge.py +5 -9
  80. anemoi/datasets/data/misc.py +41 -45
  81. anemoi/datasets/data/missing.py +11 -16
  82. anemoi/datasets/data/observations/__init__.py +8 -14
  83. anemoi/datasets/data/padded.py +3 -5
  84. anemoi/datasets/data/records/backends/__init__.py +2 -2
  85. anemoi/datasets/data/rescale.py +5 -12
  86. anemoi/datasets/data/rolling_average.py +141 -0
  87. anemoi/datasets/data/select.py +13 -16
  88. anemoi/datasets/data/statistics.py +4 -7
  89. anemoi/datasets/data/stores.py +22 -29
  90. anemoi/datasets/data/subset.py +8 -11
  91. anemoi/datasets/data/unchecked.py +7 -11
  92. anemoi/datasets/data/xy.py +25 -21
  93. anemoi/datasets/dates/__init__.py +15 -18
  94. anemoi/datasets/dates/groups.py +7 -10
  95. anemoi/datasets/dumper.py +76 -0
  96. anemoi/datasets/grids.py +4 -185
  97. anemoi/datasets/schemas/recipe.json +131 -0
  98. anemoi/datasets/testing.py +93 -7
  99. anemoi/datasets/validate.py +598 -0
  100. {anemoi_datasets-0.5.26.dist-info → anemoi_datasets-0.5.28.dist-info}/METADATA +7 -4
  101. anemoi_datasets-0.5.28.dist-info/RECORD +134 -0
  102. anemoi/datasets/create/filter.py +0 -48
  103. anemoi/datasets/create/input/concat.py +0 -164
  104. anemoi/datasets/create/input/context.py +0 -89
  105. anemoi/datasets/create/input/empty.py +0 -54
  106. anemoi/datasets/create/input/filter.py +0 -118
  107. anemoi/datasets/create/input/function.py +0 -233
  108. anemoi/datasets/create/input/join.py +0 -130
  109. anemoi/datasets/create/input/pipe.py +0 -66
  110. anemoi/datasets/create/input/step.py +0 -177
  111. anemoi/datasets/create/input/template.py +0 -162
  112. anemoi_datasets-0.5.26.dist-info/RECORD +0 -131
  113. {anemoi_datasets-0.5.26.dist-info → anemoi_datasets-0.5.28.dist-info}/WHEEL +0 -0
  114. {anemoi_datasets-0.5.26.dist-info → anemoi_datasets-0.5.28.dist-info}/entry_points.txt +0 -0
  115. {anemoi_datasets-0.5.26.dist-info → anemoi_datasets-0.5.28.dist-info}/licenses/LICENSE +0 -0
  116. {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 .legacy import legacy_source
17
-
18
-
19
- @legacy_source(__file__)
20
- def constants(context: Any, dates: List[str], template: Dict[str, Any], param: str) -> Any:
21
- """Deprecated function to retrieve constants data.
22
-
23
- Parameters
24
- ----------
25
- context : Any
26
- The context object for tracing.
27
- dates : list of str
28
- List of dates for which data is required.
29
- template : dict of str to Any
30
- Template dictionary for the data source.
31
- param : str
32
- Parameter to retrieve.
33
-
34
- Returns
35
- -------
36
- Any
37
- Data retrieved from the source.
38
- """
39
- from warnings import warn
40
-
41
- warn(
42
- "The source `constants` is deprecated, use `forcings` instead.",
43
- DeprecationWarning,
44
- stacklevel=2,
45
- )
46
- context.trace("✅", f"from_source(constants, {template}, {param}")
47
- if len(template) == 0:
48
- raise ValueError("Forcings template is empty.")
49
-
50
- return from_source("forcings", source_or_dataset=template, date=dates, param=param)
51
-
52
-
53
- execute: Any = constants
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 .legacy import legacy_source
15
+ from . import source_registry
16
+ from .legacy import LegacySource
17
17
 
18
18
 
19
- @legacy_source(__file__)
20
- def execute(context: Any, dates: List[str], **kwargs: Any) -> ekd.FieldList:
21
- """Executes the loading of an empty data source.
19
+ @source_registry.register("empty")
20
+ class EmptySource(LegacySource):
22
21
 
23
- Parameters
24
- ----------
25
- context : object
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
- Returns
33
- -------
34
- ekd.FieldList
35
- Loaded empty data source.
36
- """
37
- return ekd.from_source("empty")
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 .legacy import legacy_source
16
-
17
-
18
- @legacy_source(__file__)
19
- def forcings(context: Any, dates: List[str], template: str, param: str) -> Any:
20
- """Loads forcing data from a specified source.
21
-
22
- Parameters
23
- ----------
24
- context : object
25
- The context in which the function is executed.
26
- dates : list
27
- List of dates for which data is to be loaded.
28
- template : FieldList
29
- Template for the data source.
30
- param : str
31
- Parameter for the data source.
32
-
33
- Returns
34
- -------
35
- object
36
- Loaded forcing data.
37
- """
38
- context.trace("✅", f"from_source(forcings, {template}, {param}")
39
- return from_source("forcings", source_or_dataset=template, date=dates, param=param)
40
-
41
-
42
- execute = forcings
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 .legacy import legacy_source
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: List[str], **kwargs: Any) -> None:
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: List[str]) -> Any:
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
- @legacy_source(__file__)
81
- def execute(
82
- context: Any,
83
- dates: List[Any],
84
- path: Union[str, List[str]],
85
- flavour: Optional[Union[str, Dict[str, Any]]] = None,
86
- grid_definition: Optional[Dict[str, Any]] = None,
87
- *args: Any,
88
- **kwargs: Any,
89
- ) -> ekd.FieldList:
90
- """Executes the function to load data from GRIB files.
91
-
92
- Parameters
93
- ----------
94
- context : Any
95
- The context in which the function is executed.
96
- dates : list of Any
97
- List of dates.
98
- path : str or list of str
99
- Path or list of paths to the GRIB files.
100
- flavour : str or dict of str to Any, optional
101
- Flavour information, by default None.
102
- grid_definition : dict of str to Any, optional
103
- Grid definition configuration to create a Grid object, by default None.
104
- *args : Any
105
- Additional positional arguments.
106
- **kwargs : Any
107
- Additional keyword arguments.
108
-
109
- Returns
110
- -------
111
- Any
112
- The loaded dataset.
113
- """
114
- given_paths = path if isinstance(path, list) else [path]
115
- if flavour is not None:
116
- flavour = RuleBasedFlavour(flavour)
117
-
118
- if grid_definition is not None:
119
- grid = grid_registry.from_config(grid_definition)
120
- else:
121
- grid = None
122
-
123
- ds = from_source("empty")
124
- dates = [d.isoformat() for d in dates]
125
-
126
- for path in given_paths:
127
- paths = Pattern(path).substitute(*args, date=dates, allow_extra=True, **kwargs)
128
-
129
- for name in ("grid", "area", "rotation", "frame", "resol", "bitmap"):
130
- if name in kwargs:
131
- raise ValueError(f"MARS interpolation parameter '{name}' not supported")
132
-
133
- for path in _expand(paths):
134
- context.trace("📁", "PATH", path)
135
- s = from_source("file", path)
136
- if flavour is not None:
137
- s = flavour.map(s)
138
- s = s.sel(valid_datetime=dates, **kwargs)
139
- ds = ds + s
140
-
141
- if kwargs and not context.partial_ok:
142
- check(ds, given_paths, valid_datetime=dates, **kwargs)
143
-
144
- if grid is not None:
145
- ds = new_fieldlist_from_list([new_field_from_grid(f, grid) for f in ds])
146
-
147
- if len(ds) == 0:
148
- LOG.warning(f"No fields found for {dates} in {given_paths} (kwargs={kwargs})")
149
-
150
- return ds
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