anemoi-datasets 0.4.3__py3-none-any.whl → 0.4.5__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/commands/cleanup.py +44 -0
- anemoi/datasets/commands/create.py +50 -20
- anemoi/datasets/commands/finalise-additions.py +45 -0
- anemoi/datasets/commands/finalise.py +39 -0
- anemoi/datasets/commands/init-additions.py +45 -0
- anemoi/datasets/commands/init.py +67 -0
- anemoi/datasets/commands/inspect.py +1 -1
- anemoi/datasets/commands/load-additions.py +47 -0
- anemoi/datasets/commands/load.py +47 -0
- anemoi/datasets/commands/patch.py +39 -0
- anemoi/datasets/compute/recentre.py +1 -1
- anemoi/datasets/create/__init__.py +961 -146
- anemoi/datasets/create/check.py +5 -3
- anemoi/datasets/create/config.py +53 -2
- anemoi/datasets/create/functions/sources/accumulations.py +6 -22
- anemoi/datasets/create/functions/sources/hindcasts.py +27 -12
- anemoi/datasets/create/functions/sources/tendencies.py +1 -1
- anemoi/datasets/create/functions/sources/xarray/__init__.py +12 -2
- anemoi/datasets/create/functions/sources/xarray/coordinates.py +7 -0
- anemoi/datasets/create/functions/sources/xarray/field.py +1 -1
- anemoi/datasets/create/functions/sources/xarray/fieldlist.py +0 -2
- anemoi/datasets/create/functions/sources/xarray/flavour.py +21 -1
- anemoi/datasets/create/functions/sources/xarray/metadata.py +27 -29
- anemoi/datasets/create/functions/sources/xarray/time.py +63 -30
- anemoi/datasets/create/functions/sources/xarray/variable.py +15 -38
- anemoi/datasets/create/input.py +62 -25
- anemoi/datasets/create/statistics/__init__.py +39 -23
- anemoi/datasets/create/utils.py +3 -2
- anemoi/datasets/data/__init__.py +1 -0
- anemoi/datasets/data/concat.py +46 -2
- anemoi/datasets/data/dataset.py +109 -34
- anemoi/datasets/data/forwards.py +17 -8
- anemoi/datasets/data/grids.py +17 -3
- anemoi/datasets/data/interpolate.py +133 -0
- anemoi/datasets/data/misc.py +56 -66
- anemoi/datasets/data/missing.py +240 -0
- anemoi/datasets/data/select.py +7 -1
- anemoi/datasets/data/stores.py +3 -3
- anemoi/datasets/data/subset.py +47 -5
- anemoi/datasets/data/unchecked.py +20 -22
- anemoi/datasets/data/xy.py +125 -0
- anemoi/datasets/dates/__init__.py +33 -20
- anemoi/datasets/dates/groups.py +2 -2
- anemoi/datasets/grids.py +66 -48
- {anemoi_datasets-0.4.3.dist-info → anemoi_datasets-0.4.5.dist-info}/METADATA +5 -5
- {anemoi_datasets-0.4.3.dist-info → anemoi_datasets-0.4.5.dist-info}/RECORD +51 -41
- {anemoi_datasets-0.4.3.dist-info → anemoi_datasets-0.4.5.dist-info}/WHEEL +1 -1
- anemoi/datasets/create/loaders.py +0 -924
- {anemoi_datasets-0.4.3.dist-info → anemoi_datasets-0.4.5.dist-info}/LICENSE +0 -0
- {anemoi_datasets-0.4.3.dist-info → anemoi_datasets-0.4.5.dist-info}/entry_points.txt +0 -0
- {anemoi_datasets-0.4.3.dist-info → anemoi_datasets-0.4.5.dist-info}/top_level.txt +0 -0
anemoi/datasets/data/forwards.py
CHANGED
|
@@ -23,7 +23,7 @@ LOG = logging.getLogger(__name__)
|
|
|
23
23
|
|
|
24
24
|
class Forwards(Dataset):
|
|
25
25
|
def __init__(self, forward):
|
|
26
|
-
self.forward = forward
|
|
26
|
+
self.forward = forward.mutate()
|
|
27
27
|
|
|
28
28
|
def __len__(self):
|
|
29
29
|
return len(self.forward)
|
|
@@ -118,6 +118,9 @@ class Combined(Forwards):
|
|
|
118
118
|
# Forward most properties to the first dataset
|
|
119
119
|
super().__init__(datasets[0])
|
|
120
120
|
|
|
121
|
+
def mutate(self):
|
|
122
|
+
return self
|
|
123
|
+
|
|
121
124
|
def check_same_resolution(self, d1, d2):
|
|
122
125
|
if d1.resolution != d2.resolution:
|
|
123
126
|
raise ValueError(f"Incompatible resolutions: {d1.resolution} and {d2.resolution} ({d1} {d2})")
|
|
@@ -187,14 +190,9 @@ class Combined(Forwards):
|
|
|
187
190
|
**kwargs,
|
|
188
191
|
)
|
|
189
192
|
|
|
190
|
-
@
|
|
193
|
+
@property
|
|
191
194
|
def missing(self):
|
|
192
|
-
|
|
193
|
-
result = set()
|
|
194
|
-
for d in self.datasets:
|
|
195
|
-
result.update(offset + m for m in d.missing)
|
|
196
|
-
offset += len(d)
|
|
197
|
-
return result
|
|
195
|
+
raise NotImplementedError("missing() not implemented for Combined")
|
|
198
196
|
|
|
199
197
|
def get_dataset_names(self, names):
|
|
200
198
|
for d in self.datasets:
|
|
@@ -249,3 +247,14 @@ class GivenAxis(Combined):
|
|
|
249
247
|
return self._get_slice(n)
|
|
250
248
|
|
|
251
249
|
return np.concatenate([d[n] for d in self.datasets], axis=self.axis - 1)
|
|
250
|
+
|
|
251
|
+
@cached_property
|
|
252
|
+
def missing(self):
|
|
253
|
+
offset = 0
|
|
254
|
+
result = set()
|
|
255
|
+
for d in self.datasets:
|
|
256
|
+
print("--->", d.missing, d)
|
|
257
|
+
result.update(offset + m for m in d.missing)
|
|
258
|
+
if self.axis == 0: # Advance if axis is time
|
|
259
|
+
offset += len(d)
|
|
260
|
+
return result
|
anemoi/datasets/data/grids.py
CHANGED
|
@@ -128,7 +128,7 @@ class Grids(GridsBase):
|
|
|
128
128
|
|
|
129
129
|
|
|
130
130
|
class Cutout(GridsBase):
|
|
131
|
-
def __init__(self, datasets, axis):
|
|
131
|
+
def __init__(self, datasets, axis, min_distance_km=None, cropping_distance=2.0, neighbours=5, plot=False):
|
|
132
132
|
from anemoi.datasets.grids import cutout_mask
|
|
133
133
|
|
|
134
134
|
super().__init__(datasets, axis)
|
|
@@ -144,7 +144,10 @@ class Cutout(GridsBase):
|
|
|
144
144
|
self.lam.longitudes,
|
|
145
145
|
self.globe.latitudes,
|
|
146
146
|
self.globe.longitudes,
|
|
147
|
-
|
|
147
|
+
plot=plot,
|
|
148
|
+
min_distance_km=min_distance_km,
|
|
149
|
+
cropping_distance=cropping_distance,
|
|
150
|
+
neighbours=neighbours,
|
|
148
151
|
)
|
|
149
152
|
assert len(self.mask) == self.globe.shape[3], (
|
|
150
153
|
len(self.mask),
|
|
@@ -229,6 +232,10 @@ def cutout_factory(args, kwargs):
|
|
|
229
232
|
|
|
230
233
|
cutout = kwargs.pop("cutout")
|
|
231
234
|
axis = kwargs.pop("axis", 3)
|
|
235
|
+
plot = kwargs.pop("plot", None)
|
|
236
|
+
min_distance_km = kwargs.pop("min_distance_km", None)
|
|
237
|
+
cropping_distance = kwargs.pop("cropping_distance", 2.0)
|
|
238
|
+
neighbours = kwargs.pop("neighbours", 5)
|
|
232
239
|
|
|
233
240
|
assert len(args) == 0
|
|
234
241
|
assert isinstance(cutout, (list, tuple))
|
|
@@ -236,4 +243,11 @@ def cutout_factory(args, kwargs):
|
|
|
236
243
|
datasets = [_open(e) for e in cutout]
|
|
237
244
|
datasets, kwargs = _auto_adjust(datasets, kwargs)
|
|
238
245
|
|
|
239
|
-
return Cutout(
|
|
246
|
+
return Cutout(
|
|
247
|
+
datasets,
|
|
248
|
+
axis=axis,
|
|
249
|
+
neighbours=neighbours,
|
|
250
|
+
min_distance_km=min_distance_km,
|
|
251
|
+
cropping_distance=cropping_distance,
|
|
252
|
+
plot=plot,
|
|
253
|
+
)._subset(**kwargs)
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
# (C) Copyright 2024 European Centre for Medium-Range Weather Forecasts.
|
|
2
|
+
# This software is licensed under the terms of the Apache Licence Version 2.0
|
|
3
|
+
# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
|
|
4
|
+
# In applying this licence, ECMWF does not waive the privileges and immunities
|
|
5
|
+
# granted to it by virtue of its status as an intergovernmental organisation
|
|
6
|
+
# nor does it submit to any jurisdiction.
|
|
7
|
+
|
|
8
|
+
import logging
|
|
9
|
+
from functools import cached_property
|
|
10
|
+
|
|
11
|
+
import numpy as np
|
|
12
|
+
from anemoi.utils.dates import frequency_to_timedelta
|
|
13
|
+
|
|
14
|
+
from .debug import Node
|
|
15
|
+
from .debug import debug_indexing
|
|
16
|
+
from .forwards import Forwards
|
|
17
|
+
from .indexing import apply_index_to_slices_changes
|
|
18
|
+
from .indexing import expand_list_indexing
|
|
19
|
+
from .indexing import index_to_slices
|
|
20
|
+
from .indexing import update_tuple
|
|
21
|
+
|
|
22
|
+
LOG = logging.getLogger(__name__)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class InterpolateFrequency(Forwards):
|
|
26
|
+
|
|
27
|
+
def __init__(self, dataset, frequency):
|
|
28
|
+
super().__init__(dataset)
|
|
29
|
+
self._frequency = frequency_to_timedelta(frequency)
|
|
30
|
+
|
|
31
|
+
self.seconds = self._frequency.total_seconds()
|
|
32
|
+
other_seconds = dataset.frequency.total_seconds()
|
|
33
|
+
|
|
34
|
+
self.seconds = int(self.seconds)
|
|
35
|
+
assert self.seconds == self._frequency.total_seconds()
|
|
36
|
+
|
|
37
|
+
other_seconds = int(other_seconds)
|
|
38
|
+
assert other_seconds == dataset.frequency.total_seconds()
|
|
39
|
+
|
|
40
|
+
if self.seconds >= other_seconds:
|
|
41
|
+
raise ValueError(
|
|
42
|
+
f"Interpolate frequency {self._frequency} must be more frequent than dataset frequency {dataset.frequency}"
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
if other_seconds % self.seconds != 0:
|
|
46
|
+
raise ValueError(
|
|
47
|
+
f"Interpolate frequency {self._frequency} must be a multiple of the dataset frequency {dataset.frequency}"
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
self.ratio = other_seconds // self.seconds
|
|
51
|
+
self.alphas = np.linspace(0, 1, self.ratio + 1)
|
|
52
|
+
self.other_len = len(dataset)
|
|
53
|
+
|
|
54
|
+
@debug_indexing
|
|
55
|
+
@expand_list_indexing
|
|
56
|
+
def _get_tuple(self, index):
|
|
57
|
+
index, changes = index_to_slices(index, self.shape)
|
|
58
|
+
index, previous = update_tuple(index, 0, slice(None))
|
|
59
|
+
result = self._get_slice(previous)
|
|
60
|
+
return apply_index_to_slices_changes(result[index], changes)
|
|
61
|
+
|
|
62
|
+
def _get_slice(self, s):
|
|
63
|
+
return np.stack([self[i] for i in range(*s.indices(self._len))])
|
|
64
|
+
|
|
65
|
+
@debug_indexing
|
|
66
|
+
def __getitem__(self, n):
|
|
67
|
+
if isinstance(n, tuple):
|
|
68
|
+
return self._get_tuple(n)
|
|
69
|
+
|
|
70
|
+
if isinstance(n, slice):
|
|
71
|
+
return self._get_slice(n)
|
|
72
|
+
|
|
73
|
+
if n < 0:
|
|
74
|
+
n += self._len
|
|
75
|
+
|
|
76
|
+
if n == self._len - 1:
|
|
77
|
+
# Special case for the last element
|
|
78
|
+
return self.forward[-1]
|
|
79
|
+
|
|
80
|
+
i = n // self.ratio
|
|
81
|
+
x = n % self.ratio
|
|
82
|
+
|
|
83
|
+
if x == 0:
|
|
84
|
+
# No interpolation needed
|
|
85
|
+
return self.forward[i]
|
|
86
|
+
|
|
87
|
+
alpha = self.alphas[x]
|
|
88
|
+
|
|
89
|
+
assert 0 < alpha < 1, alpha
|
|
90
|
+
return self.forward[i] * (1 - alpha) + self.forward[i + 1] * alpha
|
|
91
|
+
|
|
92
|
+
def __len__(self):
|
|
93
|
+
return (self.other_len - 1) * self.ratio + 1
|
|
94
|
+
|
|
95
|
+
@property
|
|
96
|
+
def frequency(self):
|
|
97
|
+
return self._frequency
|
|
98
|
+
|
|
99
|
+
@cached_property
|
|
100
|
+
def dates(self):
|
|
101
|
+
result = []
|
|
102
|
+
deltas = [np.timedelta64(self.seconds * i, "s") for i in range(self.ratio)]
|
|
103
|
+
for d in self.forward.dates[:-1]:
|
|
104
|
+
for i in deltas:
|
|
105
|
+
result.append(d + i)
|
|
106
|
+
result.append(self.forward.dates[-1])
|
|
107
|
+
return np.array(result)
|
|
108
|
+
|
|
109
|
+
@property
|
|
110
|
+
def shape(self):
|
|
111
|
+
return (self._len,) + self.forward.shape[1:]
|
|
112
|
+
|
|
113
|
+
def tree(self):
|
|
114
|
+
return Node(self, [self.forward.tree()], frequency=self.frequency)
|
|
115
|
+
|
|
116
|
+
@cached_property
|
|
117
|
+
def missing(self):
|
|
118
|
+
result = []
|
|
119
|
+
j = 0
|
|
120
|
+
for i in range(self.other_len):
|
|
121
|
+
missing = i in self.forward.missing
|
|
122
|
+
for _ in range(self.ratio):
|
|
123
|
+
if missing:
|
|
124
|
+
result.append(j)
|
|
125
|
+
j += 1
|
|
126
|
+
|
|
127
|
+
result = set(x for x in result if x < self._len)
|
|
128
|
+
return result
|
|
129
|
+
|
|
130
|
+
def subclass_metadata_specific(self):
|
|
131
|
+
return {
|
|
132
|
+
# "frequency": frequency_to_string(self._frequency),
|
|
133
|
+
}
|
anemoi/datasets/data/misc.py
CHANGED
|
@@ -8,7 +8,6 @@
|
|
|
8
8
|
import calendar
|
|
9
9
|
import datetime
|
|
10
10
|
import logging
|
|
11
|
-
import re
|
|
12
11
|
from pathlib import PurePath
|
|
13
12
|
|
|
14
13
|
import numpy as np
|
|
@@ -39,26 +38,21 @@ def add_dataset_path(path):
|
|
|
39
38
|
config["datasets"]["path"].append(path)
|
|
40
39
|
|
|
41
40
|
|
|
42
|
-
def
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
if isinstance(frequency, float):
|
|
47
|
-
assert int(frequency) == frequency
|
|
48
|
-
return int(frequency)
|
|
49
|
-
|
|
50
|
-
m = re.match(r"(\d+)([dh])?", frequency)
|
|
51
|
-
if m is None:
|
|
52
|
-
raise ValueError("Invalid frequency: " + frequency)
|
|
53
|
-
|
|
54
|
-
frequency = int(m.group(1))
|
|
55
|
-
if m.group(2) == "h":
|
|
56
|
-
return frequency
|
|
57
|
-
|
|
58
|
-
if m.group(2) == "d":
|
|
59
|
-
return frequency * 24
|
|
41
|
+
def round_datetime(d, dates, up):
|
|
42
|
+
"""Round up (or down) a datetime to the nearest date in a list of dates"""
|
|
43
|
+
if dates is None or len(dates) == 0:
|
|
44
|
+
return d
|
|
60
45
|
|
|
61
|
-
|
|
46
|
+
for i, date in enumerate(dates):
|
|
47
|
+
if date == d:
|
|
48
|
+
return date
|
|
49
|
+
if date > d:
|
|
50
|
+
if up:
|
|
51
|
+
return date
|
|
52
|
+
if i > 0:
|
|
53
|
+
return dates[i - 1]
|
|
54
|
+
return date
|
|
55
|
+
return dates[-1]
|
|
62
56
|
|
|
63
57
|
|
|
64
58
|
def _as_date(d, dates, last):
|
|
@@ -67,7 +61,8 @@ def _as_date(d, dates, last):
|
|
|
67
61
|
# so we need to check for datetime.datetime first
|
|
68
62
|
|
|
69
63
|
if isinstance(d, (np.datetime64, datetime.datetime)):
|
|
70
|
-
|
|
64
|
+
d = round_datetime(d, dates, up=not last)
|
|
65
|
+
return np.datetime64(d)
|
|
71
66
|
|
|
72
67
|
if isinstance(d, datetime.date):
|
|
73
68
|
d = d.year * 10_000 + d.month * 100 + d.day
|
|
@@ -81,27 +76,27 @@ def _as_date(d, dates, last):
|
|
|
81
76
|
if len(str(d)) == 4:
|
|
82
77
|
year = d
|
|
83
78
|
if last:
|
|
84
|
-
return np.datetime64(f"{year:04}-12-31T23:59:59")
|
|
79
|
+
return _as_date(np.datetime64(f"{year:04}-12-31T23:59:59"), dates, last)
|
|
85
80
|
else:
|
|
86
|
-
return np.datetime64(f"{year:04}-01-01T00:00:00")
|
|
81
|
+
return _as_date(np.datetime64(f"{year:04}-01-01T00:00:00"), dates, last)
|
|
87
82
|
|
|
88
83
|
if len(str(d)) == 6:
|
|
89
84
|
year = d // 100
|
|
90
85
|
month = d % 100
|
|
91
86
|
if last:
|
|
92
87
|
_, last_day = calendar.monthrange(year, month)
|
|
93
|
-
return np.datetime64(f"{year:04}-{month:02}-{last_day:02}T23:59:59")
|
|
88
|
+
return _as_date(np.datetime64(f"{year:04}-{month:02}-{last_day:02}T23:59:59"), dates, last)
|
|
94
89
|
else:
|
|
95
|
-
return np.datetime64(f"{year:04}-{month:02}-01T00:00:00")
|
|
90
|
+
return _as_date(np.datetime64(f"{year:04}-{month:02}-01T00:00:00"), dates, last)
|
|
96
91
|
|
|
97
92
|
if len(str(d)) == 8:
|
|
98
93
|
year = d // 10000
|
|
99
94
|
month = (d % 10000) // 100
|
|
100
95
|
day = d % 100
|
|
101
96
|
if last:
|
|
102
|
-
return np.datetime64(f"{year:04}-{month:02}-{day:02}T23:59:59")
|
|
97
|
+
return _as_date(np.datetime64(f"{year:04}-{month:02}-{day:02}T23:59:59"), dates, last)
|
|
103
98
|
else:
|
|
104
|
-
return np.datetime64(f"{year:04}-{month:02}-{day:02}T00:00:00")
|
|
99
|
+
return _as_date(np.datetime64(f"{year:04}-{month:02}-{day:02}T00:00:00"), dates, last)
|
|
105
100
|
|
|
106
101
|
if isinstance(d, str):
|
|
107
102
|
|
|
@@ -109,7 +104,11 @@ def _as_date(d, dates, last):
|
|
|
109
104
|
date, time = d.replace(" ", "T").split("T")
|
|
110
105
|
year, month, day = [int(_) for _ in date.split("-")]
|
|
111
106
|
hour, minute, second = [int(_) for _ in time.split(":")]
|
|
112
|
-
return
|
|
107
|
+
return _as_date(
|
|
108
|
+
np.datetime64(f"{year:04}-{month:02}-{day:02}T{hour:02}:{minute:02}:{second:02}"),
|
|
109
|
+
dates,
|
|
110
|
+
last,
|
|
111
|
+
)
|
|
113
112
|
|
|
114
113
|
if "-" in d:
|
|
115
114
|
assert ":" not in d
|
|
@@ -121,11 +120,8 @@ def _as_date(d, dates, last):
|
|
|
121
120
|
return _as_date(int(bits[0]) * 100 + int(bits[1]), dates, last)
|
|
122
121
|
|
|
123
122
|
if len(bits) == 3:
|
|
124
|
-
return _as_date(
|
|
125
|
-
|
|
126
|
-
dates,
|
|
127
|
-
last,
|
|
128
|
-
)
|
|
123
|
+
return _as_date(int(bits[0]) * 10000 + int(bits[1]) * 100 + int(bits[2]), dates, last)
|
|
124
|
+
|
|
129
125
|
if ":" in d:
|
|
130
126
|
assert len(d) == 5
|
|
131
127
|
hour, minute = d.split(":")
|
|
@@ -136,7 +132,7 @@ def _as_date(d, dates, last):
|
|
|
136
132
|
month = first.month
|
|
137
133
|
day = first.day
|
|
138
134
|
|
|
139
|
-
return np.datetime64(f"{year:04}-{month:02}-{day:02}T{hour}:00:00")
|
|
135
|
+
return _as_date(np.datetime64(f"{year:04}-{month:02}-{day:02}T{hour}:00:00"), dates, last)
|
|
140
136
|
|
|
141
137
|
raise NotImplementedError(f"Unsupported date: {d} ({type(d)})")
|
|
142
138
|
|
|
@@ -163,28 +159,10 @@ def _concat_or_join(datasets, kwargs):
|
|
|
163
159
|
|
|
164
160
|
return Join(datasets)._overlay(), kwargs
|
|
165
161
|
|
|
166
|
-
# Make sure the dates are disjoint
|
|
167
|
-
for i in range(len(ranges)):
|
|
168
|
-
r = ranges[i]
|
|
169
|
-
for j in range(i + 1, len(ranges)):
|
|
170
|
-
s = ranges[j]
|
|
171
|
-
if r[0] <= s[0] <= r[1] or r[0] <= s[1] <= r[1]:
|
|
172
|
-
raise ValueError(f"Overlapping dates: {r} and {s} ({datasets[i]} {datasets[j]})")
|
|
173
|
-
|
|
174
|
-
# For now we should have the datasets in order with no gaps
|
|
175
|
-
|
|
176
|
-
frequency = _frequency_to_hours(datasets[0].frequency)
|
|
177
|
-
|
|
178
|
-
for i in range(len(ranges) - 1):
|
|
179
|
-
r = ranges[i]
|
|
180
|
-
s = ranges[i + 1]
|
|
181
|
-
if r[1] + datetime.timedelta(hours=frequency) != s[0]:
|
|
182
|
-
raise ValueError(
|
|
183
|
-
"Datasets must be sorted by dates, with no gaps: " f"{r} and {s} ({datasets[i]} {datasets[i+1]})"
|
|
184
|
-
)
|
|
185
|
-
|
|
186
162
|
from .concat import Concat
|
|
187
163
|
|
|
164
|
+
Concat.check_dataset_compatibility(datasets)
|
|
165
|
+
|
|
188
166
|
return Concat(datasets), kwargs
|
|
189
167
|
|
|
190
168
|
|
|
@@ -193,7 +171,7 @@ def _open(a):
|
|
|
193
171
|
from .stores import zarr_lookup
|
|
194
172
|
|
|
195
173
|
if isinstance(a, Dataset):
|
|
196
|
-
return a
|
|
174
|
+
return a.mutate()
|
|
197
175
|
|
|
198
176
|
if isinstance(a, zarr.hierarchy.Group):
|
|
199
177
|
return Zarr(a).mutate()
|
|
@@ -202,13 +180,13 @@ def _open(a):
|
|
|
202
180
|
return Zarr(zarr_lookup(a)).mutate()
|
|
203
181
|
|
|
204
182
|
if isinstance(a, PurePath):
|
|
205
|
-
return _open(str(a))
|
|
183
|
+
return _open(str(a)).mutate()
|
|
206
184
|
|
|
207
185
|
if isinstance(a, dict):
|
|
208
|
-
return _open_dataset(**a)
|
|
186
|
+
return _open_dataset(**a).mutate()
|
|
209
187
|
|
|
210
188
|
if isinstance(a, (list, tuple)):
|
|
211
|
-
return _open_dataset(*a)
|
|
189
|
+
return _open_dataset(*a).mutate()
|
|
212
190
|
|
|
213
191
|
raise NotImplementedError(f"Unsupported argument: {type(a)}")
|
|
214
192
|
|
|
@@ -288,47 +266,59 @@ def _open_dataset(*args, **kwargs):
|
|
|
288
266
|
for a in args:
|
|
289
267
|
sets.append(_open(a))
|
|
290
268
|
|
|
269
|
+
if "xy" in kwargs:
|
|
270
|
+
from .xy import xy_factory
|
|
271
|
+
|
|
272
|
+
assert not sets, sets
|
|
273
|
+
return xy_factory(args, kwargs).mutate()
|
|
274
|
+
|
|
275
|
+
if "x" in kwargs and "y" in kwargs:
|
|
276
|
+
from .xy import xy_factory
|
|
277
|
+
|
|
278
|
+
assert not sets, sets
|
|
279
|
+
return xy_factory(args, kwargs).mutate()
|
|
280
|
+
|
|
291
281
|
if "zip" in kwargs:
|
|
292
|
-
from .
|
|
282
|
+
from .xy import zip_factory
|
|
293
283
|
|
|
294
284
|
assert not sets, sets
|
|
295
|
-
return zip_factory(args, kwargs)
|
|
285
|
+
return zip_factory(args, kwargs).mutate()
|
|
296
286
|
|
|
297
287
|
if "chain" in kwargs:
|
|
298
288
|
from .unchecked import chain_factory
|
|
299
289
|
|
|
300
290
|
assert not sets, sets
|
|
301
|
-
return chain_factory(args, kwargs)
|
|
291
|
+
return chain_factory(args, kwargs).mutate()
|
|
302
292
|
|
|
303
293
|
if "join" in kwargs:
|
|
304
294
|
from .join import join_factory
|
|
305
295
|
|
|
306
296
|
assert not sets, sets
|
|
307
|
-
return join_factory(args, kwargs)
|
|
297
|
+
return join_factory(args, kwargs).mutate()
|
|
308
298
|
|
|
309
299
|
if "concat" in kwargs:
|
|
310
300
|
from .concat import concat_factory
|
|
311
301
|
|
|
312
302
|
assert not sets, sets
|
|
313
|
-
return concat_factory(args, kwargs)
|
|
303
|
+
return concat_factory(args, kwargs).mutate()
|
|
314
304
|
|
|
315
305
|
if "ensemble" in kwargs:
|
|
316
306
|
from .ensemble import ensemble_factory
|
|
317
307
|
|
|
318
308
|
assert not sets, sets
|
|
319
|
-
return ensemble_factory(args, kwargs)
|
|
309
|
+
return ensemble_factory(args, kwargs).mutate()
|
|
320
310
|
|
|
321
311
|
if "grids" in kwargs:
|
|
322
312
|
from .grids import grids_factory
|
|
323
313
|
|
|
324
314
|
assert not sets, sets
|
|
325
|
-
return grids_factory(args, kwargs)
|
|
315
|
+
return grids_factory(args, kwargs).mutate()
|
|
326
316
|
|
|
327
317
|
if "cutout" in kwargs:
|
|
328
318
|
from .grids import cutout_factory
|
|
329
319
|
|
|
330
320
|
assert not sets, sets
|
|
331
|
-
return cutout_factory(args, kwargs)
|
|
321
|
+
return cutout_factory(args, kwargs).mutate()
|
|
332
322
|
|
|
333
323
|
for name in ("datasets", "dataset"):
|
|
334
324
|
if name in kwargs:
|