pycontrails 0.49.5__cp39-cp39-macosx_10_9_x86_64.whl → 0.50.0__cp39-cp39-macosx_10_9_x86_64.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.
Potentially problematic release.
This version of pycontrails might be problematic. Click here for more details.
- pycontrails/_version.py +2 -2
- pycontrails/core/datalib.py +60 -38
- pycontrails/core/met.py +4 -4
- pycontrails/core/met_var.py +2 -2
- pycontrails/core/models.py +7 -3
- pycontrails/core/rgi_cython.cpython-39-darwin.so +0 -0
- pycontrails/core/vector.py +5 -5
- pycontrails/datalib/ecmwf/__init__.py +4 -0
- pycontrails/datalib/ecmwf/arco_era5.py +577 -0
- pycontrails/datalib/ecmwf/common.py +1 -1
- pycontrails/datalib/ecmwf/era5.py +2 -5
- pycontrails/datalib/ecmwf/variables.py +18 -0
- pycontrails/datalib/gfs/gfs.py +2 -2
- pycontrails/models/cocip/cocip.py +26 -3
- pycontrails/models/cocip/cocip_params.py +11 -1
- pycontrails/models/cocipgrid/cocip_grid_params.py +25 -19
- pycontrails/physics/constants.py +6 -0
- pycontrails/utils/dependencies.py +13 -11
- {pycontrails-0.49.5.dist-info → pycontrails-0.50.0.dist-info}/METADATA +4 -2
- {pycontrails-0.49.5.dist-info → pycontrails-0.50.0.dist-info}/RECORD +24 -23
- {pycontrails-0.49.5.dist-info → pycontrails-0.50.0.dist-info}/WHEEL +1 -1
- {pycontrails-0.49.5.dist-info → pycontrails-0.50.0.dist-info}/LICENSE +0 -0
- {pycontrails-0.49.5.dist-info → pycontrails-0.50.0.dist-info}/NOTICE +0 -0
- {pycontrails-0.49.5.dist-info → pycontrails-0.50.0.dist-info}/top_level.txt +0 -0
pycontrails/_version.py
CHANGED
pycontrails/core/datalib.py
CHANGED
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
import abc
|
|
6
|
-
import dataclasses
|
|
7
6
|
import hashlib
|
|
8
7
|
import logging
|
|
9
8
|
import pathlib
|
|
@@ -109,6 +108,11 @@ def parse_pressure_levels(
|
|
|
109
108
|
) -> list[int]:
|
|
110
109
|
"""Check input pressure levels are consistent type and ensure levels exist in ECMWF data source.
|
|
111
110
|
|
|
111
|
+
.. versionchanged:: 0.50.0
|
|
112
|
+
|
|
113
|
+
The returned pressure levels are now sorted. Pressure levels must be unique.
|
|
114
|
+
Raises ValueError if pressure levels have mixed signs.
|
|
115
|
+
|
|
112
116
|
Parameters
|
|
113
117
|
----------
|
|
114
118
|
pressure_levels : PressureLevelInput
|
|
@@ -127,18 +131,31 @@ def parse_pressure_levels(
|
|
|
127
131
|
ValueError
|
|
128
132
|
Raises ValueError if pressure level is not supported by ECMWF data source
|
|
129
133
|
"""
|
|
130
|
-
#
|
|
134
|
+
# Ensure pressure_levels is array-like
|
|
131
135
|
if isinstance(pressure_levels, (int, float)):
|
|
132
136
|
pressure_levels = [pressure_levels]
|
|
133
137
|
|
|
134
|
-
# Cast array-like to
|
|
135
|
-
|
|
138
|
+
# Cast array-like to int dtype and sort
|
|
139
|
+
arr = np.asarray(pressure_levels, dtype=int)
|
|
140
|
+
arr.sort()
|
|
136
141
|
|
|
137
|
-
#
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
+
# If any values are non-positive, the entire array should be [-1]
|
|
143
|
+
if np.any(arr <= 0) and not np.array_equal(arr, [-1]):
|
|
144
|
+
msg = f"Pressure levels must be all positive or all -1, got {arr}"
|
|
145
|
+
raise ValueError(msg)
|
|
146
|
+
|
|
147
|
+
# Ensure pressure levels are unique
|
|
148
|
+
if np.any(np.diff(arr) == 0):
|
|
149
|
+
msg = f"Pressure levels must be unique, got {arr}"
|
|
150
|
+
raise ValueError(msg)
|
|
151
|
+
|
|
152
|
+
out = arr.tolist()
|
|
153
|
+
if supported is None:
|
|
154
|
+
return out
|
|
155
|
+
|
|
156
|
+
if missing := set(out).difference(supported):
|
|
157
|
+
msg = f"Pressure levels {sorted(missing)} are not supported. Supported levels: {supported}"
|
|
158
|
+
raise ValueError(msg)
|
|
142
159
|
|
|
143
160
|
return out
|
|
144
161
|
|
|
@@ -146,6 +163,11 @@ def parse_pressure_levels(
|
|
|
146
163
|
def parse_variables(variables: VariableInput, supported: list[MetVariable]) -> list[MetVariable]:
|
|
147
164
|
"""Parse input variables.
|
|
148
165
|
|
|
166
|
+
.. versionchanged:: 0.50.0
|
|
167
|
+
|
|
168
|
+
The output is no longer copied. Each :class:`MetVariable` is a frozen dataclass,
|
|
169
|
+
so copying is unnecessary.
|
|
170
|
+
|
|
149
171
|
Parameters
|
|
150
172
|
----------
|
|
151
173
|
variables : VariableInput
|
|
@@ -178,35 +200,31 @@ def parse_variables(variables: VariableInput, supported: list[MetVariable]) -> l
|
|
|
178
200
|
else:
|
|
179
201
|
parsed_variables = variables
|
|
180
202
|
|
|
181
|
-
# unpack dict of supported str values from supported
|
|
182
203
|
short_names = {v.short_name: v for v in supported}
|
|
183
204
|
standard_names = {v.standard_name: v for v in supported}
|
|
184
205
|
long_names = {v.long_name: v for v in supported}
|
|
185
|
-
|
|
186
|
-
# unpack dict of support int values from supported
|
|
187
206
|
ecmwf_ids = {v.ecmwf_id: v for v in supported}
|
|
188
207
|
grib1_ids = {v.grib1_id: v for v in supported}
|
|
208
|
+
supported_set = set(supported)
|
|
189
209
|
|
|
190
210
|
for var in parsed_variables:
|
|
191
211
|
matched = _find_match(
|
|
192
212
|
var,
|
|
193
|
-
|
|
213
|
+
supported_set,
|
|
194
214
|
ecmwf_ids, # type: ignore[arg-type]
|
|
195
215
|
grib1_ids, # type: ignore[arg-type]
|
|
196
216
|
short_names,
|
|
197
217
|
standard_names,
|
|
198
218
|
long_names, # type: ignore[arg-type]
|
|
199
219
|
)
|
|
200
|
-
|
|
201
|
-
# "replace" copies dataclass
|
|
202
|
-
met_var_list.append(dataclasses.replace(matched))
|
|
220
|
+
met_var_list.append(matched)
|
|
203
221
|
|
|
204
222
|
return met_var_list
|
|
205
223
|
|
|
206
224
|
|
|
207
225
|
def _find_match(
|
|
208
226
|
var: VariableInput,
|
|
209
|
-
supported:
|
|
227
|
+
supported: set[MetVariable],
|
|
210
228
|
ecmwf_ids: dict[int, MetVariable],
|
|
211
229
|
grib1_ids: dict[int, MetVariable],
|
|
212
230
|
short_names: dict[str, MetVariable],
|
|
@@ -215,9 +233,8 @@ def _find_match(
|
|
|
215
233
|
) -> MetVariable:
|
|
216
234
|
"""Find a match for input variable in supported."""
|
|
217
235
|
|
|
218
|
-
if isinstance(var, MetVariable):
|
|
219
|
-
|
|
220
|
-
return var
|
|
236
|
+
if isinstance(var, MetVariable) and var in supported:
|
|
237
|
+
return var
|
|
221
238
|
|
|
222
239
|
# list of MetVariable options
|
|
223
240
|
# here we extract the first MetVariable in var that is supported
|
|
@@ -230,21 +247,19 @@ def _find_match(
|
|
|
230
247
|
if v in supported:
|
|
231
248
|
return v
|
|
232
249
|
|
|
233
|
-
# int code
|
|
234
250
|
elif isinstance(var, int):
|
|
235
|
-
if
|
|
236
|
-
return
|
|
237
|
-
if
|
|
238
|
-
return
|
|
251
|
+
if ret := ecmwf_ids.get(var):
|
|
252
|
+
return ret
|
|
253
|
+
if ret := grib1_ids.get(var):
|
|
254
|
+
return ret
|
|
239
255
|
|
|
240
|
-
# string reference
|
|
241
256
|
elif isinstance(var, str):
|
|
242
|
-
if
|
|
243
|
-
return
|
|
244
|
-
if
|
|
245
|
-
return
|
|
246
|
-
if
|
|
247
|
-
return
|
|
257
|
+
if ret := short_names.get(var):
|
|
258
|
+
return ret
|
|
259
|
+
if ret := standard_names.get(var):
|
|
260
|
+
return ret
|
|
261
|
+
if ret := long_names.get(var):
|
|
262
|
+
return ret
|
|
248
263
|
|
|
249
264
|
msg = f"{var} is not in supported parameters. Supported parameters include: {standard_names}"
|
|
250
265
|
raise ValueError(msg)
|
|
@@ -395,6 +410,14 @@ class MetDataSource(abc.ABC):
|
|
|
395
410
|
"""
|
|
396
411
|
return [v.standard_name for v in self.variables]
|
|
397
412
|
|
|
413
|
+
@property
|
|
414
|
+
def is_single_level(self) -> bool:
|
|
415
|
+
"""Return True if the datasource is single level data.
|
|
416
|
+
|
|
417
|
+
.. versionadded:: 0.50.0
|
|
418
|
+
"""
|
|
419
|
+
return self.pressure_levels == [-1]
|
|
420
|
+
|
|
398
421
|
@property
|
|
399
422
|
def pressure_level_variables(self) -> list[MetVariable]:
|
|
400
423
|
"""Parameters available from data source.
|
|
@@ -426,10 +449,9 @@ class MetDataSource(abc.ABC):
|
|
|
426
449
|
list[MetVariable] | None
|
|
427
450
|
List of MetVariable available in datasource
|
|
428
451
|
"""
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
return self.single_level_variables
|
|
452
|
+
return (
|
|
453
|
+
self.single_level_variables if self.is_single_level else self.pressure_level_variables
|
|
454
|
+
)
|
|
433
455
|
|
|
434
456
|
@property
|
|
435
457
|
def supported_pressure_levels(self) -> list[int] | None:
|
|
@@ -497,7 +519,7 @@ class MetDataSource(abc.ABC):
|
|
|
497
519
|
def open_metdataset(
|
|
498
520
|
self,
|
|
499
521
|
dataset: xr.Dataset | None = None,
|
|
500
|
-
xr_kwargs: dict[str,
|
|
522
|
+
xr_kwargs: dict[str, Any] | None = None,
|
|
501
523
|
**kwargs: Any,
|
|
502
524
|
) -> MetDataset:
|
|
503
525
|
"""Open MetDataset from data source.
|
|
@@ -510,7 +532,7 @@ class MetDataSource(abc.ABC):
|
|
|
510
532
|
dataset : xr.Dataset | None, optional
|
|
511
533
|
Input :class:`xr.Dataset` loaded manually.
|
|
512
534
|
The dataset must have the same format as the original data source API or files.
|
|
513
|
-
xr_kwargs : dict[str,
|
|
535
|
+
xr_kwargs : dict[str, Any] | None, optional
|
|
514
536
|
Dictionary of keyword arguments passed into :func:`xarray.open_mfdataset`
|
|
515
537
|
when opening files. Examples include "chunks", "engine", "parallel", etc.
|
|
516
538
|
Ignored if ``dataset`` is input.
|
pycontrails/core/met.py
CHANGED
|
@@ -1065,8 +1065,8 @@ class MetDataset(MetBase):
|
|
|
1065
1065
|
|
|
1066
1066
|
"""
|
|
1067
1067
|
coords_keys = self.data.dims
|
|
1068
|
-
|
|
1069
|
-
coords_vals = [
|
|
1068
|
+
indexes = self.indexes
|
|
1069
|
+
coords_vals = [indexes[key].values for key in coords_keys]
|
|
1070
1070
|
coords_meshes = np.meshgrid(*coords_vals, indexing="ij")
|
|
1071
1071
|
raveled_coords = (mesh.ravel() for mesh in coords_meshes)
|
|
1072
1072
|
data = dict(zip(coords_keys, raveled_coords))
|
|
@@ -1181,12 +1181,12 @@ class MetDataset(MetBase):
|
|
|
1181
1181
|
level: npt.ArrayLike | float,
|
|
1182
1182
|
time: npt.ArrayLike | np.datetime64,
|
|
1183
1183
|
) -> MetDataset:
|
|
1184
|
-
"""Create a :class:`MetDataset` containing a coordinate skeleton from coordinate arrays.
|
|
1184
|
+
r"""Create a :class:`MetDataset` containing a coordinate skeleton from coordinate arrays.
|
|
1185
1185
|
|
|
1186
1186
|
Parameters
|
|
1187
1187
|
----------
|
|
1188
1188
|
longitude, latitude : npt.ArrayLike | float
|
|
1189
|
-
Horizontal coordinates, in [:math
|
|
1189
|
+
Horizontal coordinates, in [:math:`\deg`]
|
|
1190
1190
|
level : npt.ArrayLike | float
|
|
1191
1191
|
Vertical coordinate, in [:math:`hPa`]
|
|
1192
1192
|
time: npt.ArrayLike | np.datetime64,
|
pycontrails/core/met_var.py
CHANGED
|
@@ -76,12 +76,12 @@ class MetVariable:
|
|
|
76
76
|
|
|
77
77
|
@property
|
|
78
78
|
def ecmwf_link(self) -> str | None:
|
|
79
|
-
"""Database link in the ECMWF
|
|
79
|
+
"""Database link in the ECMWF Parameter Database if :attr:`ecmwf_id` is defined.
|
|
80
80
|
|
|
81
81
|
Returns
|
|
82
82
|
-------
|
|
83
83
|
str
|
|
84
|
-
Database link in the ECMWF
|
|
84
|
+
Database link in the ECMWF Parameter Database
|
|
85
85
|
"""
|
|
86
86
|
return (
|
|
87
87
|
f"https://apps.ecmwf.int/codes/grib/param-db?id={self.ecmwf_id}"
|
pycontrails/core/models.py
CHANGED
|
@@ -440,17 +440,21 @@ class Model(ABC):
|
|
|
440
440
|
# Return dataset with the same coords as self.met, but empty data_vars
|
|
441
441
|
return MetDataset(xr.Dataset(coords=self.met.data.coords))
|
|
442
442
|
|
|
443
|
+
copy_source = self.params["copy_source"]
|
|
444
|
+
|
|
443
445
|
# Turn Sequence into Fleet
|
|
444
446
|
if isinstance(source, Sequence):
|
|
445
|
-
|
|
446
|
-
|
|
447
|
+
if not copy_source:
|
|
448
|
+
msg = "Parameter copy_source=False is not supported for Sequence[Flight] source"
|
|
449
|
+
raise ValueError(msg)
|
|
450
|
+
return Fleet.from_seq(source)
|
|
447
451
|
|
|
448
452
|
# Raise error if source is not a MetDataset or GeoVectorDataset
|
|
449
453
|
if not isinstance(source, (MetDataset, GeoVectorDataset)):
|
|
450
454
|
msg = f"Unknown source type: {type(source)}"
|
|
451
455
|
raise TypeError(msg)
|
|
452
456
|
|
|
453
|
-
if
|
|
457
|
+
if copy_source:
|
|
454
458
|
source = source.copy()
|
|
455
459
|
|
|
456
460
|
if not isinstance(source, Flight):
|
|
Binary file
|
pycontrails/core/vector.py
CHANGED
|
@@ -1899,19 +1899,19 @@ class GeoVectorDataset(VectorDataset):
|
|
|
1899
1899
|
MetDataset | MetDataArray
|
|
1900
1900
|
Copy of downselected MetDataset or MetDataArray.
|
|
1901
1901
|
"""
|
|
1902
|
-
|
|
1902
|
+
indexes = met.indexes
|
|
1903
1903
|
lon_slice = coordinates.slice_domain(
|
|
1904
|
-
|
|
1904
|
+
indexes["longitude"].to_numpy(),
|
|
1905
1905
|
self["longitude"],
|
|
1906
1906
|
buffer=longitude_buffer,
|
|
1907
1907
|
)
|
|
1908
1908
|
lat_slice = coordinates.slice_domain(
|
|
1909
|
-
|
|
1909
|
+
indexes["latitude"].to_numpy(),
|
|
1910
1910
|
self["latitude"],
|
|
1911
1911
|
buffer=latitude_buffer,
|
|
1912
1912
|
)
|
|
1913
1913
|
time_slice = coordinates.slice_domain(
|
|
1914
|
-
|
|
1914
|
+
indexes["time"].to_numpy(),
|
|
1915
1915
|
self["time"],
|
|
1916
1916
|
buffer=time_buffer,
|
|
1917
1917
|
)
|
|
@@ -1921,7 +1921,7 @@ class GeoVectorDataset(VectorDataset):
|
|
|
1921
1921
|
level_slice = slice(None)
|
|
1922
1922
|
else:
|
|
1923
1923
|
level_slice = coordinates.slice_domain(
|
|
1924
|
-
|
|
1924
|
+
indexes["level"].to_numpy(),
|
|
1925
1925
|
self.level,
|
|
1926
1926
|
buffer=level_buffer,
|
|
1927
1927
|
)
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
+
from pycontrails.datalib.ecmwf.arco_era5 import ARCOERA5
|
|
5
6
|
from pycontrails.datalib.ecmwf.era5 import ERA5
|
|
6
7
|
from pycontrails.datalib.ecmwf.hres import HRES
|
|
7
8
|
from pycontrails.datalib.ecmwf.ifs import IFS
|
|
@@ -11,6 +12,7 @@ from pycontrails.datalib.ecmwf.variables import (
|
|
|
11
12
|
SURFACE_VARIABLES,
|
|
12
13
|
CloudAreaFraction,
|
|
13
14
|
CloudAreaFractionInLayer,
|
|
15
|
+
Divergence,
|
|
14
16
|
PotentialVorticity,
|
|
15
17
|
RelativeHumidity,
|
|
16
18
|
RelativeVorticity,
|
|
@@ -23,11 +25,13 @@ from pycontrails.datalib.ecmwf.variables import (
|
|
|
23
25
|
)
|
|
24
26
|
|
|
25
27
|
__all__ = [
|
|
28
|
+
"ARCOERA5",
|
|
26
29
|
"ERA5",
|
|
27
30
|
"HRES",
|
|
28
31
|
"IFS",
|
|
29
32
|
"CloudAreaFraction",
|
|
30
33
|
"CloudAreaFractionInLayer",
|
|
34
|
+
"Divergence",
|
|
31
35
|
"PotentialVorticity",
|
|
32
36
|
"RelativeHumidity",
|
|
33
37
|
"RelativeVorticity",
|