pycontrails 0.50.2__cp39-cp39-macosx_10_9_x86_64.whl → 0.51.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 +22 -0
- pycontrails/core/flight.py +43 -1
- pycontrails/core/met.py +33 -5
- pycontrails/core/polygon.py +10 -3
- pycontrails/core/rgi_cython.cpython-39-darwin.so +0 -0
- pycontrails/datalib/ecmwf/__init__.py +6 -0
- pycontrails/datalib/ecmwf/arco_era5.py +2 -53
- pycontrails/datalib/ecmwf/common.py +4 -0
- pycontrails/datalib/ecmwf/era5.py +2 -6
- pycontrails/datalib/ecmwf/era5_model_level.py +481 -0
- pycontrails/datalib/ecmwf/hres_model_level.py +494 -0
- pycontrails/datalib/ecmwf/model_levels.py +79 -0
- pycontrails/datalib/ecmwf/static/model_level_dataframe_v20240418.csv +139 -0
- pycontrails/datalib/ecmwf/variables.py +12 -0
- pycontrails/models/humidity_scaling/humidity_scaling.py +55 -8
- pycontrails/models/humidity_scaling/quantiles/era5-model-level-quantiles.pq +0 -0
- pycontrails/physics/thermo.py +1 -1
- pycontrails/utils/types.py +3 -2
- {pycontrails-0.50.2.dist-info → pycontrails-0.51.0.dist-info}/METADATA +4 -4
- {pycontrails-0.50.2.dist-info → pycontrails-0.51.0.dist-info}/RECORD +26 -21
- /pycontrails/models/humidity_scaling/quantiles/{era5-quantiles.pq → era5-pressure-level-quantiles.pq} +0 -0
- {pycontrails-0.50.2.dist-info → pycontrails-0.51.0.dist-info}/LICENSE +0 -0
- {pycontrails-0.50.2.dist-info → pycontrails-0.51.0.dist-info}/NOTICE +0 -0
- {pycontrails-0.50.2.dist-info → pycontrails-0.51.0.dist-info}/WHEEL +0 -0
- {pycontrails-0.50.2.dist-info → pycontrails-0.51.0.dist-info}/top_level.txt +0 -0
pycontrails/_version.py
CHANGED
pycontrails/core/datalib.py
CHANGED
|
@@ -103,6 +103,28 @@ def parse_timesteps(time: TimeInput | None, freq: str | None = "1h") -> list[dat
|
|
|
103
103
|
return daterange.to_pydatetime().tolist()
|
|
104
104
|
|
|
105
105
|
|
|
106
|
+
def validate_timestep_freq(freq: str, datasource_freq: str) -> bool:
|
|
107
|
+
"""Check that input timestep frequency is compatible with the data source timestep frequency.
|
|
108
|
+
|
|
109
|
+
A data source timestep frequency of 1 hour allows input timestep frequencies of
|
|
110
|
+
1 hour, 2 hours, 3 hours, etc., but not 1.5 hours or 30 minutes.
|
|
111
|
+
|
|
112
|
+
Parameters
|
|
113
|
+
----------
|
|
114
|
+
freq : str
|
|
115
|
+
Input timestep frequency
|
|
116
|
+
datasource_freq : str
|
|
117
|
+
Datasource timestep frequency
|
|
118
|
+
|
|
119
|
+
Returns
|
|
120
|
+
-------
|
|
121
|
+
bool
|
|
122
|
+
True if the input timestep frequency is an even multiple
|
|
123
|
+
of the data source timestep frequency.
|
|
124
|
+
"""
|
|
125
|
+
return pd.Timedelta(freq) % pd.Timedelta(datasource_freq) == pd.Timedelta(0)
|
|
126
|
+
|
|
127
|
+
|
|
106
128
|
def parse_pressure_levels(
|
|
107
129
|
pressure_levels: PressureLevelInput, supported: list[int] | None = None
|
|
108
130
|
) -> list[int]:
|
pycontrails/core/flight.py
CHANGED
|
@@ -447,6 +447,48 @@ class Flight(GeoVectorDataset):
|
|
|
447
447
|
|
|
448
448
|
return segment_duration(self.data["time"], dtype=dtype)
|
|
449
449
|
|
|
450
|
+
def segment_haversine(self) -> npt.NDArray[np.float64]:
|
|
451
|
+
"""Compute Haversine (great circle) distance between flight waypoints.
|
|
452
|
+
|
|
453
|
+
Helper function used in :meth:`resample_and_fill`.
|
|
454
|
+
`np.nan` appended so the length of the output is the same as number of waypoints.
|
|
455
|
+
|
|
456
|
+
To account for vertical displacements when computing segment lengths,
|
|
457
|
+
use :meth:`segment_length`.
|
|
458
|
+
|
|
459
|
+
Returns
|
|
460
|
+
-------
|
|
461
|
+
npt.NDArray[np.float64]
|
|
462
|
+
Array of great circle distances in [:math:`m`] between waypoints
|
|
463
|
+
|
|
464
|
+
Raises
|
|
465
|
+
------
|
|
466
|
+
NotImplementedError
|
|
467
|
+
Raises when attr:`attrs["crs"]` is not EPSG:4326
|
|
468
|
+
|
|
469
|
+
Examples
|
|
470
|
+
--------
|
|
471
|
+
>>> from pycontrails import Flight
|
|
472
|
+
>>> fl = Flight(
|
|
473
|
+
... longitude=np.array([1, 2, 3, 5, 8]),
|
|
474
|
+
... latitude=np.arange(5),
|
|
475
|
+
... altitude=np.full(shape=(5,), fill_value=11000),
|
|
476
|
+
... time=pd.date_range('2021-01-01T12', '2021-01-01T14', periods=5),
|
|
477
|
+
... )
|
|
478
|
+
>>> fl.segment_haversine()
|
|
479
|
+
array([157255.03346286, 157231.08336815, 248456.48781503, 351047.44358851,
|
|
480
|
+
nan])
|
|
481
|
+
|
|
482
|
+
See Also
|
|
483
|
+
--------
|
|
484
|
+
:func:`segment_haversine`
|
|
485
|
+
:meth:`segment_length`
|
|
486
|
+
"""
|
|
487
|
+
if self.attrs["crs"] != "EPSG:4326":
|
|
488
|
+
raise NotImplementedError("Only implemented for EPSG:4326 CRS.")
|
|
489
|
+
|
|
490
|
+
return geo.segment_haversine(self["longitude"], self["latitude"])
|
|
491
|
+
|
|
450
492
|
def segment_length(self) -> npt.NDArray[np.float64]:
|
|
451
493
|
"""Compute spherical distance between flight waypoints.
|
|
452
494
|
|
|
@@ -1232,7 +1274,7 @@ class Flight(GeoVectorDataset):
|
|
|
1232
1274
|
raise NotImplementedError("Only implemented for EPSG:4326 CRS.")
|
|
1233
1275
|
|
|
1234
1276
|
# Omit the final nan and ensure index + 1 (below) is well defined
|
|
1235
|
-
segs = self.
|
|
1277
|
+
segs = self.segment_haversine()[:-1]
|
|
1236
1278
|
|
|
1237
1279
|
# For default geodesic_threshold, we expect gap_indices to be very
|
|
1238
1280
|
# sparse (so the for loop below is cheap)
|
pycontrails/core/met.py
CHANGED
|
@@ -1793,10 +1793,11 @@ class MetDataArray(MetBase):
|
|
|
1793
1793
|
self,
|
|
1794
1794
|
level: float | int | None = None,
|
|
1795
1795
|
time: np.datetime64 | datetime | None = None,
|
|
1796
|
-
fill_value: float =
|
|
1796
|
+
fill_value: float = np.nan,
|
|
1797
1797
|
iso_value: float | None = None,
|
|
1798
1798
|
min_area: float = 0.0,
|
|
1799
1799
|
epsilon: float = 0.0,
|
|
1800
|
+
lower_bound: bool = True,
|
|
1800
1801
|
precision: int | None = None,
|
|
1801
1802
|
interiors: bool = True,
|
|
1802
1803
|
convex_hull: bool = False,
|
|
@@ -1848,7 +1849,8 @@ class MetDataArray(MetBase):
|
|
|
1848
1849
|
automatically.
|
|
1849
1850
|
fill_value : float, optional
|
|
1850
1851
|
Value used for filling missing data and for padding the underlying data array.
|
|
1851
|
-
|
|
1852
|
+
Set to ``np.nan`` by default, which ensures that regions with missing data are
|
|
1853
|
+
never included in polygons.
|
|
1852
1854
|
iso_value : float, optional
|
|
1853
1855
|
Value in field to create iso-surface.
|
|
1854
1856
|
Defaults to the average of the min and max value of the array. (This is the
|
|
@@ -1862,6 +1864,9 @@ class MetDataArray(MetBase):
|
|
|
1862
1864
|
Control the extent to which the polygon is simplified. A value of 0 does not alter
|
|
1863
1865
|
the geometry of the polygon. The unit of this parameter is in longitude/latitude
|
|
1864
1866
|
degrees. By default, 0.0.
|
|
1867
|
+
lower_bound : bool, optional
|
|
1868
|
+
Whether to use ``iso_value`` as a lower or upper bound on values in polygon interiors.
|
|
1869
|
+
By default, True.
|
|
1865
1870
|
precision : int, optional
|
|
1866
1871
|
Number of decimal places to round coordinates to. If None, no rounding is performed.
|
|
1867
1872
|
interiors : bool, optional
|
|
@@ -1882,6 +1887,14 @@ class MetDataArray(MetBase):
|
|
|
1882
1887
|
dict[str, Any]
|
|
1883
1888
|
Python representation of GeoJSON Feature with MultiPolygon geometry.
|
|
1884
1889
|
|
|
1890
|
+
Notes
|
|
1891
|
+
-----
|
|
1892
|
+
:class:`Cocip` and :class:`CocipGrid` set some quantities to 0 and other quantities
|
|
1893
|
+
to ``np.nan`` in regions where no contrails form. When computing polygons from
|
|
1894
|
+
:class:`Cocip` or :class:`CocipGrid` output, take care that the choice of
|
|
1895
|
+
``fill_value`` correctly includes or excludes contrail-free regions. See the
|
|
1896
|
+
:class:`Cocip` documentation for details about ``np.nan`` in model output.
|
|
1897
|
+
|
|
1885
1898
|
See Also
|
|
1886
1899
|
--------
|
|
1887
1900
|
:meth:`to_polyhedra`
|
|
@@ -1925,11 +1938,12 @@ class MetDataArray(MetBase):
|
|
|
1925
1938
|
"include_altitude=False."
|
|
1926
1939
|
)
|
|
1927
1940
|
|
|
1928
|
-
np.
|
|
1941
|
+
if not np.isnan(fill_value):
|
|
1942
|
+
np.nan_to_num(arr, copy=False, nan=fill_value)
|
|
1929
1943
|
|
|
1930
1944
|
# default iso_value
|
|
1931
1945
|
if iso_value is None:
|
|
1932
|
-
iso_value = (np.
|
|
1946
|
+
iso_value = (np.nanmax(arr) + np.nanmin(arr)) / 2
|
|
1933
1947
|
warnings.warn(f"The 'iso_value' parameter was not specified. Using value: {iso_value}")
|
|
1934
1948
|
|
|
1935
1949
|
# We'll get a nice error message if dependencies are not installed
|
|
@@ -1945,6 +1959,7 @@ class MetDataArray(MetBase):
|
|
|
1945
1959
|
threshold=iso_value,
|
|
1946
1960
|
min_area=min_area,
|
|
1947
1961
|
epsilon=epsilon,
|
|
1962
|
+
lower_bound=lower_bound,
|
|
1948
1963
|
interiors=interiors,
|
|
1949
1964
|
convex_hull=convex_hull,
|
|
1950
1965
|
longitude=longitude,
|
|
@@ -1957,10 +1972,11 @@ class MetDataArray(MetBase):
|
|
|
1957
1972
|
def to_polygon_feature_collection(
|
|
1958
1973
|
self,
|
|
1959
1974
|
time: np.datetime64 | datetime | None = None,
|
|
1960
|
-
fill_value: float =
|
|
1975
|
+
fill_value: float = np.nan,
|
|
1961
1976
|
iso_value: float | None = None,
|
|
1962
1977
|
min_area: float = 0.0,
|
|
1963
1978
|
epsilon: float = 0.0,
|
|
1979
|
+
lower_bound: bool = True,
|
|
1964
1980
|
precision: int | None = None,
|
|
1965
1981
|
interiors: bool = True,
|
|
1966
1982
|
convex_hull: bool = False,
|
|
@@ -1991,6 +2007,7 @@ class MetDataArray(MetBase):
|
|
|
1991
2007
|
iso_value=iso_value,
|
|
1992
2008
|
min_area=min_area,
|
|
1993
2009
|
epsilon=epsilon,
|
|
2010
|
+
lower_bound=lower_bound,
|
|
1994
2011
|
precision=precision,
|
|
1995
2012
|
interiors=interiors,
|
|
1996
2013
|
convex_hull=convex_hull,
|
|
@@ -2011,6 +2028,7 @@ class MetDataArray(MetBase):
|
|
|
2011
2028
|
time: datetime | None = ...,
|
|
2012
2029
|
iso_value: float = ...,
|
|
2013
2030
|
simplify_fraction: float = ...,
|
|
2031
|
+
lower_bound: bool = ...,
|
|
2014
2032
|
return_type: Literal["geojson"],
|
|
2015
2033
|
path: str | None = ...,
|
|
2016
2034
|
altitude_scale: float = ...,
|
|
@@ -2025,6 +2043,7 @@ class MetDataArray(MetBase):
|
|
|
2025
2043
|
time: datetime | None = ...,
|
|
2026
2044
|
iso_value: float = ...,
|
|
2027
2045
|
simplify_fraction: float = ...,
|
|
2046
|
+
lower_bound: bool = ...,
|
|
2028
2047
|
return_type: Literal["mesh"],
|
|
2029
2048
|
path: str | None = ...,
|
|
2030
2049
|
altitude_scale: float = ...,
|
|
@@ -2038,6 +2057,7 @@ class MetDataArray(MetBase):
|
|
|
2038
2057
|
time: datetime | None = None,
|
|
2039
2058
|
iso_value: float = 0.0,
|
|
2040
2059
|
simplify_fraction: float = 1.0,
|
|
2060
|
+
lower_bound: bool = True,
|
|
2041
2061
|
return_type: str = "geojson",
|
|
2042
2062
|
path: str | None = None,
|
|
2043
2063
|
altitude_scale: float = 1.0,
|
|
@@ -2058,6 +2078,9 @@ class MetDataArray(MetBase):
|
|
|
2058
2078
|
Apply `open3d` `simplify_quadric_decimation` method to simplify the polyhedra geometry.
|
|
2059
2079
|
This parameter must be in the half-open interval (0.0, 1.0].
|
|
2060
2080
|
Defaults to 1.0, corresponding to no reduction.
|
|
2081
|
+
lower_bound : bool, optional
|
|
2082
|
+
Whether to use ``iso_value`` as a lower or upper bound on values in polyhedra interiors.
|
|
2083
|
+
By default, True.
|
|
2061
2084
|
return_type : str, optional
|
|
2062
2085
|
Must be one of "geojson" or "mesh". Defaults to "geojson".
|
|
2063
2086
|
If "geojson", this method returns a dictionary representation of a geojson MultiPolygon
|
|
@@ -2145,6 +2168,11 @@ class MetDataArray(MetBase):
|
|
|
2145
2168
|
# 3d array of longitude, latitude, altitude values
|
|
2146
2169
|
volume = self.data.sel(time=time).values
|
|
2147
2170
|
|
|
2171
|
+
# invert if iso_value is an upper bound on interior values
|
|
2172
|
+
if not lower_bound:
|
|
2173
|
+
volume = -volume
|
|
2174
|
+
iso_value = -iso_value
|
|
2175
|
+
|
|
2148
2176
|
# convert from array index back to coordinates
|
|
2149
2177
|
longitude = self.indexes["longitude"].values
|
|
2150
2178
|
latitude = self.indexes["latitude"].values
|
pycontrails/core/polygon.py
CHANGED
|
@@ -283,6 +283,7 @@ def find_multipolygon(
|
|
|
283
283
|
threshold: float,
|
|
284
284
|
min_area: float,
|
|
285
285
|
epsilon: float,
|
|
286
|
+
lower_bound: bool = True,
|
|
286
287
|
interiors: bool = True,
|
|
287
288
|
convex_hull: bool = False,
|
|
288
289
|
longitude: npt.NDArray[np.float64] | None = None,
|
|
@@ -304,10 +305,13 @@ def find_multipolygon(
|
|
|
304
305
|
epsilon : float
|
|
305
306
|
Epsilon value to use when simplifying the polygons. Passed into shapely's
|
|
306
307
|
:meth:`shapely.geometry.Polygon.simplify` method.
|
|
308
|
+
lower_bound : bool, optional
|
|
309
|
+
Whether to treat ``threshold`` as a lower or upper bound on values in polygon interiors.
|
|
310
|
+
By default, True.
|
|
307
311
|
interiors : bool, optional
|
|
308
|
-
Whether to include interior polygons.
|
|
312
|
+
Whether to include interior polygons. By default, True.
|
|
309
313
|
convex_hull : bool, optional
|
|
310
|
-
Experimental. Whether to take the convex hull of each polygon.
|
|
314
|
+
Experimental. Whether to take the convex hull of each polygon. By default, False.
|
|
311
315
|
longitude : npt.NDArray[np.float64] | None, optional
|
|
312
316
|
If provided, the coordinates values corresponding to the longitude dimensions of ``arr``.
|
|
313
317
|
The contour coordinates will be converted to longitude-latitude values by indexing
|
|
@@ -339,7 +343,10 @@ def find_multipolygon(
|
|
|
339
343
|
buffer = 0.5
|
|
340
344
|
|
|
341
345
|
arr_bin = np.empty(arr.shape, dtype=np.uint8)
|
|
342
|
-
|
|
346
|
+
if lower_bound:
|
|
347
|
+
np.greater_equal(arr, threshold, out=arr_bin)
|
|
348
|
+
else:
|
|
349
|
+
np.less_equal(arr, threshold, out=arr_bin)
|
|
343
350
|
|
|
344
351
|
mode = cv2.RETR_CCOMP if interiors else cv2.RETR_EXTERNAL
|
|
345
352
|
contours, hierarchy = cv2.findContours(arr_bin, mode, cv2.CHAIN_APPROX_SIMPLE)
|
|
Binary file
|
|
@@ -4,10 +4,13 @@ from __future__ import annotations
|
|
|
4
4
|
|
|
5
5
|
from pycontrails.datalib.ecmwf.arco_era5 import ARCOERA5
|
|
6
6
|
from pycontrails.datalib.ecmwf.era5 import ERA5
|
|
7
|
+
from pycontrails.datalib.ecmwf.era5_model_level import ERA5ModelLevel
|
|
7
8
|
from pycontrails.datalib.ecmwf.hres import HRES
|
|
9
|
+
from pycontrails.datalib.ecmwf.hres_model_level import HRESModelLevel
|
|
8
10
|
from pycontrails.datalib.ecmwf.ifs import IFS
|
|
9
11
|
from pycontrails.datalib.ecmwf.variables import (
|
|
10
12
|
ECMWF_VARIABLES,
|
|
13
|
+
MODEL_LEVEL_VARIABLES,
|
|
11
14
|
PRESSURE_LEVEL_VARIABLES,
|
|
12
15
|
SURFACE_VARIABLES,
|
|
13
16
|
CloudAreaFraction,
|
|
@@ -27,7 +30,9 @@ from pycontrails.datalib.ecmwf.variables import (
|
|
|
27
30
|
__all__ = [
|
|
28
31
|
"ARCOERA5",
|
|
29
32
|
"ERA5",
|
|
33
|
+
"ERA5ModelLevel",
|
|
30
34
|
"HRES",
|
|
35
|
+
"HRESModelLevel",
|
|
31
36
|
"IFS",
|
|
32
37
|
"CloudAreaFraction",
|
|
33
38
|
"CloudAreaFractionInLayer",
|
|
@@ -44,4 +49,5 @@ __all__ = [
|
|
|
44
49
|
"ECMWF_VARIABLES",
|
|
45
50
|
"PRESSURE_LEVEL_VARIABLES",
|
|
46
51
|
"SURFACE_VARIABLES",
|
|
52
|
+
"MODEL_LEVEL_VARIABLES",
|
|
47
53
|
]
|
|
@@ -11,7 +11,6 @@ This module supports:
|
|
|
11
11
|
This module requires the following additional dependencies:
|
|
12
12
|
|
|
13
13
|
- `metview (binaries and python bindings) <https://metview.readthedocs.io/en/latest/python.html>`_
|
|
14
|
-
- `lxml <https://lxml.de/>`_
|
|
15
14
|
- `gcsfs <https://gcsfs.readthedocs.io/en/latest/>`_
|
|
16
15
|
- `zarr <https://zarr.readthedocs.io/en/stable/>`_
|
|
17
16
|
|
|
@@ -22,7 +21,6 @@ from __future__ import annotations
|
|
|
22
21
|
import contextlib
|
|
23
22
|
import dataclasses
|
|
24
23
|
import datetime
|
|
25
|
-
import functools
|
|
26
24
|
import hashlib
|
|
27
25
|
import multiprocessing
|
|
28
26
|
import pathlib
|
|
@@ -31,7 +29,6 @@ import warnings
|
|
|
31
29
|
from collections.abc import Iterable
|
|
32
30
|
from typing import Any
|
|
33
31
|
|
|
34
|
-
import pandas as pd
|
|
35
32
|
import xarray as xr
|
|
36
33
|
from overrides import overrides
|
|
37
34
|
|
|
@@ -39,7 +36,7 @@ from pycontrails.core import cache, datalib, met_var
|
|
|
39
36
|
from pycontrails.core.met import MetDataset
|
|
40
37
|
from pycontrails.datalib.ecmwf import common as ecmwf_common
|
|
41
38
|
from pycontrails.datalib.ecmwf import variables as ecmwf_variables
|
|
42
|
-
from pycontrails.
|
|
39
|
+
from pycontrails.datalib.ecmwf.model_levels import pressure_levels_at_model_levels
|
|
43
40
|
from pycontrails.utils import dependencies
|
|
44
41
|
|
|
45
42
|
try:
|
|
@@ -76,54 +73,6 @@ MOISTURE_STORE_VARIABLES = [
|
|
|
76
73
|
PRESSURE_LEVEL_VARIABLES = [*WIND_STORE_VARIABLES, *MOISTURE_STORE_VARIABLES, met_var.Geopotential]
|
|
77
74
|
|
|
78
75
|
|
|
79
|
-
@functools.cache
|
|
80
|
-
def _read_model_level_dataframe() -> pd.DataFrame:
|
|
81
|
-
"""Read the ERA5 model level definitions published by ECMWF.
|
|
82
|
-
|
|
83
|
-
This requires the lxml package to be installed.
|
|
84
|
-
"""
|
|
85
|
-
url = "https://confluence.ecmwf.int/display/UDOC/L137+model+level+definitions"
|
|
86
|
-
try:
|
|
87
|
-
return pd.read_html(url, na_values="-", index_col="n")[0]
|
|
88
|
-
except ImportError as exc:
|
|
89
|
-
if "lxml" in exc.msg:
|
|
90
|
-
dependencies.raise_module_not_found_error(
|
|
91
|
-
"arco_era5._read_model_level_dataframe function",
|
|
92
|
-
package_name="lxml",
|
|
93
|
-
module_not_found_error=exc,
|
|
94
|
-
extra=(
|
|
95
|
-
"Alternatively, if instantiating an 'ARCOERA5' object, you can provide "
|
|
96
|
-
"the 'pressure_levels' parameter directly to avoid the need to read the "
|
|
97
|
-
"ECMWF model level definitions."
|
|
98
|
-
),
|
|
99
|
-
)
|
|
100
|
-
raise
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
def pressure_levels_at_model_levels(alt_ft_min: float, alt_ft_max: float) -> list[int]:
|
|
104
|
-
"""Return the pressure levels at each model level assuming a constant surface pressure.
|
|
105
|
-
|
|
106
|
-
The pressure levels are rounded to the nearest hPa.
|
|
107
|
-
|
|
108
|
-
Parameters
|
|
109
|
-
----------
|
|
110
|
-
alt_ft_min : float
|
|
111
|
-
Minimum altitude, [:math:`ft`].
|
|
112
|
-
alt_ft_max : float
|
|
113
|
-
Maximum altitude, [:math:`ft`].
|
|
114
|
-
|
|
115
|
-
Returns
|
|
116
|
-
-------
|
|
117
|
-
list[int]
|
|
118
|
-
List of pressure levels, [:math:`hPa`].
|
|
119
|
-
"""
|
|
120
|
-
df = _read_model_level_dataframe()
|
|
121
|
-
alt_m_min = units.ft_to_m(alt_ft_min)
|
|
122
|
-
alt_m_max = units.ft_to_m(alt_ft_max)
|
|
123
|
-
filt = df["Geometric Altitude [m]"].between(alt_m_min, alt_m_max)
|
|
124
|
-
return df.loc[filt, "pf [hPa]"].round().astype(int).tolist()
|
|
125
|
-
|
|
126
|
-
|
|
127
76
|
def _attribute_fix(ds: xr.Dataset | None) -> None:
|
|
128
77
|
"""Fix GRIB attributes.
|
|
129
78
|
|
|
@@ -444,7 +393,7 @@ class ARCOERA5(ecmwf_common.ECMWFAPI):
|
|
|
444
393
|
self.variables = datalib.parse_variables(variables, self.supported_variables)
|
|
445
394
|
self.grid = grid
|
|
446
395
|
self.cachestore = cache.DiskCacheStore() if cachestore is self.__marker else cachestore
|
|
447
|
-
self.n_jobs = n_jobs
|
|
396
|
+
self.n_jobs = max(1, n_jobs)
|
|
448
397
|
self.cleanup_metview_tempfiles = cleanup_metview_tempfiles
|
|
449
398
|
|
|
450
399
|
@property
|
|
@@ -21,7 +21,7 @@ from overrides import overrides
|
|
|
21
21
|
import pycontrails
|
|
22
22
|
from pycontrails.core import cache, datalib
|
|
23
23
|
from pycontrails.core.met import MetDataset, MetVariable
|
|
24
|
-
from pycontrails.datalib.ecmwf.common import ECMWFAPI
|
|
24
|
+
from pycontrails.datalib.ecmwf.common import ECMWFAPI, CDSCredentialsNotFound
|
|
25
25
|
from pycontrails.datalib.ecmwf.variables import PRESSURE_LEVEL_VARIABLES, SURFACE_VARIABLES
|
|
26
26
|
from pycontrails.utils import dependencies, temp
|
|
27
27
|
|
|
@@ -183,7 +183,7 @@ class ERA5(ECMWFAPI):
|
|
|
183
183
|
grid_min = 0.25 if product_type == "reanalysis" else 0.5
|
|
184
184
|
if grid < grid_min:
|
|
185
185
|
warnings.warn(
|
|
186
|
-
f"The
|
|
186
|
+
f"The highest resolution available through the CDS API is {grid_min} degrees. "
|
|
187
187
|
f"Your downloaded data will have resolution {grid}, but it is a "
|
|
188
188
|
f"reinterpolation of the {grid_min} degree data. The same interpolation can be "
|
|
189
189
|
"achieved directly with xarray."
|
|
@@ -535,7 +535,3 @@ class ERA5(ECMWFAPI):
|
|
|
535
535
|
|
|
536
536
|
ds.attrs["pycontrails_version"] = pycontrails.__version__
|
|
537
537
|
return ds
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
class CDSCredentialsNotFound(Exception):
|
|
541
|
-
"""Raise when CDS credentials are not found by :class:`cdsapi.Client` instance."""
|