anemoi-datasets 0.5.12__py3-none-any.whl → 0.5.13__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- anemoi/datasets/_version.py +2 -2
- anemoi/datasets/create/__init__.py +8 -4
- anemoi/datasets/create/check.py +1 -1
- anemoi/datasets/create/functions/__init__.py +15 -1
- anemoi/datasets/create/functions/filters/orog_to_z.py +58 -0
- anemoi/datasets/create/functions/filters/sum.py +71 -0
- anemoi/datasets/create/functions/filters/wz_to_w.py +79 -0
- anemoi/datasets/create/functions/sources/accumulations.py +1 -0
- anemoi/datasets/create/functions/sources/xarray/__init__.py +3 -3
- anemoi/datasets/create/functions/sources/xarray/field.py +5 -1
- anemoi/datasets/create/functions/sources/xarray/fieldlist.py +10 -1
- anemoi/datasets/create/functions/sources/xarray/metadata.py +5 -11
- anemoi/datasets/create/functions/sources/xarray/patch.py +44 -0
- anemoi/datasets/create/functions/sources/xarray/time.py +15 -0
- anemoi/datasets/create/functions/sources/xarray/variable.py +18 -2
- anemoi/datasets/create/input/repeated_dates.py +18 -0
- anemoi/datasets/create/statistics/__init__.py +2 -2
- anemoi/datasets/create/utils.py +4 -0
- anemoi/datasets/data/complement.py +164 -0
- anemoi/datasets/data/dataset.py +68 -5
- anemoi/datasets/data/ensemble.py +55 -0
- anemoi/datasets/data/join.py +1 -2
- anemoi/datasets/data/merge.py +3 -0
- anemoi/datasets/data/misc.py +10 -1
- anemoi/datasets/grids.py +23 -10
- {anemoi_datasets-0.5.12.dist-info → anemoi_datasets-0.5.13.dist-info}/METADATA +2 -2
- {anemoi_datasets-0.5.12.dist-info → anemoi_datasets-0.5.13.dist-info}/RECORD +31 -26
- {anemoi_datasets-0.5.12.dist-info → anemoi_datasets-0.5.13.dist-info}/WHEEL +1 -1
- {anemoi_datasets-0.5.12.dist-info → anemoi_datasets-0.5.13.dist-info}/LICENSE +0 -0
- {anemoi_datasets-0.5.12.dist-info → anemoi_datasets-0.5.13.dist-info}/entry_points.txt +0 -0
- {anemoi_datasets-0.5.12.dist-info → anemoi_datasets-0.5.13.dist-info}/top_level.txt +0 -0
anemoi/datasets/_version.py
CHANGED
|
@@ -622,10 +622,14 @@ class Load(Actor, HasRegistryMixin, HasStatisticTempMixin, HasElementForDataMixi
|
|
|
622
622
|
|
|
623
623
|
check_shape(cube, dates, dates_in_data)
|
|
624
624
|
|
|
625
|
-
def check_dates_in_data(
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
assert
|
|
625
|
+
def check_dates_in_data(dates_in_data, requested_dates):
|
|
626
|
+
requested_dates = [np.datetime64(_) for _ in requested_dates]
|
|
627
|
+
dates_in_data = [np.datetime64(_) for _ in dates_in_data]
|
|
628
|
+
assert dates_in_data == requested_dates, (
|
|
629
|
+
"Dates in data are not the requested ones:",
|
|
630
|
+
dates_in_data,
|
|
631
|
+
requested_dates,
|
|
632
|
+
)
|
|
629
633
|
|
|
630
634
|
check_dates_in_data(dates_in_data, dates)
|
|
631
635
|
|
anemoi/datasets/create/check.py
CHANGED
|
@@ -58,7 +58,7 @@ class DatasetName:
|
|
|
58
58
|
raise ValueError(self.error_message)
|
|
59
59
|
|
|
60
60
|
def _parse(self, name):
|
|
61
|
-
pattern = r"^(\w+)-([\w-]+)-(\w+)-(\w+)-(\d\d\d\d)-(\d\d\d\d)-(\d+h)-v(\d+)-?([a-zA-Z0-9-]+)?$"
|
|
61
|
+
pattern = r"^(\w+)-([\w-]+)-(\w+)-(\w+)-(\d\d\d\d)-(\d\d\d\d)-(\d+h|\d+m)-v(\d+)-?([a-zA-Z0-9-]+)?$"
|
|
62
62
|
match = re.match(pattern, name)
|
|
63
63
|
|
|
64
64
|
if not match:
|
|
@@ -22,6 +22,7 @@ def assert_is_fieldlist(obj):
|
|
|
22
22
|
def import_function(name, kind):
|
|
23
23
|
|
|
24
24
|
from anemoi.transform.filters import filter_registry
|
|
25
|
+
from anemoi.transform.sources import source_registry
|
|
25
26
|
|
|
26
27
|
name = name.replace("-", "_")
|
|
27
28
|
|
|
@@ -45,7 +46,20 @@ def import_function(name, kind):
|
|
|
45
46
|
if filter_registry.lookup(name, return_none=True):
|
|
46
47
|
|
|
47
48
|
def proc(context, data, *args, **kwargs):
|
|
48
|
-
|
|
49
|
+
filter = filter_registry.create(name, *args, **kwargs)
|
|
50
|
+
filter.context = context
|
|
51
|
+
# filter = filter_registry.create(context, name, *args, **kwargs)
|
|
52
|
+
return filter.forward(data)
|
|
53
|
+
|
|
54
|
+
return proc
|
|
55
|
+
|
|
56
|
+
if kind == "sources":
|
|
57
|
+
if source_registry.lookup(name, return_none=True):
|
|
58
|
+
|
|
59
|
+
def proc(context, data, *args, **kwargs):
|
|
60
|
+
source = source_registry.create(name, *args, **kwargs)
|
|
61
|
+
# source = source_registry.create(context, name, *args, **kwargs)
|
|
62
|
+
return source.forward(data)
|
|
49
63
|
|
|
50
64
|
return proc
|
|
51
65
|
|
|
@@ -0,0 +1,58 @@
|
|
|
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
|
+
|
|
13
|
+
from earthkit.data.indexing.fieldlist import FieldArray
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class NewDataField:
|
|
17
|
+
def __init__(self, field, data, new_name):
|
|
18
|
+
self.field = field
|
|
19
|
+
self.data = data
|
|
20
|
+
self.new_name = new_name
|
|
21
|
+
|
|
22
|
+
def to_numpy(self, *args, **kwargs):
|
|
23
|
+
return self.data
|
|
24
|
+
|
|
25
|
+
def metadata(self, key=None, **kwargs):
|
|
26
|
+
if key is None:
|
|
27
|
+
return self.field.metadata(**kwargs)
|
|
28
|
+
|
|
29
|
+
value = self.field.metadata(key, **kwargs)
|
|
30
|
+
if key == "param":
|
|
31
|
+
return self.new_name
|
|
32
|
+
return value
|
|
33
|
+
|
|
34
|
+
def __getattr__(self, name):
|
|
35
|
+
return getattr(self.field, name)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def execute(context, input, orog, z="z"):
|
|
39
|
+
"""Convert orography [m] to z (geopotential height)"""
|
|
40
|
+
result = FieldArray()
|
|
41
|
+
|
|
42
|
+
processed_fields = defaultdict(dict)
|
|
43
|
+
|
|
44
|
+
for f in input:
|
|
45
|
+
key = f.metadata(namespace="mars")
|
|
46
|
+
param = key.pop("param")
|
|
47
|
+
if param == orog:
|
|
48
|
+
key = tuple(key.items())
|
|
49
|
+
|
|
50
|
+
if param in processed_fields[key]:
|
|
51
|
+
raise ValueError(f"Duplicate field {param} for {key}")
|
|
52
|
+
|
|
53
|
+
output = f.to_numpy(flatten=True) * 9.80665
|
|
54
|
+
result.append(NewDataField(f, output, z))
|
|
55
|
+
else:
|
|
56
|
+
result.append(f)
|
|
57
|
+
|
|
58
|
+
return result
|
|
@@ -0,0 +1,71 @@
|
|
|
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
|
+
|
|
13
|
+
from earthkit.data.indexing.fieldlist import FieldArray
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class NewDataField:
|
|
17
|
+
def __init__(self, field, data, new_name):
|
|
18
|
+
self.field = field
|
|
19
|
+
self.data = data
|
|
20
|
+
self.new_name = new_name
|
|
21
|
+
|
|
22
|
+
def to_numpy(self, *args, **kwargs):
|
|
23
|
+
return self.data
|
|
24
|
+
|
|
25
|
+
def metadata(self, key=None, **kwargs):
|
|
26
|
+
if key is None:
|
|
27
|
+
return self.field.metadata(**kwargs)
|
|
28
|
+
|
|
29
|
+
value = self.field.metadata(key, **kwargs)
|
|
30
|
+
if key == "param":
|
|
31
|
+
return self.new_name
|
|
32
|
+
return value
|
|
33
|
+
|
|
34
|
+
def __getattr__(self, name):
|
|
35
|
+
return getattr(self.field, name)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def execute(context, input, params, output):
|
|
39
|
+
"""Computes the sum over a set of variables"""
|
|
40
|
+
result = FieldArray()
|
|
41
|
+
|
|
42
|
+
needed_fields = defaultdict(dict)
|
|
43
|
+
|
|
44
|
+
for f in input:
|
|
45
|
+
key = f.metadata(namespace="mars")
|
|
46
|
+
param = key.pop("param")
|
|
47
|
+
if param in params:
|
|
48
|
+
key = tuple(key.items())
|
|
49
|
+
|
|
50
|
+
if param in needed_fields[key]:
|
|
51
|
+
raise ValueError(f"Duplicate field {param} for {key}")
|
|
52
|
+
|
|
53
|
+
needed_fields[key][param] = f
|
|
54
|
+
else:
|
|
55
|
+
result.append(f)
|
|
56
|
+
|
|
57
|
+
for keys, values in needed_fields.items():
|
|
58
|
+
|
|
59
|
+
if len(values) != len(params):
|
|
60
|
+
raise ValueError("Missing fields")
|
|
61
|
+
|
|
62
|
+
s = None
|
|
63
|
+
for k, v in values.items():
|
|
64
|
+
c = v.to_numpy(flatten=True)
|
|
65
|
+
if s is None:
|
|
66
|
+
s = c
|
|
67
|
+
else:
|
|
68
|
+
s += c
|
|
69
|
+
result.append(NewDataField(values[list(values.keys())[0]], s, output))
|
|
70
|
+
|
|
71
|
+
return result
|
|
@@ -0,0 +1,79 @@
|
|
|
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
|
+
|
|
13
|
+
from earthkit.data.indexing.fieldlist import FieldArray
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class NewDataField:
|
|
17
|
+
def __init__(self, field, data, new_name):
|
|
18
|
+
self.field = field
|
|
19
|
+
self.data = data
|
|
20
|
+
self.new_name = new_name
|
|
21
|
+
|
|
22
|
+
def to_numpy(self, *args, **kwargs):
|
|
23
|
+
return self.data
|
|
24
|
+
|
|
25
|
+
def metadata(self, key=None, **kwargs):
|
|
26
|
+
if key is None:
|
|
27
|
+
return self.field.metadata(**kwargs)
|
|
28
|
+
|
|
29
|
+
value = self.field.metadata(key, **kwargs)
|
|
30
|
+
if key == "param":
|
|
31
|
+
return self.new_name
|
|
32
|
+
return value
|
|
33
|
+
|
|
34
|
+
def __getattr__(self, name):
|
|
35
|
+
return getattr(self.field, name)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def execute(context, input, wz, t, w="w"):
|
|
39
|
+
"""Convert geometric vertical velocity (m/s) to vertical velocity (Pa / s)"""
|
|
40
|
+
result = FieldArray()
|
|
41
|
+
|
|
42
|
+
params = (wz, t)
|
|
43
|
+
pairs = defaultdict(dict)
|
|
44
|
+
|
|
45
|
+
for f in input:
|
|
46
|
+
key = f.metadata(namespace="mars")
|
|
47
|
+
param = key.pop("param")
|
|
48
|
+
if param in params:
|
|
49
|
+
key = tuple(key.items())
|
|
50
|
+
|
|
51
|
+
if param in pairs[key]:
|
|
52
|
+
raise ValueError(f"Duplicate field {param} for {key}")
|
|
53
|
+
|
|
54
|
+
pairs[key][param] = f
|
|
55
|
+
if param == t:
|
|
56
|
+
result.append(f)
|
|
57
|
+
else:
|
|
58
|
+
result.append(f)
|
|
59
|
+
|
|
60
|
+
for keys, values in pairs.items():
|
|
61
|
+
|
|
62
|
+
if len(values) != 2:
|
|
63
|
+
raise ValueError("Missing fields")
|
|
64
|
+
|
|
65
|
+
wz_pl = values[wz].to_numpy(flatten=True)
|
|
66
|
+
t_pl = values[t].to_numpy(flatten=True)
|
|
67
|
+
pressure = keys[4][1] * 100 # TODO: REMOVE HARDCODED INDICES
|
|
68
|
+
|
|
69
|
+
w_pl = wz_to_w(wz_pl, t_pl, pressure)
|
|
70
|
+
result.append(NewDataField(values[wz], w_pl, w))
|
|
71
|
+
|
|
72
|
+
return result
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def wz_to_w(wz, t, pressure):
|
|
76
|
+
g = 9.81
|
|
77
|
+
Rd = 287.058
|
|
78
|
+
|
|
79
|
+
return -wz * g * pressure / (t * Rd)
|
|
@@ -379,6 +379,7 @@ def accumulations(context, dates, **request):
|
|
|
379
379
|
KWARGS = {
|
|
380
380
|
("od", "oper"): dict(patch=_scda),
|
|
381
381
|
("od", "elda"): dict(base_times=(6, 18)),
|
|
382
|
+
("od", "enfo"): dict(base_times=(0, 6, 12, 18)),
|
|
382
383
|
("ea", "oper"): dict(data_accumulation_period=1, base_times=(6, 18)),
|
|
383
384
|
("ea", "enda"): dict(data_accumulation_period=3, base_times=(6, 18)),
|
|
384
385
|
("rr", "oper"): dict(base_times=(0, 3, 6, 9, 12, 15, 18, 21)),
|
|
@@ -29,7 +29,7 @@ def check(what, ds, paths, **kwargs):
|
|
|
29
29
|
raise ValueError(f"Expected {count} fields, got {len(ds)} (kwargs={kwargs}, {what}s={paths})")
|
|
30
30
|
|
|
31
31
|
|
|
32
|
-
def load_one(emoji, context, dates, dataset, options={}, flavour=None, **kwargs):
|
|
32
|
+
def load_one(emoji, context, dates, dataset, *, options={}, flavour=None, patch=None, **kwargs):
|
|
33
33
|
import xarray as xr
|
|
34
34
|
|
|
35
35
|
"""
|
|
@@ -54,10 +54,10 @@ def load_one(emoji, context, dates, dataset, options={}, flavour=None, **kwargs)
|
|
|
54
54
|
else:
|
|
55
55
|
data = xr.open_dataset(dataset, **options)
|
|
56
56
|
|
|
57
|
-
fs = XarrayFieldList.from_xarray(data, flavour)
|
|
57
|
+
fs = XarrayFieldList.from_xarray(data, flavour=flavour, patch=patch)
|
|
58
58
|
|
|
59
59
|
if len(dates) == 0:
|
|
60
|
-
|
|
60
|
+
result = fs.sel(**kwargs)
|
|
61
61
|
else:
|
|
62
62
|
result = MultiFieldList([fs.sel(valid_datetime=date, **kwargs) for date in dates])
|
|
63
63
|
|
|
@@ -92,6 +92,10 @@ class XArrayField(Field):
|
|
|
92
92
|
def grid_points(self):
|
|
93
93
|
return self.owner.grid_points()
|
|
94
94
|
|
|
95
|
+
def to_latlon(self, flatten=True):
|
|
96
|
+
assert flatten
|
|
97
|
+
return dict(lat=self.latitudes, lon=self.longitudes)
|
|
98
|
+
|
|
95
99
|
@property
|
|
96
100
|
def resolution(self):
|
|
97
101
|
return None
|
|
@@ -120,6 +124,6 @@ class XArrayField(Field):
|
|
|
120
124
|
def __repr__(self):
|
|
121
125
|
return repr(self._metadata)
|
|
122
126
|
|
|
123
|
-
def _values(self):
|
|
127
|
+
def _values(self, dtype=None):
|
|
124
128
|
# we don't use .values as this will download the data
|
|
125
129
|
return self.selection
|
|
@@ -16,6 +16,7 @@ from earthkit.data.core.fieldlist import FieldList
|
|
|
16
16
|
|
|
17
17
|
from .field import EmptyFieldList
|
|
18
18
|
from .flavour import CoordinateGuesser
|
|
19
|
+
from .patch import patch_dataset
|
|
19
20
|
from .time import Time
|
|
20
21
|
from .variable import FilteredVariable
|
|
21
22
|
from .variable import Variable
|
|
@@ -49,7 +50,11 @@ class XarrayFieldList(FieldList):
|
|
|
49
50
|
raise IndexError(k)
|
|
50
51
|
|
|
51
52
|
@classmethod
|
|
52
|
-
def from_xarray(cls, ds, flavour=None):
|
|
53
|
+
def from_xarray(cls, ds, *, flavour=None, patch=None):
|
|
54
|
+
|
|
55
|
+
if patch is not None:
|
|
56
|
+
ds = patch_dataset(ds, patch)
|
|
57
|
+
|
|
53
58
|
variables = []
|
|
54
59
|
|
|
55
60
|
if isinstance(flavour, str):
|
|
@@ -83,6 +88,8 @@ class XarrayFieldList(FieldList):
|
|
|
83
88
|
_skip_attr(variable, "bounds")
|
|
84
89
|
_skip_attr(variable, "grid_mapping")
|
|
85
90
|
|
|
91
|
+
LOG.debug("Xarray data_vars: %s", ds.data_vars)
|
|
92
|
+
|
|
86
93
|
# Select only geographical variables
|
|
87
94
|
for name in ds.data_vars:
|
|
88
95
|
|
|
@@ -97,6 +104,7 @@ class XarrayFieldList(FieldList):
|
|
|
97
104
|
c = guess.guess(ds[coord], coord)
|
|
98
105
|
assert c, f"Could not guess coordinate for {coord}"
|
|
99
106
|
if coord not in variable.dims:
|
|
107
|
+
LOG.debug("%s: coord=%s (not a dimension): dims=%s", variable, coord, variable.dims)
|
|
100
108
|
c.is_dim = False
|
|
101
109
|
coordinates.append(c)
|
|
102
110
|
|
|
@@ -104,6 +112,7 @@ class XarrayFieldList(FieldList):
|
|
|
104
112
|
assert grid_coords <= 2
|
|
105
113
|
|
|
106
114
|
if grid_coords < 2:
|
|
115
|
+
LOG.debug("Skipping %s (not 2D): %s", variable, [(c, c.is_grid, c.is_dim) for c in coordinates])
|
|
107
116
|
continue
|
|
108
117
|
|
|
109
118
|
v = Variable(
|
|
@@ -24,6 +24,7 @@ class _MDMapping:
|
|
|
24
24
|
def __init__(self, variable):
|
|
25
25
|
self.variable = variable
|
|
26
26
|
self.time = variable.time
|
|
27
|
+
# Aliases
|
|
27
28
|
self.mapping = dict(param="variable")
|
|
28
29
|
for c in variable.coordinates:
|
|
29
30
|
for v in c.mars_names:
|
|
@@ -34,7 +35,6 @@ class _MDMapping:
|
|
|
34
35
|
return self.mapping.get(key, key)
|
|
35
36
|
|
|
36
37
|
def from_user(self, kwargs):
|
|
37
|
-
print("from_user", kwargs, self)
|
|
38
38
|
return {self._from_user(k): v for k, v in kwargs.items()}
|
|
39
39
|
|
|
40
40
|
def __repr__(self):
|
|
@@ -81,22 +81,16 @@ class XArrayMetadata(RawMetadata):
|
|
|
81
81
|
def _valid_datetime(self):
|
|
82
82
|
return self._get("valid_datetime")
|
|
83
83
|
|
|
84
|
-
def
|
|
84
|
+
def get(self, key, astype=None, **kwargs):
|
|
85
85
|
|
|
86
86
|
if key in self._d:
|
|
87
|
+
if astype is not None:
|
|
88
|
+
return astype(self._d[key])
|
|
87
89
|
return self._d[key]
|
|
88
90
|
|
|
89
|
-
if key.startswith("mars."):
|
|
90
|
-
key = key[5:]
|
|
91
|
-
if key not in self.MARS_KEYS:
|
|
92
|
-
if kwargs.get("raise_on_missing", False):
|
|
93
|
-
raise KeyError(f"Invalid key '{key}' in namespace='mars'")
|
|
94
|
-
else:
|
|
95
|
-
return kwargs.get("default", None)
|
|
96
|
-
|
|
97
91
|
key = self._mapping._from_user(key)
|
|
98
92
|
|
|
99
|
-
return super().
|
|
93
|
+
return super().get(key, astype=astype, **kwargs)
|
|
100
94
|
|
|
101
95
|
|
|
102
96
|
class XArrayFieldGeography(Geography):
|
|
@@ -0,0 +1,44 @@
|
|
|
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
|
+
import logging
|
|
12
|
+
|
|
13
|
+
LOG = logging.getLogger(__name__)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def patch_attributes(ds, attributes):
|
|
17
|
+
for name, value in attributes.items():
|
|
18
|
+
variable = ds[name]
|
|
19
|
+
variable.attrs.update(value)
|
|
20
|
+
|
|
21
|
+
return ds
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def patch_coordinates(ds, coordinates):
|
|
25
|
+
for name in coordinates:
|
|
26
|
+
ds = ds.assign_coords({name: ds[name]})
|
|
27
|
+
|
|
28
|
+
return ds
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
PATCHES = {
|
|
32
|
+
"attributes": patch_attributes,
|
|
33
|
+
"coordinates": patch_coordinates,
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def patch_dataset(ds, patch):
|
|
38
|
+
for what, values in patch.items():
|
|
39
|
+
if what not in PATCHES:
|
|
40
|
+
raise ValueError(f"Unknown patch type {what!r}")
|
|
41
|
+
|
|
42
|
+
ds = PATCHES[what](ds, values)
|
|
43
|
+
|
|
44
|
+
return ds
|
|
@@ -62,12 +62,18 @@ class Time:
|
|
|
62
62
|
|
|
63
63
|
raise NotImplementedError(f"{len(date_coordinate)=} {len(time_coordinate)=} {len(step_coordinate)=}")
|
|
64
64
|
|
|
65
|
+
def select_valid_datetime(self, variable):
|
|
66
|
+
raise NotImplementedError(f"{self.__class__.__name__}.select_valid_datetime()")
|
|
67
|
+
|
|
65
68
|
|
|
66
69
|
class Constant(Time):
|
|
67
70
|
|
|
68
71
|
def fill_time_metadata(self, coords_values, metadata):
|
|
69
72
|
return None
|
|
70
73
|
|
|
74
|
+
def select_valid_datetime(self, variable):
|
|
75
|
+
return None
|
|
76
|
+
|
|
71
77
|
|
|
72
78
|
class Analysis(Time):
|
|
73
79
|
|
|
@@ -83,6 +89,9 @@ class Analysis(Time):
|
|
|
83
89
|
|
|
84
90
|
return valid_datetime
|
|
85
91
|
|
|
92
|
+
def select_valid_datetime(self, variable):
|
|
93
|
+
return self.time_coordinate_name
|
|
94
|
+
|
|
86
95
|
|
|
87
96
|
class ForecastFromValidTimeAndStep(Time):
|
|
88
97
|
|
|
@@ -116,6 +125,9 @@ class ForecastFromValidTimeAndStep(Time):
|
|
|
116
125
|
|
|
117
126
|
return valid_datetime
|
|
118
127
|
|
|
128
|
+
def select_valid_datetime(self, variable):
|
|
129
|
+
return self.time_coordinate_name
|
|
130
|
+
|
|
119
131
|
|
|
120
132
|
class ForecastFromValidTimeAndBaseTime(Time):
|
|
121
133
|
|
|
@@ -138,6 +150,9 @@ class ForecastFromValidTimeAndBaseTime(Time):
|
|
|
138
150
|
|
|
139
151
|
return valid_datetime
|
|
140
152
|
|
|
153
|
+
def select_valid_datetime(self, variable):
|
|
154
|
+
return self.time_coordinate_name
|
|
155
|
+
|
|
141
156
|
|
|
142
157
|
class ForecastFromBaseTimeAndDate(Time):
|
|
143
158
|
|
|
@@ -37,7 +37,7 @@ class Variable:
|
|
|
37
37
|
self.coordinates = coordinates
|
|
38
38
|
|
|
39
39
|
self._metadata = metadata.copy()
|
|
40
|
-
self._metadata.update({"variable": variable.name})
|
|
40
|
+
self._metadata.update({"variable": variable.name, "param": variable.name})
|
|
41
41
|
|
|
42
42
|
self.time = time
|
|
43
43
|
|
|
@@ -45,6 +45,9 @@ class Variable:
|
|
|
45
45
|
self.names = {c.variable.name: c for c in coordinates if c.is_dim and not c.scalar and not c.is_grid}
|
|
46
46
|
self.by_name = {c.variable.name: c for c in coordinates}
|
|
47
47
|
|
|
48
|
+
# We need that alias for the time dimension
|
|
49
|
+
self._aliases = dict(valid_datetime="time")
|
|
50
|
+
|
|
48
51
|
self.length = math.prod(self.shape)
|
|
49
52
|
|
|
50
53
|
@property
|
|
@@ -96,15 +99,28 @@ class Variable:
|
|
|
96
99
|
|
|
97
100
|
k, v = kwargs.popitem()
|
|
98
101
|
|
|
102
|
+
user_provided_k = k
|
|
103
|
+
|
|
104
|
+
if k == "valid_datetime":
|
|
105
|
+
# Ask the Time object to select the valid datetime
|
|
106
|
+
k = self.time.select_valid_datetime(self)
|
|
107
|
+
if k is None:
|
|
108
|
+
return None
|
|
109
|
+
|
|
99
110
|
c = self.by_name.get(k)
|
|
100
111
|
|
|
112
|
+
# assert c is not None, f"Could not find coordinate {k} in {self.variable.name} {self.coordinates} {list(self.by_name)}"
|
|
113
|
+
|
|
101
114
|
if c is None:
|
|
102
115
|
missing[k] = v
|
|
103
116
|
return self.sel(missing, **kwargs)
|
|
104
117
|
|
|
105
118
|
i = c.index(v)
|
|
106
119
|
if i is None:
|
|
107
|
-
|
|
120
|
+
if k != user_provided_k:
|
|
121
|
+
LOG.warning(f"Could not find {user_provided_k}={v} in {c} (alias of {k})")
|
|
122
|
+
else:
|
|
123
|
+
LOG.warning(f"Could not find {k}={v} in {c}")
|
|
108
124
|
return None
|
|
109
125
|
|
|
110
126
|
coordinates = [x.reduced(i) if c is x else x for x in self.coordinates]
|
|
@@ -72,6 +72,11 @@ class DateMapperClosest(DateMapper):
|
|
|
72
72
|
end += self.frequency
|
|
73
73
|
|
|
74
74
|
to_try = sorted(to_try - self.tried)
|
|
75
|
+
info = {k: "no-data" for k in to_try}
|
|
76
|
+
|
|
77
|
+
if not to_try:
|
|
78
|
+
LOG.warning(f"No new dates to try for {group_of_dates} in {self.source}")
|
|
79
|
+
# return []
|
|
75
80
|
|
|
76
81
|
if to_try:
|
|
77
82
|
result = self.source.select(
|
|
@@ -82,19 +87,32 @@ class DateMapperClosest(DateMapper):
|
|
|
82
87
|
)
|
|
83
88
|
)
|
|
84
89
|
|
|
90
|
+
cnt = 0
|
|
85
91
|
for f in result.datasource:
|
|
92
|
+
cnt += 1
|
|
86
93
|
# We could keep the fields in a dictionary, but we don't want to keep the fields in memory
|
|
87
94
|
date = as_datetime(f.metadata("valid_datetime"))
|
|
88
95
|
|
|
89
96
|
if self.skip_all_nans:
|
|
90
97
|
if np.isnan(f.to_numpy()).all():
|
|
91
98
|
LOG.warning(f"Skipping {date} because all values are NaN")
|
|
99
|
+
info[date] = "all-nans"
|
|
92
100
|
continue
|
|
93
101
|
|
|
102
|
+
info[date] = "ok"
|
|
94
103
|
self.found.add(date)
|
|
95
104
|
|
|
105
|
+
if cnt == 0:
|
|
106
|
+
raise ValueError(f"No data found for {group_of_dates} in {self.source}")
|
|
107
|
+
|
|
96
108
|
self.tried.update(to_try)
|
|
97
109
|
|
|
110
|
+
if not self.found:
|
|
111
|
+
for k, v in info.items():
|
|
112
|
+
LOG.warning(f"{k}: {v}")
|
|
113
|
+
|
|
114
|
+
raise ValueError(f"No matching data found for {asked_dates} in {self.source}")
|
|
115
|
+
|
|
98
116
|
new_dates = defaultdict(list)
|
|
99
117
|
|
|
100
118
|
for date in asked_dates:
|
|
@@ -98,7 +98,7 @@ def fix_variance(x, name, count, sums, squares):
|
|
|
98
98
|
|
|
99
99
|
variances = squares / count - mean * mean
|
|
100
100
|
assert variances.shape == squares.shape == mean.shape
|
|
101
|
-
if all(variances >= 0):
|
|
101
|
+
if np.all(variances >= 0):
|
|
102
102
|
LOG.warning(f"All individual variances for {name} are positive, setting variance to 0.")
|
|
103
103
|
return 0
|
|
104
104
|
|
|
@@ -108,7 +108,7 @@ def fix_variance(x, name, count, sums, squares):
|
|
|
108
108
|
# return 0
|
|
109
109
|
|
|
110
110
|
LOG.warning(f"ERROR at least one individual variance is negative ({np.nanmin(variances)}).")
|
|
111
|
-
return
|
|
111
|
+
return 0
|
|
112
112
|
|
|
113
113
|
|
|
114
114
|
def check_variance(x, variables_names, minimum, maximum, mean, count, sums, squares):
|
anemoi/datasets/create/utils.py
CHANGED
|
@@ -54,6 +54,10 @@ def to_datetime(*args, **kwargs):
|
|
|
54
54
|
|
|
55
55
|
|
|
56
56
|
def make_list_int(value):
|
|
57
|
+
# Convert a string like "1/2/3" or "1/to/3" or "1/to/10/by/2" to a list of integers.
|
|
58
|
+
# Moved to anemoi.utils.humanize
|
|
59
|
+
# replace with from anemoi.utils.humanize import make_list_int
|
|
60
|
+
# when anemoi-utils is released and pyproject.toml is updated
|
|
57
61
|
if isinstance(value, str):
|
|
58
62
|
if "/" not in value:
|
|
59
63
|
return [value]
|
|
@@ -0,0 +1,164 @@
|
|
|
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
|
+
import logging
|
|
12
|
+
from functools import cached_property
|
|
13
|
+
|
|
14
|
+
from ..grids import nearest_grid_points
|
|
15
|
+
from .debug import Node
|
|
16
|
+
from .forwards import Combined
|
|
17
|
+
from .indexing import apply_index_to_slices_changes
|
|
18
|
+
from .indexing import index_to_slices
|
|
19
|
+
from .indexing import update_tuple
|
|
20
|
+
from .misc import _auto_adjust
|
|
21
|
+
from .misc import _open
|
|
22
|
+
|
|
23
|
+
LOG = logging.getLogger(__name__)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class Complement(Combined):
|
|
27
|
+
|
|
28
|
+
def __init__(self, target, source, what="variables", interpolation="nearest"):
|
|
29
|
+
super().__init__([target, source])
|
|
30
|
+
|
|
31
|
+
# We had the variables of dataset[1] to dataset[0]
|
|
32
|
+
# interpoated on the grid of dataset[0]
|
|
33
|
+
|
|
34
|
+
self.target = target
|
|
35
|
+
self.source = source
|
|
36
|
+
|
|
37
|
+
self._variables = []
|
|
38
|
+
|
|
39
|
+
# Keep the same order as the original dataset
|
|
40
|
+
for v in self.source.variables:
|
|
41
|
+
if v not in self.target.variables:
|
|
42
|
+
self._variables.append(v)
|
|
43
|
+
|
|
44
|
+
if not self._variables:
|
|
45
|
+
raise ValueError("Augment: no missing variables")
|
|
46
|
+
|
|
47
|
+
@property
|
|
48
|
+
def variables(self):
|
|
49
|
+
return self._variables
|
|
50
|
+
|
|
51
|
+
@property
|
|
52
|
+
def name_to_index(self):
|
|
53
|
+
return {v: i for i, v in enumerate(self.variables)}
|
|
54
|
+
|
|
55
|
+
@property
|
|
56
|
+
def shape(self):
|
|
57
|
+
shape = self.target.shape
|
|
58
|
+
return (shape[0], len(self._variables)) + shape[2:]
|
|
59
|
+
|
|
60
|
+
@property
|
|
61
|
+
def variables_metadata(self):
|
|
62
|
+
return {k: v for k, v in self.source.variables_metadata.items() if k in self._variables}
|
|
63
|
+
|
|
64
|
+
def check_same_variables(self, d1, d2):
|
|
65
|
+
pass
|
|
66
|
+
|
|
67
|
+
@cached_property
|
|
68
|
+
def missing(self):
|
|
69
|
+
missing = self.source.missing.copy()
|
|
70
|
+
missing = missing | self.target.missing
|
|
71
|
+
return set(missing)
|
|
72
|
+
|
|
73
|
+
def tree(self):
|
|
74
|
+
"""Generates a hierarchical tree structure for the `Cutout` instance and
|
|
75
|
+
its associated datasets.
|
|
76
|
+
|
|
77
|
+
Returns:
|
|
78
|
+
Node: A `Node` object representing the `Cutout` instance as the root
|
|
79
|
+
node, with each dataset in `self.datasets` represented as a child
|
|
80
|
+
node.
|
|
81
|
+
"""
|
|
82
|
+
return Node(self, [d.tree() for d in (self.target, self.source)])
|
|
83
|
+
|
|
84
|
+
def __getitem__(self, index):
|
|
85
|
+
if isinstance(index, (int, slice)):
|
|
86
|
+
index = (index, slice(None), slice(None), slice(None))
|
|
87
|
+
return self._get_tuple(index)
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
class ComplementNone(Complement):
|
|
91
|
+
|
|
92
|
+
def __init__(self, target, source):
|
|
93
|
+
super().__init__(target, source)
|
|
94
|
+
|
|
95
|
+
def _get_tuple(self, index):
|
|
96
|
+
index, changes = index_to_slices(index, self.shape)
|
|
97
|
+
result = self.source[index]
|
|
98
|
+
return apply_index_to_slices_changes(result, changes)
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
class ComplementNearest(Complement):
|
|
102
|
+
|
|
103
|
+
def __init__(self, target, source):
|
|
104
|
+
super().__init__(target, source)
|
|
105
|
+
|
|
106
|
+
self._nearest_grid_points = nearest_grid_points(
|
|
107
|
+
self.source.latitudes,
|
|
108
|
+
self.source.longitudes,
|
|
109
|
+
self.target.latitudes,
|
|
110
|
+
self.target.longitudes,
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
def check_compatibility(self, d1, d2):
|
|
114
|
+
pass
|
|
115
|
+
|
|
116
|
+
def _get_tuple(self, index):
|
|
117
|
+
variable_index = 1
|
|
118
|
+
index, changes = index_to_slices(index, self.shape)
|
|
119
|
+
index, previous = update_tuple(index, variable_index, slice(None))
|
|
120
|
+
source_index = [self.source.name_to_index[x] for x in self.variables[previous]]
|
|
121
|
+
source_data = self.source[index[0], source_index, index[2], ...]
|
|
122
|
+
target_data = source_data[..., self._nearest_grid_points]
|
|
123
|
+
|
|
124
|
+
result = target_data[..., index[3]]
|
|
125
|
+
|
|
126
|
+
return apply_index_to_slices_changes(result, changes)
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def complement_factory(args, kwargs):
|
|
130
|
+
from .select import Select
|
|
131
|
+
|
|
132
|
+
assert len(args) == 0, args
|
|
133
|
+
|
|
134
|
+
source = kwargs.pop("source")
|
|
135
|
+
target = kwargs.pop("complement")
|
|
136
|
+
what = kwargs.pop("what", "variables")
|
|
137
|
+
interpolation = kwargs.pop("interpolation", "none")
|
|
138
|
+
|
|
139
|
+
if what != "variables":
|
|
140
|
+
raise NotImplementedError(f"Complement what={what} not implemented")
|
|
141
|
+
|
|
142
|
+
if interpolation not in ("none", "nearest"):
|
|
143
|
+
raise NotImplementedError(f"Complement method={interpolation} not implemented")
|
|
144
|
+
|
|
145
|
+
source = _open(source)
|
|
146
|
+
target = _open(target)
|
|
147
|
+
# `select` is the same as `variables`
|
|
148
|
+
(source, target), kwargs = _auto_adjust([source, target], kwargs, exclude=["select"])
|
|
149
|
+
|
|
150
|
+
Class = {
|
|
151
|
+
None: ComplementNone,
|
|
152
|
+
"none": ComplementNone,
|
|
153
|
+
"nearest": ComplementNearest,
|
|
154
|
+
}[interpolation]
|
|
155
|
+
|
|
156
|
+
complement = Class(target=target, source=source)._subset(**kwargs)
|
|
157
|
+
|
|
158
|
+
# Will join the datasets along the variables axis
|
|
159
|
+
reorder = source.variables
|
|
160
|
+
complemented = _open([target, complement])
|
|
161
|
+
ordered = (
|
|
162
|
+
Select(complemented, complemented._reorder_to_columns(reorder), {"reoder": reorder})._subset(**kwargs).mutate()
|
|
163
|
+
)
|
|
164
|
+
return ordered
|
anemoi/datasets/data/dataset.py
CHANGED
|
@@ -168,6 +168,16 @@ class Dataset:
|
|
|
168
168
|
bbox = kwargs.pop("area")
|
|
169
169
|
return Cropping(self, bbox)._subset(**kwargs).mutate()
|
|
170
170
|
|
|
171
|
+
if "number" in kwargs or "numbers" or "member" in kwargs or "members" in kwargs:
|
|
172
|
+
from .ensemble import Number
|
|
173
|
+
|
|
174
|
+
members = {}
|
|
175
|
+
for key in ["number", "numbers", "member", "members"]:
|
|
176
|
+
if key in kwargs:
|
|
177
|
+
members[key] = kwargs.pop(key)
|
|
178
|
+
|
|
179
|
+
return Number(self, **members)._subset(**kwargs).mutate()
|
|
180
|
+
|
|
171
181
|
if "set_missing_dates" in kwargs:
|
|
172
182
|
from .missing import MissingDates
|
|
173
183
|
|
|
@@ -251,13 +261,19 @@ class Dataset:
|
|
|
251
261
|
return sorted([v for k, v in self.name_to_index.items() if k not in vars])
|
|
252
262
|
|
|
253
263
|
def _reorder_to_columns(self, vars):
|
|
264
|
+
if isinstance(vars, str) and vars == "sort":
|
|
265
|
+
# Sorting the variables alphabetically.
|
|
266
|
+
# This is cruical for pre-training then transfer learning in combination with
|
|
267
|
+
# cutout and adjust = 'all'
|
|
268
|
+
|
|
269
|
+
indices = [self.name_to_index[k] for k, v in sorted(self.name_to_index.items(), key=lambda x: x[0])]
|
|
270
|
+
assert set(indices) == set(range(len(self.name_to_index)))
|
|
271
|
+
return indices
|
|
272
|
+
|
|
254
273
|
if isinstance(vars, (list, tuple)):
|
|
255
274
|
vars = {k: i for i, k in enumerate(vars)}
|
|
256
275
|
|
|
257
|
-
indices = []
|
|
258
|
-
|
|
259
|
-
for k, v in sorted(vars.items(), key=lambda x: x[1]):
|
|
260
|
-
indices.append(self.name_to_index[k])
|
|
276
|
+
indices = [self.name_to_index[k] for k, v in sorted(vars.items(), key=lambda x: x[1])]
|
|
261
277
|
|
|
262
278
|
# Make sure we don't forget any variables
|
|
263
279
|
assert set(indices) == set(range(len(self.name_to_index)))
|
|
@@ -469,7 +485,7 @@ class Dataset:
|
|
|
469
485
|
sample_count = min(4, len(indices))
|
|
470
486
|
count = len(indices)
|
|
471
487
|
|
|
472
|
-
p = slice(0, count, count // (sample_count - 1))
|
|
488
|
+
p = slice(0, count, count // max(1, sample_count - 1))
|
|
473
489
|
samples = list(range(*p.indices(count)))
|
|
474
490
|
|
|
475
491
|
samples.append(count - 1) # Add last
|
|
@@ -502,3 +518,50 @@ class Dataset:
|
|
|
502
518
|
result.append(v)
|
|
503
519
|
|
|
504
520
|
return result
|
|
521
|
+
|
|
522
|
+
def plot(self, date, variable, member=0, **kwargs):
|
|
523
|
+
"""For debugging purposes, plot a field.
|
|
524
|
+
|
|
525
|
+
Parameters
|
|
526
|
+
----------
|
|
527
|
+
date : int or datetime.datetime or numpy.datetime64 or str
|
|
528
|
+
The date to plot.
|
|
529
|
+
variable : int or str
|
|
530
|
+
The variable to plot.
|
|
531
|
+
member : int, optional
|
|
532
|
+
The ensemble member to plot.
|
|
533
|
+
|
|
534
|
+
**kwargs:
|
|
535
|
+
Additional arguments to pass to matplotlib.pyplot.tricontourf
|
|
536
|
+
|
|
537
|
+
|
|
538
|
+
Returns
|
|
539
|
+
-------
|
|
540
|
+
matplotlib.pyplot.Axes
|
|
541
|
+
"""
|
|
542
|
+
|
|
543
|
+
from anemoi.utils.devtools import plot_values
|
|
544
|
+
from earthkit.data.utils.dates import to_datetime
|
|
545
|
+
|
|
546
|
+
if not isinstance(date, int):
|
|
547
|
+
date = np.datetime64(to_datetime(date)).astype(self.dates[0].dtype)
|
|
548
|
+
index = np.where(self.dates == date)[0]
|
|
549
|
+
if len(index) == 0:
|
|
550
|
+
raise ValueError(
|
|
551
|
+
f"Date {date} not found in the dataset {self.dates[0]} to {self.dates[-1]} by {self.frequency}"
|
|
552
|
+
)
|
|
553
|
+
date_index = index[0]
|
|
554
|
+
else:
|
|
555
|
+
date_index = date
|
|
556
|
+
|
|
557
|
+
if isinstance(variable, int):
|
|
558
|
+
variable_index = variable
|
|
559
|
+
else:
|
|
560
|
+
if variable not in self.variables:
|
|
561
|
+
raise ValueError(f"Unknown variable {variable} (available: {self.variables})")
|
|
562
|
+
|
|
563
|
+
variable_index = self.name_to_index[variable]
|
|
564
|
+
|
|
565
|
+
values = self[date_index, variable_index, member]
|
|
566
|
+
|
|
567
|
+
return plot_values(values, self.latitudes, self.longitudes, **kwargs)
|
anemoi/datasets/data/ensemble.py
CHANGED
|
@@ -10,13 +10,68 @@
|
|
|
10
10
|
|
|
11
11
|
import logging
|
|
12
12
|
|
|
13
|
+
import numpy as np
|
|
14
|
+
|
|
13
15
|
from .debug import Node
|
|
16
|
+
from .forwards import Forwards
|
|
14
17
|
from .forwards import GivenAxis
|
|
18
|
+
from .indexing import apply_index_to_slices_changes
|
|
19
|
+
from .indexing import index_to_slices
|
|
20
|
+
from .indexing import update_tuple
|
|
15
21
|
from .misc import _auto_adjust
|
|
16
22
|
from .misc import _open
|
|
17
23
|
|
|
18
24
|
LOG = logging.getLogger(__name__)
|
|
19
25
|
|
|
26
|
+
OFFSETS = dict(number=1, numbers=1, member=0, members=0)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class Number(Forwards):
|
|
30
|
+
def __init__(self, forward, **kwargs):
|
|
31
|
+
super().__init__(forward)
|
|
32
|
+
|
|
33
|
+
self.members = []
|
|
34
|
+
for key, values in kwargs.items():
|
|
35
|
+
if not isinstance(values, (list, tuple)):
|
|
36
|
+
values = [values]
|
|
37
|
+
self.members.extend([int(v) - OFFSETS[key] for v in values])
|
|
38
|
+
|
|
39
|
+
self.members = sorted(set(self.members))
|
|
40
|
+
for n in self.members:
|
|
41
|
+
if not (0 <= n < forward.shape[2]):
|
|
42
|
+
raise ValueError(f"Member {n} is out of range. `number(s)` is one-based, `member(s)` is zero-based.")
|
|
43
|
+
|
|
44
|
+
self.mask = np.array([n in self.members for n in range(forward.shape[2])], dtype=bool)
|
|
45
|
+
self._shape, _ = update_tuple(forward.shape, 2, len(self.members))
|
|
46
|
+
|
|
47
|
+
@property
|
|
48
|
+
def shape(self):
|
|
49
|
+
return self._shape
|
|
50
|
+
|
|
51
|
+
def __getitem__(self, index):
|
|
52
|
+
if isinstance(index, int):
|
|
53
|
+
result = self.forward[index]
|
|
54
|
+
result = result[:, self.mask, :]
|
|
55
|
+
return result
|
|
56
|
+
|
|
57
|
+
if isinstance(index, slice):
|
|
58
|
+
result = self.forward[index]
|
|
59
|
+
result = result[:, :, self.mask, :]
|
|
60
|
+
return result
|
|
61
|
+
|
|
62
|
+
index, changes = index_to_slices(index, self.shape)
|
|
63
|
+
result = self.forward[index]
|
|
64
|
+
result = result[:, :, self.mask, :]
|
|
65
|
+
return apply_index_to_slices_changes(result, changes)
|
|
66
|
+
|
|
67
|
+
def tree(self):
|
|
68
|
+
return Node(self, [self.forward.tree()], numbers=[n + 1 for n in self.members])
|
|
69
|
+
|
|
70
|
+
def metadata_specific(self):
|
|
71
|
+
return {
|
|
72
|
+
"numbers": [n + 1 for n in self.members],
|
|
73
|
+
}
|
|
74
|
+
|
|
20
75
|
|
|
21
76
|
class Ensemble(GivenAxis):
|
|
22
77
|
def tree(self):
|
anemoi/datasets/data/join.py
CHANGED
|
@@ -118,6 +118,7 @@ class Join(Combined):
|
|
|
118
118
|
def variables_metadata(self):
|
|
119
119
|
result = {}
|
|
120
120
|
variables = [v for v in self.variables if not (v.startswith("(") and v.endswith(")"))]
|
|
121
|
+
|
|
121
122
|
for d in self.datasets:
|
|
122
123
|
md = d.variables_metadata
|
|
123
124
|
for v in variables:
|
|
@@ -130,8 +131,6 @@ class Join(Combined):
|
|
|
130
131
|
if v not in result:
|
|
131
132
|
LOG.error("Missing metadata for %r.", v)
|
|
132
133
|
|
|
133
|
-
raise ValueError("Some variables are missing metadata.")
|
|
134
|
-
|
|
135
134
|
return result
|
|
136
135
|
|
|
137
136
|
@cached_property
|
anemoi/datasets/data/merge.py
CHANGED
|
@@ -134,6 +134,9 @@ class Merge(Combined):
|
|
|
134
134
|
def tree(self):
|
|
135
135
|
return Node(self, [d.tree() for d in self.datasets], allow_gaps_in_dates=self.allow_gaps_in_dates)
|
|
136
136
|
|
|
137
|
+
def metadata_specific(self):
|
|
138
|
+
return {"allow_gaps_in_dates": self.allow_gaps_in_dates}
|
|
139
|
+
|
|
137
140
|
@debug_indexing
|
|
138
141
|
def __getitem__(self, n):
|
|
139
142
|
if isinstance(n, tuple):
|
anemoi/datasets/data/misc.py
CHANGED
|
@@ -194,7 +194,7 @@ def _open(a):
|
|
|
194
194
|
raise NotImplementedError(f"Unsupported argument: {type(a)}")
|
|
195
195
|
|
|
196
196
|
|
|
197
|
-
def _auto_adjust(datasets, kwargs):
|
|
197
|
+
def _auto_adjust(datasets, kwargs, exclude=None):
|
|
198
198
|
|
|
199
199
|
if "adjust" not in kwargs:
|
|
200
200
|
return datasets, kwargs
|
|
@@ -214,6 +214,9 @@ def _auto_adjust(datasets, kwargs):
|
|
|
214
214
|
for a in adjust_list:
|
|
215
215
|
adjust_set.update(ALIASES.get(a, [a]))
|
|
216
216
|
|
|
217
|
+
if exclude is not None:
|
|
218
|
+
adjust_set -= set(exclude)
|
|
219
|
+
|
|
217
220
|
extra = set(adjust_set) - set(ALIASES["all"])
|
|
218
221
|
if extra:
|
|
219
222
|
raise ValueError(f"Invalid adjust keys: {extra}")
|
|
@@ -335,6 +338,12 @@ def _open_dataset(*args, **kwargs):
|
|
|
335
338
|
assert not sets, sets
|
|
336
339
|
return cutout_factory(args, kwargs).mutate()
|
|
337
340
|
|
|
341
|
+
if "complement" in kwargs:
|
|
342
|
+
from .complement import complement_factory
|
|
343
|
+
|
|
344
|
+
assert not sets, sets
|
|
345
|
+
return complement_factory(args, kwargs).mutate()
|
|
346
|
+
|
|
338
347
|
for name in ("datasets", "dataset"):
|
|
339
348
|
if name in kwargs:
|
|
340
349
|
datasets = kwargs.pop(name)
|
anemoi/datasets/grids.py
CHANGED
|
@@ -152,7 +152,7 @@ def cutout_mask(
|
|
|
152
152
|
plot=None,
|
|
153
153
|
):
|
|
154
154
|
"""Return a mask for the points in [global_lats, global_lons] that are inside of [lats, lons]"""
|
|
155
|
-
from scipy.spatial import
|
|
155
|
+
from scipy.spatial import cKDTree
|
|
156
156
|
|
|
157
157
|
# TODO: transform min_distance from lat/lon to xyz
|
|
158
158
|
|
|
@@ -195,13 +195,13 @@ def cutout_mask(
|
|
|
195
195
|
min_distance = min_distance_km / 6371.0
|
|
196
196
|
else:
|
|
197
197
|
points = {"lam": lam_points, "global": global_points, None: global_points}[min_distance_km]
|
|
198
|
-
distances, _ =
|
|
198
|
+
distances, _ = cKDTree(points).query(points, k=2)
|
|
199
199
|
min_distance = np.min(distances[:, 1])
|
|
200
200
|
|
|
201
201
|
LOG.info(f"cutout_mask using min_distance = {min_distance * 6371.0} km")
|
|
202
202
|
|
|
203
|
-
# Use a
|
|
204
|
-
distances, indices =
|
|
203
|
+
# Use a cKDTree to find the nearest points
|
|
204
|
+
distances, indices = cKDTree(lam_points).query(global_points, k=neighbours)
|
|
205
205
|
|
|
206
206
|
# Centre of the Earth
|
|
207
207
|
zero = np.array([0.0, 0.0, 0.0])
|
|
@@ -255,7 +255,7 @@ def thinning_mask(
|
|
|
255
255
|
cropping_distance=2.0,
|
|
256
256
|
):
|
|
257
257
|
"""Return the list of points in [lats, lons] closest to [global_lats, global_lons]"""
|
|
258
|
-
from scipy.spatial import
|
|
258
|
+
from scipy.spatial import cKDTree
|
|
259
259
|
|
|
260
260
|
assert global_lats.ndim == 1
|
|
261
261
|
assert global_lons.ndim == 1
|
|
@@ -291,20 +291,20 @@ def thinning_mask(
|
|
|
291
291
|
xyx = latlon_to_xyz(lats, lons)
|
|
292
292
|
points = np.array(xyx).transpose()
|
|
293
293
|
|
|
294
|
-
# Use a
|
|
295
|
-
_, indices =
|
|
294
|
+
# Use a cKDTree to find the nearest points
|
|
295
|
+
_, indices = cKDTree(points).query(global_points, k=1)
|
|
296
296
|
|
|
297
297
|
return np.array([i for i in indices])
|
|
298
298
|
|
|
299
299
|
|
|
300
300
|
def outline(lats, lons, neighbours=5):
|
|
301
|
-
from scipy.spatial import
|
|
301
|
+
from scipy.spatial import cKDTree
|
|
302
302
|
|
|
303
303
|
xyx = latlon_to_xyz(lats, lons)
|
|
304
304
|
grid_points = np.array(xyx).transpose()
|
|
305
305
|
|
|
306
|
-
# Use a
|
|
307
|
-
_, indices =
|
|
306
|
+
# Use a cKDTree to find the nearest points
|
|
307
|
+
_, indices = cKDTree(grid_points).query(grid_points, k=neighbours)
|
|
308
308
|
|
|
309
309
|
# Centre of the Earth
|
|
310
310
|
zero = np.array([0.0, 0.0, 0.0])
|
|
@@ -379,6 +379,19 @@ def serialise_mask(mask):
|
|
|
379
379
|
return result
|
|
380
380
|
|
|
381
381
|
|
|
382
|
+
def nearest_grid_points(source_latitudes, source_longitudes, target_latitudes, target_longitudes):
|
|
383
|
+
from scipy.spatial import cKDTree
|
|
384
|
+
|
|
385
|
+
source_xyz = latlon_to_xyz(source_latitudes, source_longitudes)
|
|
386
|
+
source_points = np.array(source_xyz).transpose()
|
|
387
|
+
|
|
388
|
+
target_xyz = latlon_to_xyz(target_latitudes, target_longitudes)
|
|
389
|
+
target_points = np.array(target_xyz).transpose()
|
|
390
|
+
|
|
391
|
+
_, indices = cKDTree(source_points).query(target_points, k=1)
|
|
392
|
+
return indices
|
|
393
|
+
|
|
394
|
+
|
|
382
395
|
if __name__ == "__main__":
|
|
383
396
|
global_lats, global_lons = np.meshgrid(
|
|
384
397
|
np.linspace(90, -90, 90),
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.2
|
|
2
2
|
Name: anemoi-datasets
|
|
3
|
-
Version: 0.5.
|
|
3
|
+
Version: 0.5.13
|
|
4
4
|
Summary: A package to hold various functions to support training of ML models on ECMWF data.
|
|
5
5
|
Author-email: "European Centre for Medium-Range Weather Forecasts (ECMWF)" <software.support@ecmwf.int>
|
|
6
6
|
License: Apache License
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
anemoi/datasets/__init__.py,sha256=0GOHATiKgkUqLRgAVQhNP1aPO7ULfSr8DqUf2ANPEv8,1010
|
|
2
2
|
anemoi/datasets/__main__.py,sha256=5NW2A3OgTimB4ptwYThivIRSeCrvabMuvnr8mmnVx0E,715
|
|
3
|
-
anemoi/datasets/_version.py,sha256=
|
|
4
|
-
anemoi/datasets/grids.py,sha256=
|
|
3
|
+
anemoi/datasets/_version.py,sha256=Ywt_J-kUrkp4bt0vX46Ot5kDjsAA9dtf8IfOdhjGcXA,413
|
|
4
|
+
anemoi/datasets/grids.py,sha256=xHZwE3pJs0wP05x9qCgMcTuBnBGoCU51YKx5JQ7Ukts,11398
|
|
5
5
|
anemoi/datasets/testing.py,sha256=7HGOz5_V9MbkHTDJ4KbklGRndBMrFfVrBBu6a9k0_qY,1825
|
|
6
6
|
anemoi/datasets/commands/__init__.py,sha256=O5W3yHZywRoAqmRUioAr3zMCh0hGVV18wZYGvc00ioM,698
|
|
7
7
|
anemoi/datasets/commands/cleanup.py,sha256=2rD34bHtfOCLwQh7yXa02IJmmOYMOma4YDj0PM-2-Jc,1456
|
|
@@ -20,20 +20,21 @@ anemoi/datasets/commands/publish.py,sha256=z1MV9_1BsEnw81Y_17fHkKGYe8_ZJo9eeQ1kG
|
|
|
20
20
|
anemoi/datasets/commands/scan.py,sha256=mXzYEcYsncxC7ItyL_TlVRiWji6OFYfVxO5OMD9mbEI,3304
|
|
21
21
|
anemoi/datasets/compute/__init__.py,sha256=hCW0QcLHJmE-C1r38P27_ZOvCLNewex5iQEtZqx2ckI,393
|
|
22
22
|
anemoi/datasets/compute/recentre.py,sha256=tKs-YZLhqsMRBNEUF41hcuMmyvbRuFX07xJq-Cqg2_w,4954
|
|
23
|
-
anemoi/datasets/create/__init__.py,sha256=
|
|
24
|
-
anemoi/datasets/create/check.py,sha256=
|
|
23
|
+
anemoi/datasets/create/__init__.py,sha256=ImonAvvz-HqoZMF4vczyWEV48tGiZZ8RfM01SsxJ6ew,36427
|
|
24
|
+
anemoi/datasets/create/check.py,sha256=q205XxzR7UtBRI5qOANav_NAVqAERs0aLJ8oBL3VNc4,6153
|
|
25
25
|
anemoi/datasets/create/chunks.py,sha256=c7ufk-EamAGqxOI4ScTFlBzdEiH6V1R0f0SPavtZ2Xw,2457
|
|
26
26
|
anemoi/datasets/create/config.py,sha256=Tq9kJ-bKhYSTWCXNa8lZIJpO3RteZfCr5hQVM12IgD0,8892
|
|
27
27
|
anemoi/datasets/create/patch.py,sha256=YkDiFGV0DDg4WShVrIHhtRaAp9c82mANnR93YTVl36o,4171
|
|
28
28
|
anemoi/datasets/create/persistent.py,sha256=V3agRYKbmYqAyhsznsgC3PLSoFbFCSCXqcQZvvJfVTw,4381
|
|
29
29
|
anemoi/datasets/create/size.py,sha256=5fIF0yo63dTowojfeG9-MNkgypP5SA94WralxTZwxw4,1055
|
|
30
|
-
anemoi/datasets/create/utils.py,sha256=
|
|
30
|
+
anemoi/datasets/create/utils.py,sha256=3vRCPOrW0a7ZVx7gsflORwab74LV_BVVkgo_3SgyLpw,3347
|
|
31
31
|
anemoi/datasets/create/writer.py,sha256=6XSIgNwKkjrkdnSvr69mXD8McjT3iYJ0d1rOnxaGuCQ,1394
|
|
32
32
|
anemoi/datasets/create/zarr.py,sha256=0DkHwKaBpvTOrU1KmScLEfff_KfT1Rw5DXiRMD2d3Ws,5506
|
|
33
|
-
anemoi/datasets/create/functions/__init__.py,sha256=
|
|
33
|
+
anemoi/datasets/create/functions/__init__.py,sha256=qeoEndq4f0gosfyfb-t0CZcLdngJ4GlZIgGs0bp9j0U,1965
|
|
34
34
|
anemoi/datasets/create/functions/filters/__init__.py,sha256=SP6ReV1WYIf2Typf1FUaRHhphFGpU9kBoYtI-bYdu5U,395
|
|
35
35
|
anemoi/datasets/create/functions/filters/empty.py,sha256=EGLufFcNFoqIXTZj7jQFjtFahMfgCVWj6W5j--u5Q-Q,636
|
|
36
36
|
anemoi/datasets/create/functions/filters/noop.py,sha256=5YqumPxlGDOZOrYWayLr8PTycVWG2X_0PmoFi3Hj584,458
|
|
37
|
+
anemoi/datasets/create/functions/filters/orog_to_z.py,sha256=PZwqiTVBLlwp2yuHCW_D8Epcb0fPNjzuYYtmL3Gu1XY,1658
|
|
37
38
|
anemoi/datasets/create/functions/filters/pressure_level_relative_humidity_to_specific_humidity.py,sha256=jjmocA4WDKCAL49QUFk_3S0JRiPMmeVM7Wlxmfr1v6c,1857
|
|
38
39
|
anemoi/datasets/create/functions/filters/pressure_level_specific_humidity_to_relative_humidity.py,sha256=e8LvXUq-qNKJrcjb1DSUXaPeFfxcWxFjGAkm47cOnE8,1855
|
|
39
40
|
anemoi/datasets/create/functions/filters/rename.py,sha256=cDF3xmdhwzIZn_nwaO3hxG4fb2vpKtJtmy0ZdLGXyHI,2481
|
|
@@ -43,10 +44,12 @@ anemoi/datasets/create/functions/filters/single_level_relative_humidity_to_dewpo
|
|
|
43
44
|
anemoi/datasets/create/functions/filters/single_level_relative_humidity_to_specific_humidity.py,sha256=BnuLrIFcOh_qJBmxwdJqjGqoH0ca5zyKdZgF6QPmJY8,4090
|
|
44
45
|
anemoi/datasets/create/functions/filters/single_level_specific_humidity_to_relative_humidity.py,sha256=xqfklEwCqrQlhU6NV8vlVEZdY-hN3SpPpcNny2geVUI,12686
|
|
45
46
|
anemoi/datasets/create/functions/filters/speeddir_to_uv.py,sha256=d5t78GToTTXCb1S3HyhTJ2tuwZDnk7UBsHPV4Wn4M_w,2249
|
|
47
|
+
anemoi/datasets/create/functions/filters/sum.py,sha256=_f_xyIAbGdKoCXdd5zO7XOL2AHq_c04DFO8s6PhRR8I,1979
|
|
46
48
|
anemoi/datasets/create/functions/filters/unrotate_winds.py,sha256=tDFXUSF2flD83W7GgwP1RoVXBUO0445DvQdImulzDzA,2429
|
|
47
49
|
anemoi/datasets/create/functions/filters/uv_to_speeddir.py,sha256=niNuTSmyxLn4MGeNL1lowl5M0dH7har-flXy3ZtmKPM,1762
|
|
50
|
+
anemoi/datasets/create/functions/filters/wz_to_w.py,sha256=SbTYE6rRjObR-sJEDYyc0-1Kw39zZOAheGMznD7Ic9A,2161
|
|
48
51
|
anemoi/datasets/create/functions/sources/__init__.py,sha256=TMm8LerGY7--b0AMUqnz07ZGo-F7I9FF0DGlozcTtSg,1364
|
|
49
|
-
anemoi/datasets/create/functions/sources/accumulations.py,sha256=
|
|
52
|
+
anemoi/datasets/create/functions/sources/accumulations.py,sha256=xs3Ql3h_jfJDR0mzGIh54adVaHacHE3LQpBL5_8Rx8k,12373
|
|
50
53
|
anemoi/datasets/create/functions/sources/constants.py,sha256=GaiUpJPYupiLWl8O9GEZ9KmlD88pH6dlBiUVrhNl_uA,918
|
|
51
54
|
anemoi/datasets/create/functions/sources/empty.py,sha256=YTpOJ3rcb_eS9CbnpwPWBR9r1APIAaG6a_N803YFZFE,500
|
|
52
55
|
anemoi/datasets/create/functions/sources/forcings.py,sha256=p442lCOXm8TJFRlP0mgwZujveo9gCtdAGLS4KSIqYfk,661
|
|
@@ -61,15 +64,16 @@ anemoi/datasets/create/functions/sources/tendencies.py,sha256=z8iDelu0vvDE8S-Rus
|
|
|
61
64
|
anemoi/datasets/create/functions/sources/xarray_kerchunk.py,sha256=8evD6Sype3ffCbmQ0jMBpgR97UeNvkTB5rwchhy4YzY,1446
|
|
62
65
|
anemoi/datasets/create/functions/sources/xarray_zarr.py,sha256=3JvoGfQZ4NCUcfxDAbNZOL7z2VRNJzr1H3r8dsWbrgk,545
|
|
63
66
|
anemoi/datasets/create/functions/sources/zenodo.py,sha256=rPL9uNPeFTdI9XvVEahtHkxzE18MyrjNXZjpt_sNeH4,1251
|
|
64
|
-
anemoi/datasets/create/functions/sources/xarray/__init__.py,sha256=
|
|
67
|
+
anemoi/datasets/create/functions/sources/xarray/__init__.py,sha256=byu5zPP_4b7CjgSKvO3iL4xyZPmdoEVX93Tl7LBZc0c,3174
|
|
65
68
|
anemoi/datasets/create/functions/sources/xarray/coordinates.py,sha256=-FkcAaio2KumOd20eb1hLv9rRhjnu-CyqtqzrMsZx18,6213
|
|
66
|
-
anemoi/datasets/create/functions/sources/xarray/field.py,sha256=
|
|
67
|
-
anemoi/datasets/create/functions/sources/xarray/fieldlist.py,sha256=
|
|
69
|
+
anemoi/datasets/create/functions/sources/xarray/field.py,sha256=VfEuY-o1KZS1Bn4l7pR8FCx9hTtDbzKzPqJfwunwvRE,3816
|
|
70
|
+
anemoi/datasets/create/functions/sources/xarray/fieldlist.py,sha256=3wCLbdqpPlBlzJHKp_ETxAochPA9iFDyF94JVn1DOB8,6281
|
|
68
71
|
anemoi/datasets/create/functions/sources/xarray/flavour.py,sha256=6mqldGyx40Zgy4_VkuGWKgrSuPbWKe__nmEradQO5qg,14855
|
|
69
72
|
anemoi/datasets/create/functions/sources/xarray/grid.py,sha256=OuLBVv_CdgtLgGACpqhjX8fwtYzM7tfJiwUOXbG_ifw,3644
|
|
70
|
-
anemoi/datasets/create/functions/sources/xarray/metadata.py,sha256=
|
|
71
|
-
anemoi/datasets/create/functions/sources/xarray/
|
|
72
|
-
anemoi/datasets/create/functions/sources/xarray/
|
|
73
|
+
anemoi/datasets/create/functions/sources/xarray/metadata.py,sha256=zbbb0ssKhZJvogLJ1WPJMBVVHl40GjHWbmE6RzLwAz4,4336
|
|
74
|
+
anemoi/datasets/create/functions/sources/xarray/patch.py,sha256=k1v7bUs-sO7-431T0bh5CSTE1FtgjhIlaPQ2-kSpc2E,1051
|
|
75
|
+
anemoi/datasets/create/functions/sources/xarray/time.py,sha256=jGnaupnNQr9x4F7ijahzxtMQltC5fLbrEKajq5dIxR8,6458
|
|
76
|
+
anemoi/datasets/create/functions/sources/xarray/variable.py,sha256=IdxZGOu1DMaUVlDGyVHuZiGUsN4buJoxexSFUD_NyFg,5029
|
|
73
77
|
anemoi/datasets/create/input/__init__.py,sha256=cAwfW9AQiG2PfmZ2Irll7HX8HyiC0Nk1Q9OhoQ84ZAg,1625
|
|
74
78
|
anemoi/datasets/create/input/action.py,sha256=SApZApq-_mlOwk1NTERgQlPdPL8lBlIk6rxYX3JBw_E,3857
|
|
75
79
|
anemoi/datasets/create/input/concat.py,sha256=DwxgoTSTqNDsVcX5btUBAA7vXtX3G5m-zJ-jDrmAC-c,3279
|
|
@@ -81,28 +85,29 @@ anemoi/datasets/create/input/function.py,sha256=F5GQgbtFYmyqFAgNGoGDuWw-xqkcCLzu
|
|
|
81
85
|
anemoi/datasets/create/input/join.py,sha256=wQP1-vVg4as-R5i3pstgK6HmTJAY7WyWYhCEF6FIU1c,1991
|
|
82
86
|
anemoi/datasets/create/input/misc.py,sha256=r7NC_QRYA8iiJJbSFgQnNuixymATK0CPZknGxgYcLOk,1975
|
|
83
87
|
anemoi/datasets/create/input/pipe.py,sha256=KfPCtiqyfqkXbmC-2LTqHkCQ7bJY46XMvNDnp9QeHTQ,1344
|
|
84
|
-
anemoi/datasets/create/input/repeated_dates.py,sha256=
|
|
88
|
+
anemoi/datasets/create/input/repeated_dates.py,sha256=59EvJ_cQwA-p_42cmMFy3pBAAWV0xwPg4E3q2PIofcM,7461
|
|
85
89
|
anemoi/datasets/create/input/result.py,sha256=-pcVcaaj3G_xcNKWWTgzVH5Ds5-ETWmErN0KeQGitAw,20013
|
|
86
90
|
anemoi/datasets/create/input/step.py,sha256=CoowF9mc3kepT8XQ2ObxO750rnQEkYNTviIHQ1m-4UA,2886
|
|
87
91
|
anemoi/datasets/create/input/template.py,sha256=Vgi4wQ1aeswLbji0fIzshYhISmzdrt7b0BmgeJJjYGc,1859
|
|
88
92
|
anemoi/datasets/create/input/trace.py,sha256=DYXMSnwKqOIx0XWZTKNJojWz4EqaFLknTh6ysxsW9uY,2198
|
|
89
|
-
anemoi/datasets/create/statistics/__init__.py,sha256=
|
|
93
|
+
anemoi/datasets/create/statistics/__init__.py,sha256=iJ3mZ6eEI88wPXUKyOhNKqhakyHoceX9ICEKXVOriTo,12789
|
|
90
94
|
anemoi/datasets/create/statistics/summary.py,sha256=wmnz4fZkr6fomXgI8JlMutU8gakfrXTc5ixf3Np7gZA,3385
|
|
91
95
|
anemoi/datasets/data/__init__.py,sha256=AW1-Ycj77pWQsZcDGsp0pgTS5rFW6XC4CzuUEIUPAIk,1558
|
|
96
|
+
anemoi/datasets/data/complement.py,sha256=hTvA_zTGIHAvZYPv1npVpRpxPg8nXbNTEBAU8r33rlc,5105
|
|
92
97
|
anemoi/datasets/data/concat.py,sha256=udtYINuoLOEYYKhi_VpG2-emv80pwZbFAZKwNwXJk3s,5244
|
|
93
|
-
anemoi/datasets/data/dataset.py,sha256=
|
|
98
|
+
anemoi/datasets/data/dataset.py,sha256=x_ID6Ga_TbBfECqhOC4i3CTqo0UD34KJVqUWpvI9Si4,17926
|
|
94
99
|
anemoi/datasets/data/debug.css,sha256=z2X_ZDSnZ9C3pyZPWnQiEyAxuMxUaxJxET4oaCImTAQ,211
|
|
95
100
|
anemoi/datasets/data/debug.py,sha256=IjCMwtAvknF51PCl_YRYgMZB2iX_9DC5DKILNgl_UHQ,6300
|
|
96
|
-
anemoi/datasets/data/ensemble.py,sha256=
|
|
101
|
+
anemoi/datasets/data/ensemble.py,sha256=7ognsmoHDGw0cCs3hsARoV32J1qlQys6iUCJ7XSrARI,2923
|
|
97
102
|
anemoi/datasets/data/fill_missing.py,sha256=4btLi-D-hFTsS_57_gIC1nK5AVifAO-V4M-fqMrtrxk,4636
|
|
98
103
|
anemoi/datasets/data/forwards.py,sha256=P9DfSY5B9w9gtkKfV6TIzXel_LY83g-2nEreJy2rYkU,8916
|
|
99
104
|
anemoi/datasets/data/grids.py,sha256=p7_nT7RLH6uKcxeAzQiGYk9lFxU_OOikDrwlb2rdEqI,15765
|
|
100
105
|
anemoi/datasets/data/indexing.py,sha256=9lycQXSqUIbYj52JlFv0w_Gf6soVZnbVGswYMvGPpqs,4773
|
|
101
106
|
anemoi/datasets/data/interpolate.py,sha256=D27lSH8yNhm0aoO0U3UoRbr3kni7OWXSu_X4jCbIrA0,4137
|
|
102
|
-
anemoi/datasets/data/join.py,sha256=
|
|
107
|
+
anemoi/datasets/data/join.py,sha256=IG9Bj4o4Z25cl5YGMqtl75UuSZCWIJwGIUB0fsfnkE8,5456
|
|
103
108
|
anemoi/datasets/data/masked.py,sha256=eAVGVmQR7tWsd3xXYGXGyq28uRLwL50vOXWTNNdHxl0,4530
|
|
104
|
-
anemoi/datasets/data/merge.py,sha256=
|
|
105
|
-
anemoi/datasets/data/misc.py,sha256=
|
|
109
|
+
anemoi/datasets/data/merge.py,sha256=dr0sX2ufm-qOgOAMV5oh8qQwPvSdYbU-mhux6u-cmQw,5547
|
|
110
|
+
anemoi/datasets/data/misc.py,sha256=J1v84jHpRgDK0DUrNmII5oqt3jft8rSTve2GtxqTKa8,10310
|
|
106
111
|
anemoi/datasets/data/missing.py,sha256=SWEjiC1usBjZtlKMr73uKetnoQZoflVQVGqLP2gJR7A,7131
|
|
107
112
|
anemoi/datasets/data/rescale.py,sha256=wMU7tFZebnOqJJxaIGOqNqhpNKGsPNZMC1YxuiHvri4,4112
|
|
108
113
|
anemoi/datasets/data/select.py,sha256=XW_ohlhrF8FLe13pdM3DRZDxbHxntcsO0F56GRqZQY0,4293
|
|
@@ -115,9 +120,9 @@ anemoi/datasets/dates/__init__.py,sha256=wX2FvlmRfHV5HDmllIxwfrC1LuRlb7i6SguLLas
|
|
|
115
120
|
anemoi/datasets/dates/groups.py,sha256=i7x8z0kv6E8qUfm1tMZR1aaOqNwQzEkV-VWpOvHjoX4,5390
|
|
116
121
|
anemoi/datasets/utils/__init__.py,sha256=hCW0QcLHJmE-C1r38P27_ZOvCLNewex5iQEtZqx2ckI,393
|
|
117
122
|
anemoi/datasets/utils/fields.py,sha256=l7xKOiRLgk9Eewykqu7xZP9xOajG2dx2CiDlGvBVejU,1411
|
|
118
|
-
anemoi_datasets-0.5.
|
|
119
|
-
anemoi_datasets-0.5.
|
|
120
|
-
anemoi_datasets-0.5.
|
|
121
|
-
anemoi_datasets-0.5.
|
|
122
|
-
anemoi_datasets-0.5.
|
|
123
|
-
anemoi_datasets-0.5.
|
|
123
|
+
anemoi_datasets-0.5.13.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
124
|
+
anemoi_datasets-0.5.13.dist-info/METADATA,sha256=ArEcpcTEQ_Tt270GtB5GEeDX_1SJMKM8P4NWMdSve8M,15598
|
|
125
|
+
anemoi_datasets-0.5.13.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
|
|
126
|
+
anemoi_datasets-0.5.13.dist-info/entry_points.txt,sha256=yR-o-4uiPEA_GLBL81SkMYnUoxq3CAV3hHulQiRtGG0,66
|
|
127
|
+
anemoi_datasets-0.5.13.dist-info/top_level.txt,sha256=DYn8VPs-fNwr7fNH9XIBqeXIwiYYd2E2k5-dUFFqUz0,7
|
|
128
|
+
anemoi_datasets-0.5.13.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|