anemoi-datasets 0.5.25__py3-none-any.whl → 0.5.27__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/grib-index.py +1 -1
- anemoi/datasets/commands/inspect.py +27 -35
- anemoi/datasets/commands/validate.py +59 -0
- anemoi/datasets/compute/recentre.py +3 -6
- anemoi/datasets/create/__init__.py +22 -25
- anemoi/datasets/create/check.py +10 -12
- anemoi/datasets/create/chunks.py +1 -2
- anemoi/datasets/create/config.py +3 -6
- anemoi/datasets/create/filter.py +21 -24
- anemoi/datasets/create/input/__init__.py +1 -2
- anemoi/datasets/create/input/action.py +3 -5
- anemoi/datasets/create/input/concat.py +5 -8
- anemoi/datasets/create/input/context.py +3 -6
- anemoi/datasets/create/input/data_sources.py +5 -8
- anemoi/datasets/create/input/empty.py +1 -2
- anemoi/datasets/create/input/filter.py +2 -3
- anemoi/datasets/create/input/function.py +1 -2
- anemoi/datasets/create/input/join.py +4 -5
- anemoi/datasets/create/input/misc.py +4 -6
- anemoi/datasets/create/input/repeated_dates.py +13 -18
- anemoi/datasets/create/input/result.py +29 -33
- anemoi/datasets/create/input/step.py +6 -24
- anemoi/datasets/create/input/template.py +3 -4
- 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 +47 -52
- anemoi/datasets/create/sources/accumulations2.py +4 -8
- anemoi/datasets/create/sources/constants.py +1 -3
- anemoi/datasets/create/sources/empty.py +1 -2
- anemoi/datasets/create/sources/fdb.py +133 -0
- anemoi/datasets/create/sources/forcings.py +1 -2
- anemoi/datasets/create/sources/grib.py +6 -10
- anemoi/datasets/create/sources/grib_index.py +13 -15
- anemoi/datasets/create/sources/hindcasts.py +2 -5
- anemoi/datasets/create/sources/legacy.py +1 -1
- anemoi/datasets/create/sources/mars.py +17 -21
- anemoi/datasets/create/sources/netcdf.py +1 -2
- anemoi/datasets/create/sources/opendap.py +1 -3
- anemoi/datasets/create/sources/patterns.py +4 -6
- anemoi/datasets/create/sources/planetary_computer.py +44 -0
- anemoi/datasets/create/sources/recentre.py +8 -11
- anemoi/datasets/create/sources/source.py +3 -6
- anemoi/datasets/create/sources/tendencies.py +2 -5
- anemoi/datasets/create/sources/xarray.py +4 -6
- anemoi/datasets/create/sources/xarray_support/__init__.py +15 -32
- anemoi/datasets/create/sources/xarray_support/coordinates.py +16 -12
- anemoi/datasets/create/sources/xarray_support/field.py +17 -16
- anemoi/datasets/create/sources/xarray_support/fieldlist.py +11 -15
- anemoi/datasets/create/sources/xarray_support/flavour.py +83 -45
- 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 +47 -6
- anemoi/datasets/create/sources/xarray_support/time.py +10 -13
- anemoi/datasets/create/sources/xarray_support/variable.py +27 -23
- anemoi/datasets/create/sources/xarray_zarr.py +1 -2
- anemoi/datasets/create/sources/zenodo.py +3 -5
- anemoi/datasets/create/statistics/__init__.py +3 -6
- anemoi/datasets/create/testing.py +2 -74
- anemoi/datasets/create/typing.py +1 -2
- anemoi/datasets/create/utils.py +1 -2
- anemoi/datasets/create/zarr.py +7 -2
- anemoi/datasets/data/__init__.py +15 -6
- anemoi/datasets/data/complement.py +52 -23
- anemoi/datasets/data/concat.py +5 -8
- anemoi/datasets/data/dataset.py +42 -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 +30 -28
- anemoi/datasets/data/grids.py +12 -16
- 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/select.py +13 -16
- anemoi/datasets/data/statistics.py +4 -7
- anemoi/datasets/data/stores.py +23 -77
- 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 +13 -18
- anemoi/datasets/dates/groups.py +7 -10
- anemoi/datasets/grids.py +11 -12
- anemoi/datasets/testing.py +93 -7
- anemoi/datasets/validate.py +598 -0
- {anemoi_datasets-0.5.25.dist-info → anemoi_datasets-0.5.27.dist-info}/METADATA +5 -4
- anemoi_datasets-0.5.27.dist-info/RECORD +134 -0
- anemoi/datasets/create/filters/__init__.py +0 -33
- anemoi/datasets/create/filters/empty.py +0 -37
- anemoi/datasets/create/filters/legacy.py +0 -93
- anemoi/datasets/create/filters/noop.py +0 -37
- anemoi/datasets/create/filters/orog_to_z.py +0 -58
- anemoi/datasets/create/filters/pressure_level_relative_humidity_to_specific_humidity.py +0 -83
- anemoi/datasets/create/filters/pressure_level_specific_humidity_to_relative_humidity.py +0 -84
- anemoi/datasets/create/filters/rename.py +0 -205
- anemoi/datasets/create/filters/rotate_winds.py +0 -105
- anemoi/datasets/create/filters/single_level_dewpoint_to_relative_humidity.py +0 -78
- anemoi/datasets/create/filters/single_level_relative_humidity_to_dewpoint.py +0 -84
- anemoi/datasets/create/filters/single_level_relative_humidity_to_specific_humidity.py +0 -163
- anemoi/datasets/create/filters/single_level_specific_humidity_to_relative_humidity.py +0 -451
- anemoi/datasets/create/filters/speeddir_to_uv.py +0 -95
- anemoi/datasets/create/filters/sum.py +0 -68
- anemoi/datasets/create/filters/transform.py +0 -51
- anemoi/datasets/create/filters/unrotate_winds.py +0 -105
- anemoi/datasets/create/filters/uv_to_speeddir.py +0 -94
- anemoi/datasets/create/filters/wz_to_w.py +0 -98
- anemoi/datasets/utils/__init__.py +0 -8
- anemoi_datasets-0.5.25.dist-info/RECORD +0 -150
- {anemoi_datasets-0.5.25.dist-info → anemoi_datasets-0.5.27.dist-info}/WHEEL +0 -0
- {anemoi_datasets-0.5.25.dist-info → anemoi_datasets-0.5.27.dist-info}/entry_points.txt +0 -0
- {anemoi_datasets-0.5.25.dist-info → anemoi_datasets-0.5.27.dist-info}/licenses/LICENSE +0 -0
- {anemoi_datasets-0.5.25.dist-info → anemoi_datasets-0.5.27.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)
|