anemoi-datasets 0.5.15__py3-none-any.whl → 0.5.17__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 +4 -1
- anemoi/datasets/__main__.py +12 -2
- anemoi/datasets/_version.py +9 -4
- anemoi/datasets/commands/cleanup.py +17 -2
- anemoi/datasets/commands/compare.py +18 -2
- anemoi/datasets/commands/copy.py +196 -14
- anemoi/datasets/commands/create.py +50 -7
- anemoi/datasets/commands/finalise-additions.py +17 -2
- anemoi/datasets/commands/finalise.py +17 -2
- anemoi/datasets/commands/init-additions.py +17 -2
- anemoi/datasets/commands/init.py +16 -2
- anemoi/datasets/commands/inspect.py +283 -62
- anemoi/datasets/commands/load-additions.py +16 -2
- anemoi/datasets/commands/load.py +16 -2
- anemoi/datasets/commands/patch.py +17 -2
- anemoi/datasets/commands/publish.py +17 -2
- anemoi/datasets/commands/scan.py +31 -3
- anemoi/datasets/compute/recentre.py +47 -11
- anemoi/datasets/create/__init__.py +612 -85
- anemoi/datasets/create/check.py +142 -20
- anemoi/datasets/create/chunks.py +64 -4
- anemoi/datasets/create/config.py +185 -21
- anemoi/datasets/create/filter.py +50 -0
- anemoi/datasets/create/filters/__init__.py +33 -0
- anemoi/datasets/create/filters/empty.py +37 -0
- anemoi/datasets/create/filters/legacy.py +93 -0
- anemoi/datasets/create/filters/noop.py +37 -0
- anemoi/datasets/create/filters/orog_to_z.py +58 -0
- anemoi/datasets/create/{functions/filters → filters}/pressure_level_relative_humidity_to_specific_humidity.py +33 -10
- anemoi/datasets/create/{functions/filters → filters}/pressure_level_specific_humidity_to_relative_humidity.py +32 -8
- anemoi/datasets/create/filters/rename.py +205 -0
- anemoi/datasets/create/{functions/filters → filters}/rotate_winds.py +43 -28
- anemoi/datasets/create/{functions/filters → filters}/single_level_dewpoint_to_relative_humidity.py +32 -9
- anemoi/datasets/create/{functions/filters → filters}/single_level_relative_humidity_to_dewpoint.py +33 -9
- anemoi/datasets/create/{functions/filters → filters}/single_level_relative_humidity_to_specific_humidity.py +55 -7
- anemoi/datasets/create/{functions/filters → filters}/single_level_specific_humidity_to_relative_humidity.py +98 -37
- anemoi/datasets/create/filters/speeddir_to_uv.py +95 -0
- anemoi/datasets/create/{functions/filters → filters}/sum.py +24 -27
- anemoi/datasets/create/filters/transform.py +53 -0
- anemoi/datasets/create/{functions/filters → filters}/unrotate_winds.py +27 -18
- anemoi/datasets/create/filters/uv_to_speeddir.py +94 -0
- anemoi/datasets/create/{functions/filters → filters}/wz_to_w.py +51 -33
- anemoi/datasets/create/input/__init__.py +76 -5
- anemoi/datasets/create/input/action.py +149 -13
- anemoi/datasets/create/input/concat.py +81 -10
- anemoi/datasets/create/input/context.py +39 -4
- anemoi/datasets/create/input/data_sources.py +72 -6
- anemoi/datasets/create/input/empty.py +21 -3
- anemoi/datasets/create/input/filter.py +60 -12
- anemoi/datasets/create/input/function.py +154 -37
- anemoi/datasets/create/input/join.py +86 -14
- anemoi/datasets/create/input/misc.py +67 -17
- anemoi/datasets/create/input/pipe.py +33 -6
- anemoi/datasets/create/input/repeated_dates.py +189 -41
- anemoi/datasets/create/input/result.py +202 -87
- anemoi/datasets/create/input/step.py +119 -22
- anemoi/datasets/create/input/template.py +100 -13
- anemoi/datasets/create/input/trace.py +62 -7
- anemoi/datasets/create/patch.py +52 -4
- anemoi/datasets/create/persistent.py +134 -17
- anemoi/datasets/create/size.py +15 -1
- anemoi/datasets/create/source.py +51 -0
- anemoi/datasets/create/sources/__init__.py +36 -0
- anemoi/datasets/create/{functions/sources → sources}/accumulations.py +296 -30
- anemoi/datasets/create/{functions/sources → sources}/constants.py +27 -2
- anemoi/datasets/create/{functions/sources → sources}/eccc_fstd.py +7 -3
- anemoi/datasets/create/sources/empty.py +37 -0
- anemoi/datasets/create/{functions/sources → sources}/forcings.py +25 -1
- anemoi/datasets/create/sources/grib.py +297 -0
- anemoi/datasets/create/{functions/sources → sources}/hindcasts.py +38 -4
- anemoi/datasets/create/sources/legacy.py +93 -0
- anemoi/datasets/create/{functions/sources → sources}/mars.py +168 -20
- anemoi/datasets/create/sources/netcdf.py +42 -0
- anemoi/datasets/create/sources/opendap.py +43 -0
- anemoi/datasets/create/{functions/sources/__init__.py → sources/patterns.py} +35 -4
- anemoi/datasets/create/sources/recentre.py +150 -0
- anemoi/datasets/create/{functions/sources → sources}/source.py +27 -5
- anemoi/datasets/create/{functions/sources → sources}/tendencies.py +64 -7
- anemoi/datasets/create/sources/xarray.py +92 -0
- anemoi/datasets/create/sources/xarray_kerchunk.py +36 -0
- anemoi/datasets/create/sources/xarray_support/README.md +1 -0
- anemoi/datasets/create/{functions/sources/xarray → sources/xarray_support}/__init__.py +109 -8
- anemoi/datasets/create/sources/xarray_support/coordinates.py +442 -0
- anemoi/datasets/create/{functions/sources/xarray → sources/xarray_support}/field.py +94 -16
- anemoi/datasets/create/{functions/sources/xarray → sources/xarray_support}/fieldlist.py +90 -25
- anemoi/datasets/create/sources/xarray_support/flavour.py +1036 -0
- anemoi/datasets/create/{functions/sources/xarray → sources/xarray_support}/grid.py +92 -31
- anemoi/datasets/create/sources/xarray_support/metadata.py +395 -0
- anemoi/datasets/create/sources/xarray_support/patch.py +91 -0
- anemoi/datasets/create/sources/xarray_support/time.py +391 -0
- anemoi/datasets/create/sources/xarray_support/variable.py +331 -0
- anemoi/datasets/create/sources/xarray_zarr.py +41 -0
- anemoi/datasets/create/{functions/sources → sources}/zenodo.py +34 -5
- anemoi/datasets/create/statistics/__init__.py +233 -44
- anemoi/datasets/create/statistics/summary.py +52 -6
- anemoi/datasets/create/testing.py +76 -0
- anemoi/datasets/create/{functions/filters/noop.py → typing.py} +6 -3
- anemoi/datasets/create/utils.py +97 -6
- anemoi/datasets/create/writer.py +26 -4
- anemoi/datasets/create/zarr.py +170 -23
- anemoi/datasets/data/__init__.py +51 -4
- anemoi/datasets/data/complement.py +191 -40
- anemoi/datasets/data/concat.py +141 -16
- anemoi/datasets/data/dataset.py +552 -61
- anemoi/datasets/data/debug.py +197 -26
- anemoi/datasets/data/ensemble.py +93 -8
- anemoi/datasets/data/fill_missing.py +165 -18
- anemoi/datasets/data/forwards.py +428 -56
- anemoi/datasets/data/grids.py +323 -97
- anemoi/datasets/data/indexing.py +112 -19
- anemoi/datasets/data/interpolate.py +92 -12
- anemoi/datasets/data/join.py +158 -19
- anemoi/datasets/data/masked.py +129 -15
- anemoi/datasets/data/merge.py +137 -23
- anemoi/datasets/data/misc.py +172 -16
- anemoi/datasets/data/missing.py +233 -29
- anemoi/datasets/data/rescale.py +111 -10
- anemoi/datasets/data/select.py +168 -26
- anemoi/datasets/data/statistics.py +67 -6
- anemoi/datasets/data/stores.py +149 -64
- anemoi/datasets/data/subset.py +159 -25
- anemoi/datasets/data/unchecked.py +168 -57
- anemoi/datasets/data/xy.py +168 -25
- anemoi/datasets/dates/__init__.py +191 -16
- anemoi/datasets/dates/groups.py +189 -47
- anemoi/datasets/grids.py +270 -31
- anemoi/datasets/testing.py +28 -1
- {anemoi_datasets-0.5.15.dist-info → anemoi_datasets-0.5.17.dist-info}/METADATA +10 -7
- anemoi_datasets-0.5.17.dist-info/RECORD +137 -0
- {anemoi_datasets-0.5.15.dist-info → anemoi_datasets-0.5.17.dist-info}/WHEEL +1 -1
- {anemoi_datasets-0.5.15.dist-info → anemoi_datasets-0.5.17.dist-info/licenses}/LICENSE +1 -1
- anemoi/datasets/create/functions/__init__.py +0 -66
- anemoi/datasets/create/functions/filters/__init__.py +0 -9
- anemoi/datasets/create/functions/filters/empty.py +0 -17
- anemoi/datasets/create/functions/filters/orog_to_z.py +0 -58
- anemoi/datasets/create/functions/filters/rename.py +0 -79
- anemoi/datasets/create/functions/filters/speeddir_to_uv.py +0 -78
- anemoi/datasets/create/functions/filters/uv_to_speeddir.py +0 -56
- anemoi/datasets/create/functions/sources/empty.py +0 -15
- anemoi/datasets/create/functions/sources/grib.py +0 -150
- anemoi/datasets/create/functions/sources/netcdf.py +0 -15
- anemoi/datasets/create/functions/sources/opendap.py +0 -15
- anemoi/datasets/create/functions/sources/recentre.py +0 -60
- anemoi/datasets/create/functions/sources/xarray/coordinates.py +0 -255
- anemoi/datasets/create/functions/sources/xarray/flavour.py +0 -472
- anemoi/datasets/create/functions/sources/xarray/metadata.py +0 -148
- anemoi/datasets/create/functions/sources/xarray/patch.py +0 -44
- anemoi/datasets/create/functions/sources/xarray/time.py +0 -177
- anemoi/datasets/create/functions/sources/xarray/variable.py +0 -188
- anemoi/datasets/create/functions/sources/xarray_kerchunk.py +0 -42
- anemoi/datasets/create/functions/sources/xarray_zarr.py +0 -15
- anemoi/datasets/utils/fields.py +0 -47
- anemoi_datasets-0.5.15.dist-info/RECORD +0 -129
- {anemoi_datasets-0.5.15.dist-info → anemoi_datasets-0.5.17.dist-info}/entry_points.txt +0 -0
- {anemoi_datasets-0.5.15.dist-info → anemoi_datasets-0.5.17.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,391 @@
|
|
|
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 datetime
|
|
12
|
+
import logging
|
|
13
|
+
from abc import ABC
|
|
14
|
+
from abc import abstractmethod
|
|
15
|
+
from typing import Any
|
|
16
|
+
from typing import Dict
|
|
17
|
+
from typing import List
|
|
18
|
+
from typing import Optional
|
|
19
|
+
|
|
20
|
+
from anemoi.utils.dates import as_datetime
|
|
21
|
+
|
|
22
|
+
from .coordinates import Coordinate
|
|
23
|
+
from .variable import Variable
|
|
24
|
+
|
|
25
|
+
LOG = logging.getLogger(__name__)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class Time(ABC):
|
|
29
|
+
"""Base class for different time representations."""
|
|
30
|
+
|
|
31
|
+
@classmethod
|
|
32
|
+
def from_coordinates(cls, coordinates: List[Coordinate]) -> "Time":
|
|
33
|
+
"""Create a Time instance from a list of coordinates.
|
|
34
|
+
|
|
35
|
+
Returns
|
|
36
|
+
-------
|
|
37
|
+
Union[ForecastFromValidTimeAndStep, Analysis, Constant, ForecastFromValidTimeAndBaseTime, ForecastFromBaseTimeAndDate]
|
|
38
|
+
An instance of a subclass of Time.
|
|
39
|
+
|
|
40
|
+
Args
|
|
41
|
+
----
|
|
42
|
+
coordinates : List[Coordinate]
|
|
43
|
+
List of coordinate objects.
|
|
44
|
+
"""
|
|
45
|
+
time_coordinate = [c for c in coordinates if c.is_time]
|
|
46
|
+
step_coordinate = [c for c in coordinates if c.is_step]
|
|
47
|
+
date_coordinate = [c for c in coordinates if c.is_date]
|
|
48
|
+
|
|
49
|
+
if len(date_coordinate) == 0 and len(time_coordinate) == 1 and len(step_coordinate) == 1:
|
|
50
|
+
return ForecastFromValidTimeAndStep(time_coordinate[0], step_coordinate[0])
|
|
51
|
+
|
|
52
|
+
if len(date_coordinate) == 0 and len(time_coordinate) == 1 and len(step_coordinate) == 0:
|
|
53
|
+
return Analysis(time_coordinate[0])
|
|
54
|
+
|
|
55
|
+
if len(date_coordinate) == 0 and len(time_coordinate) == 0 and len(step_coordinate) == 0:
|
|
56
|
+
return Constant()
|
|
57
|
+
|
|
58
|
+
if len(date_coordinate) == 1 and len(time_coordinate) == 1 and len(step_coordinate) == 0:
|
|
59
|
+
return ForecastFromValidTimeAndBaseTime(date_coordinate[0], time_coordinate[0])
|
|
60
|
+
|
|
61
|
+
if len(date_coordinate) == 1 and len(time_coordinate) == 0 and len(step_coordinate) == 1:
|
|
62
|
+
return ForecastFromBaseTimeAndDate(date_coordinate[0], step_coordinate[0])
|
|
63
|
+
|
|
64
|
+
if len(date_coordinate) == 1 and len(time_coordinate) == 1 and len(step_coordinate) == 1:
|
|
65
|
+
return ForecastFromValidTimeAndStep(time_coordinate[0], step_coordinate[0], date_coordinate[0])
|
|
66
|
+
|
|
67
|
+
LOG.error("")
|
|
68
|
+
LOG.error(f"{len(date_coordinate)} date_coordinate")
|
|
69
|
+
for c in date_coordinate:
|
|
70
|
+
LOG.error(" %s %s %s %s", c, c.is_date, c.is_time, c.is_step)
|
|
71
|
+
# LOG.error(' %s', c.variable)
|
|
72
|
+
|
|
73
|
+
LOG.error("")
|
|
74
|
+
LOG.error(f"{len(time_coordinate)} time_coordinate")
|
|
75
|
+
for c in time_coordinate:
|
|
76
|
+
LOG.error(" %s %s %s %s", c, c.is_date, c.is_time, c.is_step)
|
|
77
|
+
# LOG.error(' %s', c.variable)
|
|
78
|
+
|
|
79
|
+
LOG.error("")
|
|
80
|
+
LOG.error(f"{len(step_coordinate)} step_coordinate")
|
|
81
|
+
for c in step_coordinate:
|
|
82
|
+
LOG.error(" %s %s %s %s", c, c.is_date, c.is_time, c.is_step)
|
|
83
|
+
# LOG.error(' %s', c.variable)
|
|
84
|
+
|
|
85
|
+
raise NotImplementedError(f"{len(date_coordinate)=} {len(time_coordinate)=} {len(step_coordinate)=}")
|
|
86
|
+
|
|
87
|
+
@abstractmethod
|
|
88
|
+
def select_valid_datetime(self, variable: Variable) -> Optional[str]:
|
|
89
|
+
"""Select the valid datetime for a given variable.
|
|
90
|
+
|
|
91
|
+
Parameters
|
|
92
|
+
----------
|
|
93
|
+
variable : Variable
|
|
94
|
+
The variable to select the datetime for.
|
|
95
|
+
|
|
96
|
+
Returns
|
|
97
|
+
-------
|
|
98
|
+
Optional[str]
|
|
99
|
+
The name of the time coordinate.
|
|
100
|
+
"""
|
|
101
|
+
pass
|
|
102
|
+
|
|
103
|
+
@abstractmethod
|
|
104
|
+
def fill_time_metadata(self, coords_values: Dict[str, Any], metadata: Dict[str, Any]) -> None:
|
|
105
|
+
"""Fill metadata with time information.
|
|
106
|
+
|
|
107
|
+
Args
|
|
108
|
+
----
|
|
109
|
+
coords_values : Dict[str, Any]
|
|
110
|
+
Coordinate values.
|
|
111
|
+
metadata : Dict[str, Any]
|
|
112
|
+
Metadata dictionary.
|
|
113
|
+
"""
|
|
114
|
+
|
|
115
|
+
pass
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
class Constant(Time):
|
|
119
|
+
"""Represents a constant time."""
|
|
120
|
+
|
|
121
|
+
def fill_time_metadata(self, coords_values: Dict[str, Any], metadata: Dict[str, Any]) -> None:
|
|
122
|
+
"""Fill metadata with time information.
|
|
123
|
+
|
|
124
|
+
Parameters
|
|
125
|
+
----------
|
|
126
|
+
coords_values : Dict[str, Any]
|
|
127
|
+
Coordinate values.
|
|
128
|
+
metadata : Dict[str, Any]
|
|
129
|
+
Metadata dictionary.
|
|
130
|
+
"""
|
|
131
|
+
return None
|
|
132
|
+
|
|
133
|
+
def select_valid_datetime(self, variable: Variable) -> None:
|
|
134
|
+
"""Select the valid datetime for a given variable.
|
|
135
|
+
|
|
136
|
+
Parameters
|
|
137
|
+
----------
|
|
138
|
+
variable : Variable
|
|
139
|
+
The variable to select the datetime for.
|
|
140
|
+
"""
|
|
141
|
+
return None
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
class Analysis(Time):
|
|
145
|
+
"""Represents an analysis time."""
|
|
146
|
+
|
|
147
|
+
def __init__(self, time_coordinate: Coordinate) -> None:
|
|
148
|
+
"""Initialize Analysis with a time coordinate.
|
|
149
|
+
|
|
150
|
+
Parameters
|
|
151
|
+
----------
|
|
152
|
+
time_coordinate : Coordinate
|
|
153
|
+
The time coordinate.
|
|
154
|
+
"""
|
|
155
|
+
self.time_coordinate_name = time_coordinate.variable.name
|
|
156
|
+
|
|
157
|
+
def fill_time_metadata(self, coords_values: Dict[str, Any], metadata: Dict[str, Any]) -> Any:
|
|
158
|
+
"""Fill metadata with time information.
|
|
159
|
+
|
|
160
|
+
Parameters
|
|
161
|
+
----------
|
|
162
|
+
coords_values : Dict[str, Any]
|
|
163
|
+
Coordinate values.
|
|
164
|
+
metadata : Dict[str, Any]
|
|
165
|
+
Metadata dictionary.
|
|
166
|
+
|
|
167
|
+
Returns
|
|
168
|
+
-------
|
|
169
|
+
Any
|
|
170
|
+
The valid datetime.
|
|
171
|
+
"""
|
|
172
|
+
valid_datetime = coords_values[self.time_coordinate_name]
|
|
173
|
+
|
|
174
|
+
metadata["date"] = as_datetime(valid_datetime).strftime("%Y%m%d")
|
|
175
|
+
metadata["time"] = as_datetime(valid_datetime).strftime("%H%M")
|
|
176
|
+
metadata["step"] = 0
|
|
177
|
+
|
|
178
|
+
return valid_datetime
|
|
179
|
+
|
|
180
|
+
def select_valid_datetime(self, variable: Variable) -> str:
|
|
181
|
+
"""Select the valid datetime for a given variable.
|
|
182
|
+
|
|
183
|
+
Parameters
|
|
184
|
+
----------
|
|
185
|
+
variable : Variable
|
|
186
|
+
The variable to select the datetime for.
|
|
187
|
+
|
|
188
|
+
Returns
|
|
189
|
+
-------
|
|
190
|
+
str
|
|
191
|
+
The name of the time coordinate.
|
|
192
|
+
"""
|
|
193
|
+
return self.time_coordinate_name
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
class ForecastFromValidTimeAndStep(Time):
|
|
197
|
+
"""Represents a forecast time derived from valid time and step."""
|
|
198
|
+
|
|
199
|
+
def __init__(
|
|
200
|
+
self, time_coordinate: Coordinate, step_coordinate: Coordinate, date_coordinate: Optional[Coordinate] = None
|
|
201
|
+
) -> None:
|
|
202
|
+
"""Initialize ForecastFromValidTimeAndStep with time, step, and optional date coordinates.
|
|
203
|
+
|
|
204
|
+
Args
|
|
205
|
+
----
|
|
206
|
+
time_coordinate : Coordinate
|
|
207
|
+
The time coordinate.
|
|
208
|
+
step_coordinate : Coordinate
|
|
209
|
+
The step coordinate.
|
|
210
|
+
date_coordinate : Optional[Coordinate]
|
|
211
|
+
The date coordinate.
|
|
212
|
+
"""
|
|
213
|
+
self.time_coordinate_name = time_coordinate.variable.name
|
|
214
|
+
self.step_coordinate_name = step_coordinate.variable.name
|
|
215
|
+
self.date_coordinate_name = date_coordinate.variable.name if date_coordinate else None
|
|
216
|
+
|
|
217
|
+
def fill_time_metadata(self, coords_values: Dict[str, Any], metadata: Dict[str, Any]) -> Any:
|
|
218
|
+
"""Fill metadata with time information.
|
|
219
|
+
|
|
220
|
+
Returns
|
|
221
|
+
-------
|
|
222
|
+
Any
|
|
223
|
+
The valid datetime.
|
|
224
|
+
|
|
225
|
+
Args
|
|
226
|
+
----
|
|
227
|
+
coords_values : Dict[str, Any]
|
|
228
|
+
Coordinate values.
|
|
229
|
+
metadata : Dict[str, Any]
|
|
230
|
+
Metadata dictionary.
|
|
231
|
+
"""
|
|
232
|
+
valid_datetime = coords_values[self.time_coordinate_name]
|
|
233
|
+
step = coords_values[self.step_coordinate_name]
|
|
234
|
+
|
|
235
|
+
assert isinstance(step, datetime.timedelta)
|
|
236
|
+
base_datetime = valid_datetime - step
|
|
237
|
+
|
|
238
|
+
hours = step.total_seconds() / 3600
|
|
239
|
+
assert int(hours) == hours
|
|
240
|
+
|
|
241
|
+
metadata["date"] = as_datetime(base_datetime).strftime("%Y%m%d")
|
|
242
|
+
metadata["time"] = as_datetime(base_datetime).strftime("%H%M")
|
|
243
|
+
metadata["step"] = int(hours)
|
|
244
|
+
|
|
245
|
+
# When date is present, it should be compatible with time and step
|
|
246
|
+
|
|
247
|
+
if self.date_coordinate_name is not None:
|
|
248
|
+
# Not sure that this is the correct assumption
|
|
249
|
+
assert coords_values[self.date_coordinate_name] == base_datetime, (
|
|
250
|
+
coords_values[self.date_coordinate_name],
|
|
251
|
+
base_datetime,
|
|
252
|
+
)
|
|
253
|
+
|
|
254
|
+
return valid_datetime
|
|
255
|
+
|
|
256
|
+
def select_valid_datetime(self, variable: Variable) -> str:
|
|
257
|
+
"""Select the valid datetime for a given variable.
|
|
258
|
+
|
|
259
|
+
Parameters
|
|
260
|
+
----------
|
|
261
|
+
variable : Variable
|
|
262
|
+
The variable to select the datetime for.
|
|
263
|
+
|
|
264
|
+
Returns
|
|
265
|
+
-------
|
|
266
|
+
str
|
|
267
|
+
The name of the time coordinate.
|
|
268
|
+
"""
|
|
269
|
+
return self.time_coordinate_name
|
|
270
|
+
|
|
271
|
+
|
|
272
|
+
class ForecastFromValidTimeAndBaseTime(Time):
|
|
273
|
+
"""Represents a forecast time derived from valid time and base time."""
|
|
274
|
+
|
|
275
|
+
def __init__(self, date_coordinate: Coordinate, time_coordinate: Coordinate) -> None:
|
|
276
|
+
"""Initialize ForecastFromValidTimeAndBaseTime with date and time coordinates.
|
|
277
|
+
|
|
278
|
+
Args
|
|
279
|
+
----
|
|
280
|
+
date_coordinate : Coordinate
|
|
281
|
+
The date coordinate.
|
|
282
|
+
time_coordinate : Coordinate
|
|
283
|
+
The time coordinate.
|
|
284
|
+
"""
|
|
285
|
+
self.date_coordinate_name = date_coordinate.name
|
|
286
|
+
self.time_coordinate_name = time_coordinate.name
|
|
287
|
+
|
|
288
|
+
def fill_time_metadata(self, coords_values: Dict[str, Any], metadata: Dict[str, Any]) -> Any:
|
|
289
|
+
"""Fill metadata with time information.
|
|
290
|
+
|
|
291
|
+
Returns
|
|
292
|
+
-------
|
|
293
|
+
Any
|
|
294
|
+
The valid datetime.
|
|
295
|
+
|
|
296
|
+
Args
|
|
297
|
+
----
|
|
298
|
+
coords_values : Dict[str, Any]
|
|
299
|
+
Coordinate values.
|
|
300
|
+
metadata : Dict[str, Any]
|
|
301
|
+
Metadata dictionary.
|
|
302
|
+
"""
|
|
303
|
+
valid_datetime = coords_values[self.time_coordinate_name]
|
|
304
|
+
base_datetime = coords_values[self.date_coordinate_name]
|
|
305
|
+
|
|
306
|
+
step = valid_datetime - base_datetime
|
|
307
|
+
|
|
308
|
+
hours = step.total_seconds() / 3600
|
|
309
|
+
assert int(hours) == hours
|
|
310
|
+
|
|
311
|
+
metadata["date"] = as_datetime(base_datetime).strftime("%Y%m%d")
|
|
312
|
+
metadata["time"] = as_datetime(base_datetime).strftime("%H%M")
|
|
313
|
+
metadata["step"] = int(hours)
|
|
314
|
+
|
|
315
|
+
return valid_datetime
|
|
316
|
+
|
|
317
|
+
def select_valid_datetime(self, variable: Variable) -> str:
|
|
318
|
+
"""Select the valid datetime for a given variable.
|
|
319
|
+
|
|
320
|
+
Parameters
|
|
321
|
+
----------
|
|
322
|
+
variable : Variable
|
|
323
|
+
The variable to select the datetime for.
|
|
324
|
+
|
|
325
|
+
Returns
|
|
326
|
+
-------
|
|
327
|
+
str
|
|
328
|
+
The name of the time coordinate.
|
|
329
|
+
"""
|
|
330
|
+
return self.time_coordinate_name
|
|
331
|
+
|
|
332
|
+
|
|
333
|
+
class ForecastFromBaseTimeAndDate(Time):
|
|
334
|
+
"""Represents a forecast time derived from base time and date."""
|
|
335
|
+
|
|
336
|
+
def __init__(self, date_coordinate: Coordinate, step_coordinate: Coordinate) -> None:
|
|
337
|
+
"""Initialize ForecastFromBaseTimeAndDate with date and step coordinates.
|
|
338
|
+
|
|
339
|
+
Args
|
|
340
|
+
----
|
|
341
|
+
date_coordinate : Coordinate
|
|
342
|
+
The date coordinate.
|
|
343
|
+
step_coordinate : Coordinate
|
|
344
|
+
The step coordinate.
|
|
345
|
+
"""
|
|
346
|
+
self.date_coordinate_name = date_coordinate.name
|
|
347
|
+
self.step_coordinate_name = step_coordinate.name
|
|
348
|
+
|
|
349
|
+
def fill_time_metadata(self, coords_values: Dict[str, Any], metadata: Dict[str, Any]) -> Any:
|
|
350
|
+
"""Fill metadata with time information.
|
|
351
|
+
|
|
352
|
+
Returns
|
|
353
|
+
-------
|
|
354
|
+
Any
|
|
355
|
+
The valid datetime.
|
|
356
|
+
|
|
357
|
+
Args
|
|
358
|
+
----
|
|
359
|
+
coords_values : Dict[str, Any]
|
|
360
|
+
Coordinate values.
|
|
361
|
+
metadata : Dict[str, Any]
|
|
362
|
+
Metadata dictionary.
|
|
363
|
+
"""
|
|
364
|
+
date = coords_values[self.date_coordinate_name]
|
|
365
|
+
step = coords_values[self.step_coordinate_name]
|
|
366
|
+
assert isinstance(step, datetime.timedelta)
|
|
367
|
+
|
|
368
|
+
metadata["date"] = as_datetime(date).strftime("%Y%m%d")
|
|
369
|
+
metadata["time"] = as_datetime(date).strftime("%H%M")
|
|
370
|
+
|
|
371
|
+
hours = step.total_seconds() / 3600
|
|
372
|
+
|
|
373
|
+
assert int(hours) == hours
|
|
374
|
+
metadata["step"] = int(hours)
|
|
375
|
+
|
|
376
|
+
return date + step
|
|
377
|
+
|
|
378
|
+
def select_valid_datetime(self, variable: Variable) -> Optional[str]:
|
|
379
|
+
"""Select the valid datetime for a given variable.
|
|
380
|
+
|
|
381
|
+
Parameters
|
|
382
|
+
----------
|
|
383
|
+
variable : Variable
|
|
384
|
+
The variable to select the datetime for.
|
|
385
|
+
|
|
386
|
+
Returns
|
|
387
|
+
-------
|
|
388
|
+
Optional[str]
|
|
389
|
+
The name of the time coordinate.
|
|
390
|
+
"""
|
|
391
|
+
raise NotImplementedError("ForecastFromBaseTimeAndDate.select_valid_datetime")
|