anemoi-datasets 0.5.25__py3-none-any.whl → 0.5.26__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 (41) hide show
  1. anemoi/datasets/_version.py +2 -2
  2. anemoi/datasets/commands/grib-index.py +1 -1
  3. anemoi/datasets/create/filter.py +22 -24
  4. anemoi/datasets/create/input/step.py +2 -16
  5. anemoi/datasets/create/sources/planetary_computer.py +44 -0
  6. anemoi/datasets/create/sources/xarray_support/__init__.py +6 -22
  7. anemoi/datasets/create/sources/xarray_support/coordinates.py +8 -0
  8. anemoi/datasets/create/sources/xarray_support/field.py +1 -4
  9. anemoi/datasets/create/sources/xarray_support/flavour.py +44 -6
  10. anemoi/datasets/create/sources/xarray_support/patch.py +44 -1
  11. anemoi/datasets/create/sources/xarray_support/variable.py +6 -2
  12. anemoi/datasets/data/complement.py +44 -10
  13. anemoi/datasets/data/forwards.py +8 -2
  14. anemoi/datasets/data/stores.py +7 -56
  15. anemoi/datasets/grids.py +6 -3
  16. {anemoi_datasets-0.5.25.dist-info → anemoi_datasets-0.5.26.dist-info}/METADATA +3 -2
  17. {anemoi_datasets-0.5.25.dist-info → anemoi_datasets-0.5.26.dist-info}/RECORD +21 -40
  18. anemoi/datasets/create/filters/__init__.py +0 -33
  19. anemoi/datasets/create/filters/empty.py +0 -37
  20. anemoi/datasets/create/filters/legacy.py +0 -93
  21. anemoi/datasets/create/filters/noop.py +0 -37
  22. anemoi/datasets/create/filters/orog_to_z.py +0 -58
  23. anemoi/datasets/create/filters/pressure_level_relative_humidity_to_specific_humidity.py +0 -83
  24. anemoi/datasets/create/filters/pressure_level_specific_humidity_to_relative_humidity.py +0 -84
  25. anemoi/datasets/create/filters/rename.py +0 -205
  26. anemoi/datasets/create/filters/rotate_winds.py +0 -105
  27. anemoi/datasets/create/filters/single_level_dewpoint_to_relative_humidity.py +0 -78
  28. anemoi/datasets/create/filters/single_level_relative_humidity_to_dewpoint.py +0 -84
  29. anemoi/datasets/create/filters/single_level_relative_humidity_to_specific_humidity.py +0 -163
  30. anemoi/datasets/create/filters/single_level_specific_humidity_to_relative_humidity.py +0 -451
  31. anemoi/datasets/create/filters/speeddir_to_uv.py +0 -95
  32. anemoi/datasets/create/filters/sum.py +0 -68
  33. anemoi/datasets/create/filters/transform.py +0 -51
  34. anemoi/datasets/create/filters/unrotate_winds.py +0 -105
  35. anemoi/datasets/create/filters/uv_to_speeddir.py +0 -94
  36. anemoi/datasets/create/filters/wz_to_w.py +0 -98
  37. anemoi/datasets/create/testing.py +0 -76
  38. {anemoi_datasets-0.5.25.dist-info → anemoi_datasets-0.5.26.dist-info}/WHEEL +0 -0
  39. {anemoi_datasets-0.5.25.dist-info → anemoi_datasets-0.5.26.dist-info}/entry_points.txt +0 -0
  40. {anemoi_datasets-0.5.25.dist-info → anemoi_datasets-0.5.26.dist-info}/licenses/LICENSE +0 -0
  41. {anemoi_datasets-0.5.25.dist-info → anemoi_datasets-0.5.26.dist-info}/top_level.txt +0 -0
@@ -1,205 +0,0 @@
1
- # (C) Copyright 2024 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 re
11
- from typing import Any
12
- from typing import Dict
13
- from typing import Optional
14
-
15
- import earthkit.data as ekd
16
- from earthkit.data.indexing.fieldlist import FieldArray
17
-
18
- from .legacy import legacy_filter
19
-
20
-
21
- class RenamedFieldMapping:
22
- """Rename a field based on the value of another field.
23
-
24
- Parameters
25
- ----------
26
- field : Any
27
- The field to be renamed.
28
- what : str
29
- The name of the field that will be used to rename the field.
30
- renaming : dict of dict of str
31
- A dictionary mapping the values of 'what' to the new names.
32
- """
33
-
34
- def __init__(self, field: Any, what: str, renaming: Dict[str, Dict[str, str]]) -> None:
35
- """Initialize a RenamedFieldMapping instance.
36
-
37
- Parameters
38
- ----------
39
- field : Any
40
- The field to be renamed.
41
- what : str
42
- The name of the field that will be used to rename the field.
43
- renaming : dict of dict of str
44
- A dictionary mapping the values of 'what' to the new names.
45
- """
46
- self.field = field
47
- self.what = what
48
- self.renaming = {}
49
- for k, v in renaming.items():
50
- self.renaming[k] = {str(a): str(b) for a, b in v.items()}
51
-
52
- def metadata(self, key: Optional[str] = None, **kwargs: Any) -> Any:
53
- """Get metadata from the original field, with the option to rename the parameter.
54
-
55
- Parameters
56
- ----------
57
- key : str, optional
58
- The metadata key.
59
- **kwargs : Any
60
- Additional keyword arguments.
61
-
62
- Returns
63
- -------
64
- Any
65
- The metadata value.
66
- """
67
- if key is None:
68
- return self.field.metadata(**kwargs)
69
-
70
- value = self.field.metadata(key, **kwargs)
71
- if key == self.what:
72
- return self.renaming.get(self.what, {}).get(value, value)
73
-
74
- return value
75
-
76
- def __getattr__(self, name: str) -> Any:
77
- """Get an attribute from the original field.
78
-
79
- Parameters
80
- ----------
81
- name : str
82
- The name of the attribute.
83
-
84
- Returns
85
- -------
86
- Any
87
- The attribute value.
88
- """
89
- return getattr(self.field, name)
90
-
91
- def __repr__(self) -> str:
92
- """Get the string representation of the original field.
93
-
94
- Returns
95
- -------
96
- str
97
- The string representation of the original field.
98
- """
99
- return repr(self.field)
100
-
101
-
102
- class RenamedFieldFormat:
103
- """Rename a field based on a format string.
104
-
105
- Parameters
106
- ----------
107
- field : Any
108
- The field to be renamed.
109
- what : str
110
- The name of the field that will be used to rename the field.
111
- format : str
112
- The format string for renaming.
113
- """
114
-
115
- def __init__(self, field: Any, what: str, format: str) -> None:
116
- """Initialize a RenamedFieldFormat instance.
117
-
118
- Parameters
119
- ----------
120
- field : Any
121
- The field to be renamed.
122
- what : str
123
- The name of the field that will be used to rename the field.
124
- format : str
125
- The format string for renaming.
126
- """
127
- self.field = field
128
- self.what = what
129
- self.format = format
130
- self.bits = re.findall(r"{(\w+)}", format)
131
-
132
- def metadata(self, *args: Any, **kwargs: Any) -> Any:
133
- """Get metadata from the original field, with the option to rename the parameter using a format string.
134
-
135
- Parameters
136
- ----------
137
- *args : Any
138
- Positional arguments.
139
- **kwargs : Any
140
- Additional keyword arguments.
141
-
142
- Returns
143
- -------
144
- Any
145
- The metadata value.
146
- """
147
- value = self.field.metadata(*args, **kwargs)
148
- if args:
149
- assert len(args) == 1
150
- if args[0] == self.what:
151
- bits = {b: self.field.metadata(b, **kwargs) for b in self.bits}
152
- return self.format.format(**bits)
153
- return value
154
-
155
- def __getattr__(self, name: str) -> Any:
156
- """Get an attribute from the original field.
157
-
158
- Parameters
159
- ----------
160
- name : str
161
- The name of the attribute.
162
-
163
- Returns
164
- -------
165
- Any
166
- The attribute value.
167
- """
168
- return getattr(self.field, name)
169
-
170
- def __repr__(self) -> str:
171
- """Get the string representation of the original field.
172
-
173
- Returns
174
- -------
175
- str
176
- The string representation of the original field.
177
- """
178
- return repr(self.field)
179
-
180
-
181
- @legacy_filter(__file__)
182
- def execute(context: Any, input: ekd.FieldList, what: str = "param", **kwargs: Any) -> ekd.FieldList:
183
- """Rename fields based on the value of another field or a format string.
184
-
185
- Parameters
186
- ----------
187
- context : Any
188
- The context in which the function is executed.
189
- input : List[Any]
190
- List of input fields.
191
- what : str, optional
192
- The field to be used for renaming. Defaults to "param".
193
- **kwargs : Any
194
- Additional keyword arguments for renaming.
195
-
196
- Returns
197
- -------
198
- ekd.FieldList
199
- Array of renamed fields.
200
- """
201
-
202
- if what in kwargs and isinstance(kwargs[what], str):
203
- return FieldArray([RenamedFieldFormat(fs, what, kwargs[what]) for fs in input])
204
-
205
- return FieldArray([RenamedFieldMapping(fs, what, kwargs) for fs in input])
@@ -1,105 +0,0 @@
1
- # (C) Copyright 2024 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
-
11
- from collections import defaultdict
12
- from typing import Any
13
- from typing import Dict
14
- from typing import Optional
15
-
16
- import earthkit.data as ekd
17
- import tqdm
18
- from anemoi.transform.fields import new_field_from_numpy
19
- from anemoi.transform.fields import new_fieldlist_from_list
20
- from anemoi.utils.humanize import plural
21
- from earthkit.geo.rotate import rotate_vector
22
-
23
- from .legacy import legacy_filter
24
-
25
-
26
- @legacy_filter(__file__)
27
- def execute(
28
- context: Any,
29
- input: ekd.FieldList,
30
- x_wind: str,
31
- y_wind: str,
32
- source_projection: Optional[str] = None,
33
- target_projection: str = "+proj=longlat",
34
- ) -> ekd.FieldList:
35
- """Rotate wind components from one projection to another.
36
-
37
- Parameters
38
- ----------
39
- context : Any
40
- The context in which the function is executed.
41
- input : ekd.FieldList
42
- List of input fields.
43
- x_wind : str
44
- X wind component parameter.
45
- y_wind : str
46
- Y wind component parameter.
47
- source_projection : Optional[str], optional
48
- Source projection, by default None.
49
- target_projection : str, optional
50
- Target projection, by default "+proj=longlat".
51
-
52
- Returns
53
- -------
54
- ekd.FieldList
55
- Array of fields with rotated wind components.
56
- """
57
- from pyproj import CRS
58
-
59
- context.trace("🔄", "Rotating winds (extracting winds from ", plural(len(input), "field"))
60
-
61
- result = []
62
-
63
- wind_params: tuple[str, str] = (x_wind, y_wind)
64
- wind_pairs: Dict[tuple, Dict[str, Any]] = defaultdict(dict)
65
-
66
- for f in input:
67
- key = f.metadata(namespace="mars")
68
- param = key.pop("param")
69
-
70
- if param not in wind_params:
71
- result.append(f)
72
- continue
73
-
74
- key = tuple(key.items())
75
-
76
- if param in wind_pairs[key]:
77
- raise ValueError(f"Duplicate wind component {param} for {key}")
78
-
79
- wind_pairs[key][param] = f
80
-
81
- context.trace("🔄", "Rotating", plural(len(wind_pairs), "wind"), "(speed will likely include data download)")
82
-
83
- for _, pairs in tqdm.tqdm(list(wind_pairs.items())):
84
- if len(pairs) != 2:
85
- raise ValueError("Missing wind component")
86
-
87
- x = pairs[x_wind]
88
- y = pairs[y_wind]
89
-
90
- assert x.grid_mapping == y.grid_mapping
91
-
92
- lats, lons = x.grid_points()
93
- x_new, y_new = rotate_vector(
94
- lats,
95
- lons,
96
- x.to_numpy(flatten=True),
97
- y.to_numpy(flatten=True),
98
- (source_projection if source_projection is not None else CRS.from_cf(x.grid_mapping)),
99
- target_projection,
100
- )
101
-
102
- result.append(new_field_from_numpy(x, x_new))
103
- result.append(new_field_from_numpy(y, y_new))
104
-
105
- return new_fieldlist_from_list(result)
@@ -1,78 +0,0 @@
1
- # (C) Copyright 2024 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
-
11
- from collections import defaultdict
12
- from typing import Any
13
- from typing import Dict
14
-
15
- import earthkit.data as ekd
16
- from anemoi.transform.fields import new_field_from_numpy
17
- from anemoi.transform.fields import new_fieldlist_from_list
18
- from earthkit.meteo import thermo
19
-
20
- from .legacy import legacy_filter
21
-
22
-
23
- @legacy_filter(__file__)
24
- def execute(context: Any, input: ekd.FieldList, t: str, td: str, rh: str = "d") -> ekd.FieldList:
25
- """Convert dewpoint on single levels to relative humidity.
26
-
27
- Parameters
28
- ----------
29
- context : Any
30
- The context in which the function is executed.
31
- input : List[Any]
32
- List of input fields.
33
- t : str
34
- Temperature parameter.
35
- td : str
36
- Dewpoint parameter.
37
- rh : str, optional
38
- Relative humidity parameter. Defaults to "d".
39
-
40
- Returns
41
- -------
42
- ekd.FieldList
43
- Array of fields with relative humidity.
44
- """
45
- result = []
46
- params: tuple[str, str] = (t, td)
47
- pairs: Dict[tuple, Dict[str, Any]] = defaultdict(dict)
48
-
49
- # Gather all necessary fields
50
- for f in input:
51
- key = f.metadata(namespace="mars")
52
- param = key.pop("param")
53
- if param in params:
54
- key = tuple(key.items())
55
-
56
- if param in pairs[key]:
57
- raise ValueError(f"Duplicate field {param} for {key}")
58
-
59
- pairs[key][param] = f
60
- if param == t:
61
- result.append(f)
62
- # all other parameters
63
- else:
64
- result.append(f)
65
-
66
- for keys, values in pairs.items():
67
- # some checks
68
-
69
- if len(values) != 2:
70
- raise ValueError("Missing fields")
71
-
72
- t_values = values[t].to_numpy(flatten=True)
73
- td_values = values[td].to_numpy(flatten=True)
74
- # actual conversion from td --> rh
75
- rh_values = thermo.relative_humidity_from_dewpoint(t=t_values, td=td_values)
76
- result.append(new_field_from_numpy(values[td], rh_values, param=rh))
77
-
78
- return new_fieldlist_from_list(result)
@@ -1,84 +0,0 @@
1
- # (C) Copyright 2024 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 collections import defaultdict
11
- from typing import Any
12
- from typing import Dict
13
- from typing import Tuple
14
-
15
- import earthkit.data as ekd
16
- from anemoi.transform.fields import new_field_from_numpy
17
- from anemoi.transform.fields import new_fieldlist_from_list
18
- from earthkit.data.indexing.fieldlist import FieldArray
19
- from earthkit.meteo import thermo
20
-
21
- from .legacy import legacy_filter
22
-
23
- EPS = 1.0e-4
24
-
25
-
26
- @legacy_filter(__file__)
27
- def execute(context: Any, input: ekd.FieldList, t: str, rh: str, td: str = "d") -> FieldArray:
28
- """Convert relative humidity on single levels to dewpoint.
29
-
30
- Parameters
31
- ----------
32
- context : Any
33
- The context in which the function is executed.
34
- input : List[Any]
35
- List of input fields.
36
- t : str
37
- Temperature parameter.
38
- rh : str
39
- Relative humidity parameter.
40
- td : str, optional
41
- Dewpoint parameter. Defaults to "d".
42
-
43
- Returns
44
- -------
45
- FieldArray
46
- Array of fields with dewpoint.
47
- """
48
- result = []
49
- params: Tuple[str, str] = (t, rh)
50
- pairs: Dict[Tuple[Any, ...], Dict[str, Any]] = defaultdict(dict)
51
-
52
- # Gather all necessary fields
53
- for f in input:
54
- key = f.metadata(namespace="mars")
55
- param = key.pop("param")
56
- if param in params:
57
- key = tuple(key.items())
58
-
59
- if param in pairs[key]:
60
- raise ValueError(f"Duplicate field {param} for {key}")
61
-
62
- pairs[key][param] = f
63
- if param == t:
64
- result.append(f)
65
- # all other parameters
66
- else:
67
- result.append(f)
68
-
69
- for keys, values in pairs.items():
70
- # some checks
71
-
72
- if len(values) != 2:
73
- raise ValueError("Missing fields")
74
-
75
- t_values = values[t].to_numpy(flatten=True)
76
- rh_values = values[rh].to_numpy(flatten=True)
77
- # Prevent 0 % Relative humidity which cannot be converted to dewpoint
78
- # Seems to happen over Egypt in the CERRA dataset
79
- rh_values[rh_values == 0] = EPS
80
- # actual conversion from rh --> td
81
- td_values = thermo.dewpoint_from_relative_humidity(t=t_values, r=rh_values)
82
- result.append(new_field_from_numpy(values[rh], td_values, param=td))
83
-
84
- return new_fieldlist_from_list(result)
@@ -1,163 +0,0 @@
1
- # (C) Copyright 2024 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
-
11
- from typing import Any
12
- from typing import Dict
13
- from typing import List
14
-
15
- import earthkit.data as ekd
16
- import numpy as np
17
- from anemoi.transform.fields import new_field_from_numpy
18
- from anemoi.transform.fields import new_fieldlist_from_list
19
- from earthkit.meteo import thermo
20
-
21
- from .legacy import legacy_filter
22
- from .single_level_specific_humidity_to_relative_humidity import AutoDict
23
- from .single_level_specific_humidity_to_relative_humidity import pressure_at_height_level
24
-
25
-
26
- @legacy_filter(__file__)
27
- def execute(
28
- context: Any,
29
- input: List[Any],
30
- height: float,
31
- t: str,
32
- rh: str,
33
- sp: str,
34
- new_name: str = "2q",
35
- **kwargs: Dict[str, Any],
36
- ) -> ekd.FieldList:
37
- """Convert the single (height) level relative humidity to specific humidity.
38
-
39
- Parameters
40
- ----------
41
- context : Any
42
- The context in which the function is executed.
43
- input : List[Any]
44
- List of input fields.
45
- height : float
46
- The height level.
47
- t : str
48
- Temperature parameter name.
49
- rh : str
50
- Relative humidity parameter name.
51
- sp : str
52
- Surface pressure parameter name.
53
- new_name : str, optional
54
- The new name for the specific humidity field, by default "2q".
55
- **kwargs : Dict[str, Any]
56
- Additional keyword arguments.
57
-
58
- Returns
59
- -------
60
- ekd.FieldList
61
- The resulting field list with specific humidity fields.
62
-
63
- Raises
64
- ------
65
- KeyError
66
- If mandatory keys are missing.
67
- ValueError
68
- If there are duplicate fields or missing fields.
69
- """
70
- result = []
71
-
72
- MANDATORY_KEYS = ["A", "B"]
73
- OPTIONAL_KEYS = ["t_ml", "q_ml"]
74
- MISSING_KEYS = []
75
- DEFAULTS = dict(t_ml="t", q_ml="q")
76
-
77
- for key in OPTIONAL_KEYS:
78
- if key not in kwargs:
79
- print(f"key {key} not found in yaml-file, using default key: {DEFAULTS[key]}")
80
- kwargs[key] = DEFAULTS[key]
81
-
82
- for key in MANDATORY_KEYS:
83
- if key not in kwargs:
84
- MISSING_KEYS.append(key)
85
-
86
- if MISSING_KEYS:
87
- raise KeyError(f"Following keys are missing: {', '.join(MISSING_KEYS)}")
88
-
89
- single_level_params = (t, rh, sp)
90
- model_level_params = (kwargs["t_ml"], kwargs["q_ml"])
91
-
92
- needed_fields = AutoDict()
93
-
94
- # Gather all necessary fields
95
- for f in input:
96
- key = f.metadata(namespace="mars")
97
- param = key.pop("param")
98
- # check single level parameters
99
- if param in single_level_params:
100
- levtype = key.pop("levtype")
101
- key = tuple(key.items())
102
-
103
- if param in needed_fields[key][levtype]:
104
- raise ValueError(f"Duplicate single level field {param} for {key}")
105
-
106
- needed_fields[key][levtype][param] = f
107
- if param == rh:
108
- if kwargs.get("keep_rh", False):
109
- result.append(f)
110
- else:
111
- result.append(f)
112
-
113
- # check model level parameters
114
- elif param in model_level_params:
115
- levtype = key.pop("levtype")
116
- levelist = key.pop("levelist")
117
- key = tuple(key.items())
118
-
119
- if param in needed_fields[key][levtype][levelist]:
120
- raise ValueError(f"Duplicate model level field {param} for {key} at level {levelist}")
121
-
122
- needed_fields[key][levtype][levelist][param] = f
123
-
124
- # all other parameters
125
- else:
126
- result.append(f)
127
-
128
- for _, values in needed_fields.items():
129
- # some checks
130
- if len(values["sfc"]) != 3:
131
- raise ValueError("Missing surface fields")
132
-
133
- rh_sl = values["sfc"][rh].to_numpy(flatten=True)
134
- t_sl = values["sfc"][t].to_numpy(flatten=True)
135
- sp_sl = values["sfc"][sp].to_numpy(flatten=True)
136
-
137
- nlevels = len(kwargs["A"]) - 1
138
- if len(values["ml"]) != nlevels:
139
- raise ValueError("Missing model levels")
140
-
141
- for key in values["ml"].keys():
142
- if len(values["ml"][key]) != 2:
143
- raise ValueError(f"Missing field on level {key}")
144
-
145
- # create 3D arrays for upper air fields
146
- levels = list(values["ml"].keys())
147
- levels.sort()
148
- t_ml = []
149
- q_ml = []
150
- for level in levels:
151
- t_ml.append(values["ml"][level][kwargs["t_ml"]].to_numpy(flatten=True))
152
- q_ml.append(values["ml"][level][kwargs["q_ml"]].to_numpy(flatten=True))
153
-
154
- t_ml = np.stack(t_ml)
155
- q_ml = np.stack(q_ml)
156
-
157
- # actual conversion from rh --> q_v
158
- p_sl = pressure_at_height_level(height, q_ml, t_ml, sp_sl, np.array(kwargs["A"]), np.array(kwargs["B"]))
159
- q_sl = thermo.specific_humidity_from_relative_humidity(t_sl, rh_sl, p_sl)
160
-
161
- result.append(new_field_from_numpy(values["sfc"][rh], q_sl, param=new_name))
162
-
163
- return new_fieldlist_from_list(result)