pycontrails 0.52.3__cp310-cp310-macosx_10_9_x86_64.whl → 0.53.1__cp310-cp310-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/cache.py +1 -1
- pycontrails/core/flight.py +8 -5
- pycontrails/core/flightplan.py +1 -1
- pycontrails/core/interpolation.py +1 -0
- pycontrails/core/met.py +11 -13
- pycontrails/core/met_var.py +1 -1
- pycontrails/core/models.py +5 -5
- pycontrails/core/rgi_cython.cpython-310-darwin.so +0 -0
- pycontrails/core/vector.py +5 -5
- pycontrails/datalib/_leo_utils/vis.py +10 -11
- pycontrails/datalib/_met_utils/metsource.py +20 -15
- pycontrails/datalib/ecmwf/common.py +1 -1
- pycontrails/datalib/ecmwf/era5.py +21 -10
- pycontrails/datalib/ecmwf/era5_model_level.py +15 -8
- pycontrails/datalib/ecmwf/hres_model_level.py +3 -3
- pycontrails/datalib/ecmwf/variables.py +3 -3
- pycontrails/datalib/gfs/gfs.py +4 -3
- pycontrails/datalib/landsat.py +10 -9
- pycontrails/ext/synthetic_flight.py +1 -1
- pycontrails/models/accf.py +1 -1
- pycontrails/models/apcemm/apcemm.py +5 -5
- pycontrails/models/cocip/cocip.py +3 -3
- pycontrails/models/cocip/output_formats.py +2 -2
- pycontrails/models/cocip/radiative_forcing.py +3 -3
- pycontrails/models/cocipgrid/cocip_grid.py +8 -8
- pycontrails/models/ps_model/ps_model.py +4 -4
- pycontrails/models/sac.py +2 -2
- pycontrails/physics/thermo.py +1 -1
- pycontrails/utils/json.py +16 -18
- pycontrails/utils/types.py +7 -6
- {pycontrails-0.52.3.dist-info → pycontrails-0.53.1.dist-info}/METADATA +6 -6
- {pycontrails-0.52.3.dist-info → pycontrails-0.53.1.dist-info}/RECORD +37 -37
- {pycontrails-0.52.3.dist-info → pycontrails-0.53.1.dist-info}/WHEEL +1 -1
- {pycontrails-0.52.3.dist-info → pycontrails-0.53.1.dist-info}/top_level.txt +0 -1
- {pycontrails-0.52.3.dist-info → pycontrails-0.53.1.dist-info}/LICENSE +0 -0
- {pycontrails-0.52.3.dist-info → pycontrails-0.53.1.dist-info}/NOTICE +0 -0
pycontrails/_version.py
CHANGED
pycontrails/core/cache.py
CHANGED
|
@@ -146,7 +146,7 @@ class CacheStore(ABC):
|
|
|
146
146
|
"""
|
|
147
147
|
|
|
148
148
|
# TODO: run in parallel?
|
|
149
|
-
return [self.put(d, cp) for d, cp in zip(data_path, cache_path)]
|
|
149
|
+
return [self.put(d, cp) for d, cp in zip(data_path, cache_path, strict=True)]
|
|
150
150
|
|
|
151
151
|
# In the three methods below, child classes have a complete docstring.
|
|
152
152
|
|
pycontrails/core/flight.py
CHANGED
|
@@ -1356,7 +1356,7 @@ class Flight(GeoVectorDataset):
|
|
|
1356
1356
|
# NOTE: geod.npts does not return the initial or terminal points
|
|
1357
1357
|
lonlats: list[tuple[float, float]] = geod.npts(lon0, lat0, lon1, lat1, n_steps)
|
|
1358
1358
|
|
|
1359
|
-
lons, lats = zip(*lonlats)
|
|
1359
|
+
lons, lats = zip(*lonlats, strict=True)
|
|
1360
1360
|
longitudes.extend(lons)
|
|
1361
1361
|
latitudes.extend(lats)
|
|
1362
1362
|
|
|
@@ -1657,10 +1657,11 @@ def _return_linestring(data: dict[str, npt.NDArray[np.float64]]) -> list[list[fl
|
|
|
1657
1657
|
The list of coordinates
|
|
1658
1658
|
"""
|
|
1659
1659
|
# rounding to reduce the size of resultant json arrays
|
|
1660
|
-
points = zip(
|
|
1660
|
+
points = zip(
|
|
1661
1661
|
np.round(data["longitude"], decimals=4),
|
|
1662
1662
|
np.round(data["latitude"], decimals=4),
|
|
1663
1663
|
np.round(data["altitude"], decimals=4),
|
|
1664
|
+
strict=True,
|
|
1664
1665
|
)
|
|
1665
1666
|
return [list(p) for p in points]
|
|
1666
1667
|
|
|
@@ -1949,7 +1950,9 @@ def _altitude_interpolation_climb_descend_middle(
|
|
|
1949
1950
|
# Form array of cumulative altitude values if the flight were to climb
|
|
1950
1951
|
# at nominal_rocd over each group of nan
|
|
1951
1952
|
cumalt_list = []
|
|
1952
|
-
for start_na_idx, end_na_idx, size in zip(
|
|
1953
|
+
for start_na_idx, end_na_idx, size in zip(
|
|
1954
|
+
start_na_idxs, end_na_idxs, na_group_size, strict=True
|
|
1955
|
+
):
|
|
1953
1956
|
if s[start_na_idx] <= s[end_na_idx]:
|
|
1954
1957
|
cumalt_list.append(np.arange(1, size, dtype=float))
|
|
1955
1958
|
else:
|
|
@@ -2053,7 +2056,7 @@ def filter_altitude(
|
|
|
2053
2056
|
--------
|
|
2054
2057
|
:meth:`traffic.core.flight.Flight.filter`
|
|
2055
2058
|
:func:`scipy.signal.medfilt`
|
|
2056
|
-
"""
|
|
2059
|
+
"""
|
|
2057
2060
|
if not len(altitude_ft):
|
|
2058
2061
|
raise ValueError("Altitude must have non-zero length to filter")
|
|
2059
2062
|
|
|
@@ -2114,7 +2117,7 @@ def filter_altitude(
|
|
|
2114
2117
|
|
|
2115
2118
|
result = np.copy(altitude_ft)
|
|
2116
2119
|
if np.any(start_idxs):
|
|
2117
|
-
for i0, i1 in zip(start_idxs, end_idxs):
|
|
2120
|
+
for i0, i1 in zip(start_idxs, end_idxs, strict=True):
|
|
2118
2121
|
result[i0:i1] = altitude_filt[i0:i1]
|
|
2119
2122
|
|
|
2120
2123
|
# reapply Savitzky-Golay filter to smooth climb and descent
|
pycontrails/core/flightplan.py
CHANGED
|
@@ -76,6 +76,7 @@ class PycontrailsRegularGridInterpolator(scipy.interpolate.RegularGridInterpolat
|
|
|
76
76
|
self.method = _pick_method(scipy.__version__, method)
|
|
77
77
|
self.bounds_error = bounds_error
|
|
78
78
|
self.fill_value = fill_value
|
|
79
|
+
self._spline = None
|
|
79
80
|
|
|
80
81
|
def _prepare_xi_simple(self, xi: npt.NDArray[np.float64]) -> npt.NDArray[np.bool_]:
|
|
81
82
|
"""Run looser version of :meth:`_prepare_xi`.
|
pycontrails/core/met.py
CHANGED
|
@@ -70,12 +70,12 @@ class MetBase(ABC, Generic[XArrayType]):
|
|
|
70
70
|
cachestore: CacheStore | None
|
|
71
71
|
|
|
72
72
|
#: Default dimension order for DataArray or Dataset (x, y, z, t)
|
|
73
|
-
dim_order:
|
|
73
|
+
dim_order: tuple[Hashable, Hashable, Hashable, Hashable] = (
|
|
74
74
|
"longitude",
|
|
75
75
|
"latitude",
|
|
76
76
|
"level",
|
|
77
77
|
"time",
|
|
78
|
-
|
|
78
|
+
)
|
|
79
79
|
|
|
80
80
|
def __repr__(self) -> str:
|
|
81
81
|
data = getattr(self, "data", None)
|
|
@@ -200,10 +200,8 @@ class MetBase(ABC, Generic[XArrayType]):
|
|
|
200
200
|
def _validate_transpose(self) -> None:
|
|
201
201
|
"""Check that data is transposed according to :attr:`dim_order`."""
|
|
202
202
|
|
|
203
|
-
dims_tuple = tuple(self.dim_order)
|
|
204
|
-
|
|
205
203
|
def _check_da(da: xr.DataArray, key: Hashable | None = None) -> None:
|
|
206
|
-
if da.dims !=
|
|
204
|
+
if da.dims != self.dim_order:
|
|
207
205
|
if key is not None:
|
|
208
206
|
msg = (
|
|
209
207
|
f"Data dimension not transposed on variable '{key}'. Initiate with"
|
|
@@ -271,7 +269,7 @@ class MetBase(ABC, Generic[XArrayType]):
|
|
|
271
269
|
self.data["time"] = self.data["time"].astype("datetime64[ns]", copy=False)
|
|
272
270
|
|
|
273
271
|
# sortby to ensure each coordinate has ascending order
|
|
274
|
-
self.data = self.data.sortby(self.dim_order, ascending=True)
|
|
272
|
+
self.data = self.data.sortby(list(self.dim_order), ascending=True)
|
|
275
273
|
|
|
276
274
|
if not self.is_wrapped:
|
|
277
275
|
# Ensure longitude is contained in interval [-180, 180)
|
|
@@ -293,7 +291,7 @@ class MetBase(ABC, Generic[XArrayType]):
|
|
|
293
291
|
self._validate_latitude()
|
|
294
292
|
|
|
295
293
|
# transpose to have ordering (x, y, z, t, ...)
|
|
296
|
-
dim_order = self.dim_order
|
|
294
|
+
dim_order = [*self.dim_order, *(d for d in self.data.dims if d not in self.dim_order)]
|
|
297
295
|
self.data = self.data.transpose(*dim_order)
|
|
298
296
|
|
|
299
297
|
# single level data
|
|
@@ -489,7 +487,7 @@ class MetBase(ABC, Generic[XArrayType]):
|
|
|
489
487
|
self.cachestore = self.cachestore or DiskCacheStore()
|
|
490
488
|
|
|
491
489
|
# group by hour and save one dataset for each hour to temp file
|
|
492
|
-
times, datasets = zip(*dataset.groupby("time", squeeze=False))
|
|
490
|
+
times, datasets = zip(*dataset.groupby("time", squeeze=False), strict=True)
|
|
493
491
|
|
|
494
492
|
# Open ExitStack to control temp_file context manager
|
|
495
493
|
with ExitStack() as stack:
|
|
@@ -920,7 +918,7 @@ class MetDataset(MetBase):
|
|
|
920
918
|
KeyError
|
|
921
919
|
Raises when dataset does not contain variable in ``vars``
|
|
922
920
|
"""
|
|
923
|
-
if isinstance(vars,
|
|
921
|
+
if isinstance(vars, MetVariable | str):
|
|
924
922
|
vars = (vars,)
|
|
925
923
|
|
|
926
924
|
met_keys: list[str] = []
|
|
@@ -1022,7 +1020,7 @@ class MetDataset(MetBase):
|
|
|
1022
1020
|
|
|
1023
1021
|
@overrides
|
|
1024
1022
|
def broadcast_coords(self, name: str) -> xr.DataArray:
|
|
1025
|
-
da = xr.ones_like(self.data[
|
|
1023
|
+
da = xr.ones_like(self.data[next(iter(self.data.keys()))]) * self.data[name]
|
|
1026
1024
|
da.name = name
|
|
1027
1025
|
|
|
1028
1026
|
return da
|
|
@@ -1074,7 +1072,7 @@ class MetDataset(MetBase):
|
|
|
1074
1072
|
coords_vals = [indexes[key].values for key in coords_keys]
|
|
1075
1073
|
coords_meshes = np.meshgrid(*coords_vals, indexing="ij")
|
|
1076
1074
|
raveled_coords = (mesh.ravel() for mesh in coords_meshes)
|
|
1077
|
-
data = dict(zip(coords_keys, raveled_coords))
|
|
1075
|
+
data = dict(zip(coords_keys, raveled_coords, strict=True))
|
|
1078
1076
|
|
|
1079
1077
|
out = vector_module.GeoVectorDataset(data, copy=False)
|
|
1080
1078
|
for key, da in self.data.items():
|
|
@@ -1865,7 +1863,7 @@ class MetDataArray(MetBase):
|
|
|
1865
1863
|
cachestore = cachestore or DiskCacheStore()
|
|
1866
1864
|
chunks = chunks or {}
|
|
1867
1865
|
data = _load(hash, cachestore, chunks)
|
|
1868
|
-
return cls(data[
|
|
1866
|
+
return cls(data[next(iter(data.data_vars))])
|
|
1869
1867
|
|
|
1870
1868
|
@property
|
|
1871
1869
|
def proportion(self) -> float:
|
|
@@ -2258,7 +2256,7 @@ class MetDataArray(MetBase):
|
|
|
2258
2256
|
-----
|
|
2259
2257
|
Uses the `scikit-image Marching Cubes <https://scikit-image.org/docs/dev/auto_examples/edges/plot_marching_cubes.html>`_
|
|
2260
2258
|
algorithm to reconstruct a surface from the point-cloud like arrays.
|
|
2261
|
-
"""
|
|
2259
|
+
"""
|
|
2262
2260
|
try:
|
|
2263
2261
|
from skimage import measure
|
|
2264
2262
|
except ModuleNotFoundError as e:
|
pycontrails/core/met_var.py
CHANGED
|
@@ -22,7 +22,7 @@ class MetVariable:
|
|
|
22
22
|
- `NCEP Grib v2 Code Table <https://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_doc/grib2_table4-2.shtml>`_
|
|
23
23
|
|
|
24
24
|
Used for defining support parameters in a grib-like fashion.
|
|
25
|
-
"""
|
|
25
|
+
"""
|
|
26
26
|
|
|
27
27
|
#: Short variable name.
|
|
28
28
|
#: Chosen for greatest consistency between data sources.
|
pycontrails/core/models.py
CHANGED
|
@@ -11,7 +11,7 @@ import warnings
|
|
|
11
11
|
from abc import ABC, abstractmethod
|
|
12
12
|
from collections.abc import Sequence
|
|
13
13
|
from dataclasses import dataclass, fields
|
|
14
|
-
from typing import Any, NoReturn, TypeVar,
|
|
14
|
+
from typing import Any, NoReturn, TypeVar, overload
|
|
15
15
|
|
|
16
16
|
import numpy as np
|
|
17
17
|
import numpy.typing as npt
|
|
@@ -30,13 +30,13 @@ from pycontrails.utils.types import type_guard
|
|
|
30
30
|
logger = logging.getLogger(__name__)
|
|
31
31
|
|
|
32
32
|
#: Model input source types
|
|
33
|
-
ModelInput =
|
|
33
|
+
ModelInput = MetDataset | GeoVectorDataset | Flight | Sequence[Flight] | None
|
|
34
34
|
|
|
35
35
|
#: Model output source types
|
|
36
|
-
ModelOutput =
|
|
36
|
+
ModelOutput = MetDataArray | MetDataset | GeoVectorDataset | Flight | list[Flight]
|
|
37
37
|
|
|
38
38
|
#: Model attribute source types
|
|
39
|
-
SourceType =
|
|
39
|
+
SourceType = MetDataset | GeoVectorDataset | Flight | Fleet
|
|
40
40
|
|
|
41
41
|
_Source = TypeVar("_Source")
|
|
42
42
|
|
|
@@ -453,7 +453,7 @@ class Model(ABC):
|
|
|
453
453
|
return Fleet.from_seq(source)
|
|
454
454
|
|
|
455
455
|
# Raise error if source is not a MetDataset or GeoVectorDataset
|
|
456
|
-
if not isinstance(source,
|
|
456
|
+
if not isinstance(source, MetDataset | GeoVectorDataset):
|
|
457
457
|
msg = f"Unknown source type: {type(source)}"
|
|
458
458
|
raise TypeError(msg)
|
|
459
459
|
|
|
Binary file
|
pycontrails/core/vector.py
CHANGED
|
@@ -847,7 +847,7 @@ class VectorDataset:
|
|
|
847
847
|
------
|
|
848
848
|
TypeError
|
|
849
849
|
If ``mask`` is not a boolean array.
|
|
850
|
-
"""
|
|
850
|
+
"""
|
|
851
851
|
self.data._validate_array(mask)
|
|
852
852
|
if mask.dtype != bool:
|
|
853
853
|
raise TypeError("Parameter `mask` must be a boolean array.")
|
|
@@ -983,7 +983,7 @@ class VectorDataset:
|
|
|
983
983
|
numeric_attrs = (
|
|
984
984
|
attr
|
|
985
985
|
for attr, val in self.attrs.items()
|
|
986
|
-
if (isinstance(val,
|
|
986
|
+
if (isinstance(val, int | float | np.number) and attr not in ignore_keys)
|
|
987
987
|
)
|
|
988
988
|
self.broadcast_attrs(numeric_attrs, overwrite)
|
|
989
989
|
|
|
@@ -1072,7 +1072,7 @@ class VectorDataset:
|
|
|
1072
1072
|
obj = obj.to_numpy()
|
|
1073
1073
|
|
|
1074
1074
|
# Convert numpy objects to python objects
|
|
1075
|
-
if isinstance(obj,
|
|
1075
|
+
if isinstance(obj, np.ndarray | np.generic):
|
|
1076
1076
|
|
|
1077
1077
|
# round time to unix seconds
|
|
1078
1078
|
if key == "time":
|
|
@@ -1166,7 +1166,7 @@ class VectorDataset:
|
|
|
1166
1166
|
attrs = {}
|
|
1167
1167
|
|
|
1168
1168
|
for k, v in {**obj, **obj_kwargs}.items():
|
|
1169
|
-
if isinstance(v,
|
|
1169
|
+
if isinstance(v, list | np.ndarray):
|
|
1170
1170
|
data[k] = v
|
|
1171
1171
|
else:
|
|
1172
1172
|
attrs[k] = v
|
|
@@ -1194,7 +1194,7 @@ class VectorDataset:
|
|
|
1194
1194
|
See Also
|
|
1195
1195
|
--------
|
|
1196
1196
|
:func:`numpy.array_split`
|
|
1197
|
-
"""
|
|
1197
|
+
"""
|
|
1198
1198
|
full_index = np.arange(self.size)
|
|
1199
1199
|
index_splits = np.array_split(full_index, n_splits)
|
|
1200
1200
|
for index in index_splits:
|
|
@@ -6,16 +6,6 @@ import numpy as np
|
|
|
6
6
|
|
|
7
7
|
from pycontrails.utils import dependencies
|
|
8
8
|
|
|
9
|
-
try:
|
|
10
|
-
import skimage as ski
|
|
11
|
-
except ModuleNotFoundError as exc:
|
|
12
|
-
dependencies.raise_module_not_found_error(
|
|
13
|
-
name="landsat module",
|
|
14
|
-
package_name="scikit-image",
|
|
15
|
-
module_not_found_error=exc,
|
|
16
|
-
pycontrails_optional_package="sat",
|
|
17
|
-
)
|
|
18
|
-
|
|
19
9
|
|
|
20
10
|
def normalize(channel: np.ndarray) -> np.ndarray:
|
|
21
11
|
"""Normalize channel values to range [0, 1], preserving ``np.nan`` in output.
|
|
@@ -53,8 +43,17 @@ def equalize(channel: np.ndarray, **equalize_kwargs: Any) -> np.ndarray:
|
|
|
53
43
|
NaN values are converted to 0 before passing to :py:func:`ski.exposure.equalize_adapthist`
|
|
54
44
|
and may affect equalized values in the neighborhood where they occur.
|
|
55
45
|
"""
|
|
46
|
+
try:
|
|
47
|
+
import skimage.exposure
|
|
48
|
+
except ModuleNotFoundError as exc:
|
|
49
|
+
dependencies.raise_module_not_found_error(
|
|
50
|
+
name="landsat module",
|
|
51
|
+
package_name="scikit-image",
|
|
52
|
+
module_not_found_error=exc,
|
|
53
|
+
pycontrails_optional_package="sat",
|
|
54
|
+
)
|
|
56
55
|
return np.where(
|
|
57
56
|
np.isnan(channel),
|
|
58
57
|
np.nan,
|
|
59
|
-
|
|
58
|
+
skimage.exposure.equalize_adapthist(np.nan_to_num(channel, nan=0.0), **equalize_kwargs),
|
|
60
59
|
)
|
|
@@ -8,7 +8,7 @@ import logging
|
|
|
8
8
|
import pathlib
|
|
9
9
|
from collections.abc import Sequence
|
|
10
10
|
from datetime import datetime
|
|
11
|
-
from typing import Any,
|
|
11
|
+
from typing import Any, TypeAlias
|
|
12
12
|
|
|
13
13
|
import numpy as np
|
|
14
14
|
import pandas as pd
|
|
@@ -20,11 +20,13 @@ from pycontrails.utils.types import DatetimeLike
|
|
|
20
20
|
|
|
21
21
|
logger = logging.getLogger(__name__)
|
|
22
22
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
]
|
|
27
|
-
|
|
23
|
+
# https://github.com/python/mypy/issues/14824
|
|
24
|
+
TimeInput: TypeAlias = str | DatetimeLike | Sequence[str | DatetimeLike]
|
|
25
|
+
VariableInput = (
|
|
26
|
+
str | int | MetVariable | np.ndarray | Sequence[str | int | MetVariable | Sequence[MetVariable]]
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
PressureLevelInput = int | float | np.ndarray | Sequence[int | float]
|
|
28
30
|
|
|
29
31
|
#: NetCDF engine to use for parsing netcdf files
|
|
30
32
|
NETCDF_ENGINE: str = "netcdf4"
|
|
@@ -66,23 +68,23 @@ def parse_timesteps(time: TimeInput | None, freq: str | None = "1h") -> list[dat
|
|
|
66
68
|
------
|
|
67
69
|
ValueError
|
|
68
70
|
Raises when the time has len > 2 or when time elements fail to be parsed with pd.to_datetime
|
|
69
|
-
"""
|
|
71
|
+
"""
|
|
70
72
|
|
|
71
73
|
if time is None:
|
|
72
74
|
return []
|
|
73
75
|
|
|
74
76
|
# confirm input is tuple or list-like of length 2
|
|
75
|
-
if isinstance(time,
|
|
77
|
+
if isinstance(time, str | datetime | pd.Timestamp | np.datetime64):
|
|
76
78
|
time = (time, time)
|
|
77
79
|
elif len(time) == 1:
|
|
78
80
|
time = (time[0], time[0])
|
|
79
81
|
elif len(time) != 2:
|
|
80
|
-
msg = f"Input time bounds must have length
|
|
82
|
+
msg = f"Input time bounds must have length 1 or 2, got {len(time)}"
|
|
81
83
|
raise ValueError(msg)
|
|
82
84
|
|
|
83
85
|
# convert all to pandas Timestamp
|
|
84
86
|
try:
|
|
85
|
-
|
|
87
|
+
t0, t1 = (pd.to_datetime(t) for t in time)
|
|
86
88
|
except ValueError as e:
|
|
87
89
|
msg = (
|
|
88
90
|
f"Failed to parse time input {time}. "
|
|
@@ -91,10 +93,13 @@ def parse_timesteps(time: TimeInput | None, freq: str | None = "1h") -> list[dat
|
|
|
91
93
|
raise ValueError(msg) from e
|
|
92
94
|
|
|
93
95
|
if freq is None:
|
|
94
|
-
daterange = pd.DatetimeIndex([
|
|
96
|
+
daterange = pd.DatetimeIndex([t0, t1])
|
|
95
97
|
else:
|
|
96
98
|
# get date range that encompasses all whole hours
|
|
97
|
-
daterange = pd.date_range(
|
|
99
|
+
daterange = pd.date_range(t0.floor(freq), t1.ceil(freq), freq=freq)
|
|
100
|
+
if len(daterange) == 0:
|
|
101
|
+
msg = f"Time range {t0} to {t1} with freq {freq} has no valid time steps."
|
|
102
|
+
raise ValueError(msg)
|
|
98
103
|
|
|
99
104
|
# return list of datetimes
|
|
100
105
|
return daterange.to_pydatetime().tolist()
|
|
@@ -151,7 +156,7 @@ def parse_pressure_levels(
|
|
|
151
156
|
Raises ValueError if pressure level is not supported by ECMWF data source
|
|
152
157
|
"""
|
|
153
158
|
# Ensure pressure_levels is array-like
|
|
154
|
-
if isinstance(pressure_levels,
|
|
159
|
+
if isinstance(pressure_levels, int | float):
|
|
155
160
|
pressure_levels = [pressure_levels]
|
|
156
161
|
|
|
157
162
|
# Cast array-like to int dtype and sort
|
|
@@ -212,7 +217,7 @@ def parse_variables(variables: VariableInput, supported: list[MetVariable]) -> l
|
|
|
212
217
|
met_var_list: list[MetVariable] = []
|
|
213
218
|
|
|
214
219
|
# ensure input variables are a list of str
|
|
215
|
-
if isinstance(variables,
|
|
220
|
+
if isinstance(variables, str | int | MetVariable):
|
|
216
221
|
parsed_variables = [variables]
|
|
217
222
|
elif isinstance(variables, np.ndarray):
|
|
218
223
|
parsed_variables = variables.tolist()
|
|
@@ -257,7 +262,7 @@ def _find_match(
|
|
|
257
262
|
|
|
258
263
|
# list of MetVariable options
|
|
259
264
|
# here we extract the first MetVariable in var that is supported
|
|
260
|
-
elif isinstance(var,
|
|
265
|
+
elif isinstance(var, list | tuple):
|
|
261
266
|
for v in var:
|
|
262
267
|
# sanity check since we don't support other types as lists
|
|
263
268
|
if not isinstance(v, MetVariable):
|
|
@@ -63,7 +63,7 @@ class ECMWFAPI(metsource.MetDataSource):
|
|
|
63
63
|
except KeyError as exc:
|
|
64
64
|
# this snippet shows the missing times for convenience
|
|
65
65
|
np_timesteps = {np.datetime64(t, "ns") for t in self.timesteps}
|
|
66
|
-
missing_times = sorted(np_timesteps.difference(ds["time"].values))
|
|
66
|
+
missing_times = sorted(np_timesteps.difference(ds["time"].values)) # type: ignore[type-var]
|
|
67
67
|
msg = f"Input dataset is missing time coordinates {[str(t) for t in missing_times]}"
|
|
68
68
|
raise KeyError(msg) from exc
|
|
69
69
|
|
|
@@ -80,10 +80,17 @@ class ERA5(ECMWFAPI):
|
|
|
80
80
|
Cache data store for staging ECMWF ERA5 files.
|
|
81
81
|
Defaults to :class:`cache.DiskCacheStore`.
|
|
82
82
|
If None, cache is turned off.
|
|
83
|
-
url : str
|
|
84
|
-
Override `cdsapi <https://github.com/ecmwf/cdsapi>`_ url
|
|
85
|
-
|
|
86
|
-
|
|
83
|
+
url : str | None
|
|
84
|
+
Override the default `cdsapi <https://github.com/ecmwf/cdsapi>`_ url.
|
|
85
|
+
As of August 2024, the url for the `CDS-Beta <https://cds-beta.climate.copernicus.eu>`_
|
|
86
|
+
is "https://cds-beta.climate.copernicus.eu/api", and the url for the legacy server is
|
|
87
|
+
"https://cds.climate.copernicus.eu/api/v2". If None, the url is set
|
|
88
|
+
by the ``CDSAPI_URL`` environment variable. If this is not defined, the
|
|
89
|
+
``cdsapi`` package will determine the url.
|
|
90
|
+
key : str | None
|
|
91
|
+
Override default `cdsapi <https://github.com/ecmwf/cdsapi>`_ key. If None,
|
|
92
|
+
the key is set by the ``CDSAPI_KEY`` environment variable. If this is not defined,
|
|
93
|
+
the ``cdsapi`` package will determine the key.
|
|
87
94
|
|
|
88
95
|
Notes
|
|
89
96
|
-----
|
|
@@ -121,7 +128,7 @@ class ERA5(ECMWFAPI):
|
|
|
121
128
|
... pressure_levels=[350, 300],
|
|
122
129
|
... cachestore=gcp_cache
|
|
123
130
|
... )
|
|
124
|
-
"""
|
|
131
|
+
"""
|
|
125
132
|
|
|
126
133
|
__slots__ = (
|
|
127
134
|
"product_type",
|
|
@@ -522,17 +529,21 @@ class ERA5(ECMWFAPI):
|
|
|
522
529
|
xr.Dataset
|
|
523
530
|
Processed :class:`xr.Dataset`
|
|
524
531
|
"""
|
|
525
|
-
|
|
526
532
|
if "pycontrails_version" in ds.attrs:
|
|
527
533
|
LOG.debug("Input dataset processed with pycontrails > 0.29")
|
|
528
534
|
return ds
|
|
529
535
|
|
|
530
|
-
#
|
|
531
|
-
|
|
532
|
-
# for "reanalysis-era5-single-levels" or if self.pressure_levels length == 1,
|
|
536
|
+
# For "reanalysis-era5-single-levels" or if self.pressure_levels length == 1,
|
|
533
537
|
# then the netcdf file does not contain the dimension "level"
|
|
534
538
|
if len(self.pressure_levels) == 1:
|
|
535
|
-
ds = ds.expand_dims(
|
|
539
|
+
ds = ds.expand_dims(level=self.pressure_levels)
|
|
540
|
+
|
|
541
|
+
# New CDS-Beta gives "valid_time" instead of "time"
|
|
542
|
+
# and "pressure_level" instead of "level"
|
|
543
|
+
if "valid_time" in ds:
|
|
544
|
+
ds = ds.rename(valid_time="time")
|
|
545
|
+
if "pressure_level" in ds:
|
|
546
|
+
ds = ds.rename(pressure_level="level")
|
|
536
547
|
|
|
537
548
|
ds.attrs["pycontrails_version"] = pycontrails.__version__
|
|
538
549
|
return ds
|
|
@@ -54,8 +54,8 @@ ALL_ENSEMBLE_MEMBERS = list(range(10))
|
|
|
54
54
|
class ERA5ModelLevel(ECMWFAPI):
|
|
55
55
|
"""Class to support model-level ERA5 data access, download, and organization.
|
|
56
56
|
|
|
57
|
-
The interface is similar to :class:`pycontrails.datalib.ecmwf.ERA5`, which downloads
|
|
58
|
-
with much lower vertical resolution.
|
|
57
|
+
The interface is similar to :class:`pycontrails.datalib.ecmwf.ERA5`, which downloads
|
|
58
|
+
pressure-level with much lower vertical resolution.
|
|
59
59
|
|
|
60
60
|
Requires account with
|
|
61
61
|
`Copernicus Data Portal <https://cds.climate.copernicus.eu/cdsapp#!/home>`_
|
|
@@ -114,11 +114,18 @@ class ERA5ModelLevel(ECMWFAPI):
|
|
|
114
114
|
cache_grib: bool, optional
|
|
115
115
|
If True, cache downloaded GRIB files rather than storing them in a temporary file.
|
|
116
116
|
By default, False.
|
|
117
|
-
url : str
|
|
118
|
-
Override `cdsapi <https://github.com/ecmwf/cdsapi>`_ url
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
117
|
+
url : str | None
|
|
118
|
+
Override the default `cdsapi <https://github.com/ecmwf/cdsapi>`_ url.
|
|
119
|
+
As of August 2024, the url for the `CDS-Beta <https://cds-beta.climate.copernicus.eu>`_
|
|
120
|
+
is "https://cds-beta.climate.copernicus.eu/api", and the url for the legacy server is
|
|
121
|
+
"https://cds.climate.copernicus.eu/api/v2". If None, the url is set
|
|
122
|
+
by the ``CDSAPI_URL`` environment variable. If this is not defined, the
|
|
123
|
+
``cdsapi`` package will determine the url.
|
|
124
|
+
key : str | None
|
|
125
|
+
Override default `cdsapi <https://github.com/ecmwf/cdsapi>`_ key. If None,
|
|
126
|
+
the key is set by the ``CDSAPI_KEY`` environment variable. If this is not defined,
|
|
127
|
+
the ``cdsapi`` package will determine the key.
|
|
128
|
+
"""
|
|
122
129
|
|
|
123
130
|
__marker = object()
|
|
124
131
|
|
|
@@ -349,7 +356,7 @@ class ERA5ModelLevel(ECMWFAPI):
|
|
|
349
356
|
unique_dates = set(t.strftime("%Y-%m-%d") for t in times)
|
|
350
357
|
unique_times = set(t.strftime("%H:%M:%S") for t in times)
|
|
351
358
|
# param 152 = log surface pressure, needed for metview level conversion
|
|
352
|
-
grib_params = set(self.variable_ecmwfids
|
|
359
|
+
grib_params = set((*self.variable_ecmwfids, 152))
|
|
353
360
|
common = {
|
|
354
361
|
"class": "ea",
|
|
355
362
|
"date": "/".join(sorted(unique_dates)),
|
|
@@ -377,7 +377,7 @@ class HRESModelLevel(ECMWFAPI):
|
|
|
377
377
|
time = self.forecast_time.strftime("%H:%M:%S")
|
|
378
378
|
steps = self.get_forecast_steps(times)
|
|
379
379
|
# param 152 = log surface pressure, needed for metview level conversion
|
|
380
|
-
grib_params = set(self.variable_ecmwfids
|
|
380
|
+
grib_params = set((*self.variable_ecmwfids, 152))
|
|
381
381
|
return (
|
|
382
382
|
f"retrieve,\n"
|
|
383
383
|
f"class=od,\n"
|
|
@@ -473,8 +473,8 @@ class HRESModelLevel(ECMWFAPI):
|
|
|
473
473
|
LOG.debug("Opening GRIB file")
|
|
474
474
|
fs_ml = mv.read(target)
|
|
475
475
|
|
|
476
|
-
# reduce memory overhead by
|
|
477
|
-
for time, step in zip(times, self.get_forecast_steps(times)):
|
|
476
|
+
# reduce memory overhead by caching one timestep at a time
|
|
477
|
+
for time, step in zip(times, self.get_forecast_steps(times), strict=True):
|
|
478
478
|
fs_pl = mv.Fieldset()
|
|
479
479
|
selection = dict(step=step)
|
|
480
480
|
lnsp = fs_ml.select(shortName="lnsp", **selection)
|
|
@@ -118,7 +118,7 @@ RelativeHumidity = MetVariable(
|
|
|
118
118
|
"At temperatures below -23°C it is calculated for saturation over ice. "
|
|
119
119
|
"Between -23°C and 0°C this parameter is calculated by interpolating between the ice and"
|
|
120
120
|
" water values using a quadratic function."
|
|
121
|
-
"See https://www.ecmwf.int/sites/default/files/elibrary/2016/17117-part-iv-physical-processes.pdf#subsection.7.4.2"
|
|
121
|
+
"See https://www.ecmwf.int/sites/default/files/elibrary/2016/17117-part-iv-physical-processes.pdf#subsection.7.4.2"
|
|
122
122
|
),
|
|
123
123
|
)
|
|
124
124
|
|
|
@@ -166,7 +166,7 @@ TopNetSolarRadiation = MetVariable(
|
|
|
166
166
|
"The incoming solar radiation is the amount received from the Sun. "
|
|
167
167
|
"The outgoing solar radiation is the amount reflected and scattered by the Earth's"
|
|
168
168
|
" atmosphere and surface"
|
|
169
|
-
"See https://www.ecmwf.int/sites/default/files/elibrary/2015/18490-radiation-quantities-ecmwf-model-and-mars.pdf"
|
|
169
|
+
"See https://www.ecmwf.int/sites/default/files/elibrary/2015/18490-radiation-quantities-ecmwf-model-and-mars.pdf"
|
|
170
170
|
),
|
|
171
171
|
)
|
|
172
172
|
|
|
@@ -183,7 +183,7 @@ TopNetThermalRadiation = MetVariable(
|
|
|
183
183
|
"radiation emitted to space at the top of the atmosphere is commonly known as the Outgoing"
|
|
184
184
|
" Longwave Radiation (OLR). "
|
|
185
185
|
"The top net thermal radiation (this parameter) is equal to the negative of OLR."
|
|
186
|
-
"See https://www.ecmwf.int/sites/default/files/elibrary/2015/18490-radiation-quantities-ecmwf-model-and-mars.pdf"
|
|
186
|
+
"See https://www.ecmwf.int/sites/default/files/elibrary/2015/18490-radiation-quantities-ecmwf-model-and-mars.pdf"
|
|
187
187
|
),
|
|
188
188
|
)
|
|
189
189
|
|
pycontrails/datalib/gfs/gfs.py
CHANGED
|
@@ -13,8 +13,9 @@ import hashlib
|
|
|
13
13
|
import logging
|
|
14
14
|
import pathlib
|
|
15
15
|
import warnings
|
|
16
|
+
from collections.abc import Callable
|
|
16
17
|
from datetime import datetime
|
|
17
|
-
from typing import TYPE_CHECKING, Any
|
|
18
|
+
from typing import TYPE_CHECKING, Any
|
|
18
19
|
|
|
19
20
|
import numpy as np
|
|
20
21
|
import pandas as pd
|
|
@@ -113,7 +114,7 @@ class GFSForecast(metsource.MetDataSource):
|
|
|
113
114
|
- `Documentation <https://www.ncei.noaa.gov/products/weather-climate-models/global-forecast>`_
|
|
114
115
|
- `Parameter sets <https://www.nco.ncep.noaa.gov/pmb/products/gfs/>`_
|
|
115
116
|
- `GFS Documentation <https://www.emc.ncep.noaa.gov/emc/pages/numerical_forecast_systems/gfs/documentation.php>`_
|
|
116
|
-
"""
|
|
117
|
+
"""
|
|
117
118
|
|
|
118
119
|
__slots__ = ("client", "grid", "cachestore", "show_progress", "forecast_time")
|
|
119
120
|
|
|
@@ -495,7 +496,7 @@ class GFSForecast(metsource.MetDataSource):
|
|
|
495
496
|
GFS dataset
|
|
496
497
|
"""
|
|
497
498
|
# translate into netcdf from grib
|
|
498
|
-
logger.debug(f"Translating {filepath} for timestep {
|
|
499
|
+
logger.debug(f"Translating {filepath} for timestep {t!s} into netcdf")
|
|
499
500
|
|
|
500
501
|
# get step for timestep
|
|
501
502
|
step = pd.Timedelta(t - self.forecast_time) // pd.Timedelta(1, "h")
|
pycontrails/datalib/landsat.py
CHANGED
|
@@ -33,15 +33,6 @@ except ModuleNotFoundError as exc:
|
|
|
33
33
|
pycontrails_optional_package="sat",
|
|
34
34
|
)
|
|
35
35
|
|
|
36
|
-
try:
|
|
37
|
-
import rasterio
|
|
38
|
-
except ModuleNotFoundError as exc:
|
|
39
|
-
dependencies.raise_module_not_found_error(
|
|
40
|
-
name="landsat module",
|
|
41
|
-
package_name="rasterio",
|
|
42
|
-
module_not_found_error=exc,
|
|
43
|
-
pycontrails_optional_package="sat",
|
|
44
|
-
)
|
|
45
36
|
|
|
46
37
|
#: BigQuery table with imagery metadata
|
|
47
38
|
BQ_TABLE = "bigquery-public-data.cloud_storage_geo_index.landsat_index"
|
|
@@ -313,6 +304,16 @@ def _check_band_resolution(bands: set[str]) -> None:
|
|
|
313
304
|
|
|
314
305
|
def _read(path: str, meta: str, band: str, processing: str) -> xr.DataArray:
|
|
315
306
|
"""Read imagery data from Landsat files."""
|
|
307
|
+
try:
|
|
308
|
+
import rasterio
|
|
309
|
+
except ModuleNotFoundError as exc:
|
|
310
|
+
dependencies.raise_module_not_found_error(
|
|
311
|
+
name="landsat module",
|
|
312
|
+
package_name="rasterio",
|
|
313
|
+
module_not_found_error=exc,
|
|
314
|
+
pycontrails_optional_package="sat",
|
|
315
|
+
)
|
|
316
|
+
|
|
316
317
|
src = rasterio.open(path)
|
|
317
318
|
img = src.read(1)
|
|
318
319
|
crs = pyproj.CRS.from_epsg(src.crs.to_epsg())
|
|
@@ -417,7 +417,7 @@ class SyntheticFlight:
|
|
|
417
417
|
times_arr = np.asarray(times).T
|
|
418
418
|
data = [
|
|
419
419
|
{"longitude": lon, "latitude": lat, "level": level, "time": time}
|
|
420
|
-
for lon, lat, level, time in zip(lons_arr, lats_arr, level, times_arr)
|
|
420
|
+
for lon, lat, level, time in zip(lons_arr, lats_arr, level, times_arr, strict=True)
|
|
421
421
|
]
|
|
422
422
|
dfs = [pd.DataFrame(d).dropna() for d in data]
|
|
423
423
|
dfs = [df for df in dfs if len(df) >= self.min_n_waypoints]
|
pycontrails/models/accf.py
CHANGED
|
@@ -154,7 +154,7 @@ class ACCF(Model):
|
|
|
154
154
|
sur_variables = (ecmwf.SurfaceSolarDownwardRadiation, ecmwf.TopNetThermalRadiation)
|
|
155
155
|
default_params = ACCFParams
|
|
156
156
|
|
|
157
|
-
short_vars =
|
|
157
|
+
short_vars = frozenset(v.short_name for v in (*met_variables, *sur_variables))
|
|
158
158
|
|
|
159
159
|
# This variable won't get used since we are not writing the output
|
|
160
160
|
# anywhere, but the library will complain if it's not defined
|
|
@@ -84,10 +84,10 @@ class APCEMMParams(models.ModelParams):
|
|
|
84
84
|
engine_uid: str | None = None
|
|
85
85
|
|
|
86
86
|
#: Aircraft performance model
|
|
87
|
-
aircraft_performance: AircraftPerformance = PSFlight
|
|
87
|
+
aircraft_performance: AircraftPerformance = dataclasses.field(default_factory=PSFlight)
|
|
88
88
|
|
|
89
89
|
#: Fuel type
|
|
90
|
-
fuel: Fuel = JetA
|
|
90
|
+
fuel: Fuel = dataclasses.field(default_factory=JetA)
|
|
91
91
|
|
|
92
92
|
#: List of flight waypoints to simulate in APCEMM.
|
|
93
93
|
#: By default, runs a simulation for every waypoint.
|
|
@@ -371,7 +371,7 @@ class APCEMM(models.Model):
|
|
|
371
371
|
@overload
|
|
372
372
|
def eval(self, source: None = ..., **params: Any) -> NoReturn: ...
|
|
373
373
|
|
|
374
|
-
def eval(self, source: Flight | None = None, **params: Any) -> Flight
|
|
374
|
+
def eval(self, source: Flight | None = None, **params: Any) -> Flight:
|
|
375
375
|
"""Set up and run APCEMM simulations initialized at flight waypoints.
|
|
376
376
|
|
|
377
377
|
Simulates the formation and evolution of contrails from a Flight
|
|
@@ -776,8 +776,8 @@ class APCEMM(models.Model):
|
|
|
776
776
|
# Compute azimuth
|
|
777
777
|
# Use forward and backward differences for first and last waypoints
|
|
778
778
|
# and centered differences elsewhere
|
|
779
|
-
ileft = [0
|
|
780
|
-
iright =
|
|
779
|
+
ileft = [0, *range(self.source.size - 1)]
|
|
780
|
+
iright = [*range(1, self.source.size), self.source.size - 1]
|
|
781
781
|
lon0 = self.source["longitude"][ileft]
|
|
782
782
|
lat0 = self.source["latitude"][ileft]
|
|
783
783
|
lon1 = self.source["longitude"][iright]
|
|
@@ -335,7 +335,7 @@ class Cocip(Model):
|
|
|
335
335
|
self,
|
|
336
336
|
source: Flight | Sequence[Flight] | None = None,
|
|
337
337
|
**params: Any,
|
|
338
|
-
) -> Flight | list[Flight]
|
|
338
|
+
) -> Flight | list[Flight]:
|
|
339
339
|
"""Run CoCiP simulation on flight.
|
|
340
340
|
|
|
341
341
|
Simulates the formation and evolution of contrails from a Flight
|
|
@@ -1132,7 +1132,7 @@ class Cocip(Model):
|
|
|
1132
1132
|
)
|
|
1133
1133
|
vector = GeoVectorDataset(
|
|
1134
1134
|
{
|
|
1135
|
-
key: np.
|
|
1135
|
+
key: np.concatenate((latest_contrail[key], future_contrails[key]))
|
|
1136
1136
|
for key in ("longitude", "latitude", "level", "time")
|
|
1137
1137
|
}
|
|
1138
1138
|
)
|
|
@@ -1516,7 +1516,7 @@ def _process_rad(rad: MetDataset) -> MetDataset:
|
|
|
1516
1516
|
-----
|
|
1517
1517
|
- https://www.ecmwf.int/sites/default/files/elibrary/2015/18490-radiation-quantities-ecmwf-model-and-mars.pdf
|
|
1518
1518
|
- https://confluence.ecmwf.int/pages/viewpage.action?pageId=155337784
|
|
1519
|
-
"""
|
|
1519
|
+
"""
|
|
1520
1520
|
# If the time coordinate has already been shifted, early return
|
|
1521
1521
|
if "shift_radiation_time" in rad["time"].attrs:
|
|
1522
1522
|
return rad
|
|
@@ -638,7 +638,7 @@ def regional_statistics(da_var: xr.DataArray, *, agg: str) -> pd.Series:
|
|
|
638
638
|
-----
|
|
639
639
|
- The spatial bounding box for each region is defined in Teoh et al. (2023)
|
|
640
640
|
- Teoh, R., Engberg, Z., Shapiro, M., Dray, L., and Stettler, M.: A high-resolution Global
|
|
641
|
-
Aviation emissions Inventory based on ADS-B (GAIA) for 2019
|
|
641
|
+
Aviation emissions Inventory based on ADS-B (GAIA) for 2019-2021, EGUsphere [preprint],
|
|
642
642
|
https://doi.org/10.5194/egusphere-2023-724, 2023.
|
|
643
643
|
"""
|
|
644
644
|
if (agg == "mean") and (len(da_var.time) > 1):
|
|
@@ -715,7 +715,7 @@ def _regional_data_arrays(da_global: xr.DataArray) -> dict[str, xr.DataArray]:
|
|
|
715
715
|
-----
|
|
716
716
|
- The spatial bounding box for each region is defined in Teoh et al. (2023)
|
|
717
717
|
- Teoh, R., Engberg, Z., Shapiro, M., Dray, L., and Stettler, M.: A high-resolution Global
|
|
718
|
-
Aviation emissions Inventory based on ADS-B (GAIA) for 2019
|
|
718
|
+
Aviation emissions Inventory based on ADS-B (GAIA) for 2019-2021, EGUsphere [preprint],
|
|
719
719
|
https://doi.org/10.5194/egusphere-2023-724, 2023.
|
|
720
720
|
"""
|
|
721
721
|
return {
|
|
@@ -10,6 +10,7 @@ References
|
|
|
10
10
|
from __future__ import annotations
|
|
11
11
|
|
|
12
12
|
import dataclasses
|
|
13
|
+
import itertools
|
|
13
14
|
|
|
14
15
|
import numpy as np
|
|
15
16
|
import numpy.typing as npt
|
|
@@ -941,7 +942,7 @@ def contrail_contrail_overlap_radiative_effects(
|
|
|
941
942
|
References
|
|
942
943
|
----------
|
|
943
944
|
- Schumann et al. (2021) Air traffic and contrail changes over Europe during COVID-19:
|
|
944
|
-
A model study, Atmos. Chem. Phys., 21, 7429
|
|
945
|
+
A model study, Atmos. Chem. Phys., 21, 7429-7450, https://doi.org/10.5194/ACP-21-7429-2021.
|
|
945
946
|
- Teoh et al. (2023) Global aviation contrail climate effects from 2019 to 2021.
|
|
946
947
|
|
|
947
948
|
Notes
|
|
@@ -999,8 +1000,7 @@ def contrail_contrail_overlap_radiative_effects(
|
|
|
999
1000
|
# Account for contrail overlapping starting from bottom to top layers
|
|
1000
1001
|
altitude_layers = np.arange(min_altitude_m, max_altitude_m + 1.0, dz_overlap_m)
|
|
1001
1002
|
|
|
1002
|
-
|
|
1003
|
-
for alt_layer0, alt_layer1 in zip(altitude_layers, altitude_layers[1:]):
|
|
1003
|
+
for alt_layer0, alt_layer1 in itertools.pairwise(altitude_layers):
|
|
1004
1004
|
is_in_layer = (altitude >= alt_layer0) & (altitude < alt_layer1)
|
|
1005
1005
|
|
|
1006
1006
|
# Get contrail waypoints at current altitude layer
|
|
@@ -143,7 +143,7 @@ class CocipGrid(models.Model):
|
|
|
143
143
|
|
|
144
144
|
def eval(
|
|
145
145
|
self, source: GeoVectorDataset | MetDataset | None = None, **params: Any
|
|
146
|
-
) -> GeoVectorDataset | MetDataset
|
|
146
|
+
) -> GeoVectorDataset | MetDataset:
|
|
147
147
|
"""Run CoCiP simulation on a 4d coordinate grid or arbitrary set of 4d points.
|
|
148
148
|
|
|
149
149
|
If the :attr:`params` ``verbose_outputs_evolution`` is True, the model holds
|
|
@@ -322,7 +322,7 @@ class CocipGrid(models.Model):
|
|
|
322
322
|
|
|
323
323
|
if met is None:
|
|
324
324
|
# idx is the first index at which self.met.variables["time"].to_numpy() >= time_end
|
|
325
|
-
idx = np.searchsorted(self.met.indexes["time"].to_numpy(), time_end)
|
|
325
|
+
idx = np.searchsorted(self.met.indexes["time"].to_numpy(), time_end).item()
|
|
326
326
|
sl = slice(max(0, idx - 1), idx + 1)
|
|
327
327
|
logger.debug("Select met slice %s", sl)
|
|
328
328
|
met = MetDataset(self.met.data.isel(time=sl), copy=False)
|
|
@@ -331,7 +331,7 @@ class CocipGrid(models.Model):
|
|
|
331
331
|
current_times = met.indexes["time"].to_numpy()
|
|
332
332
|
all_times = self.met.indexes["time"].to_numpy()
|
|
333
333
|
# idx is the first index at which all_times >= time_end
|
|
334
|
-
idx = np.searchsorted(all_times, time_end)
|
|
334
|
+
idx = np.searchsorted(all_times, time_end).item()
|
|
335
335
|
sl = slice(max(0, idx - 1), idx + 1)
|
|
336
336
|
|
|
337
337
|
# case 1: cannot re-use end of current met as start of new met
|
|
@@ -353,7 +353,7 @@ class CocipGrid(models.Model):
|
|
|
353
353
|
|
|
354
354
|
if rad is None:
|
|
355
355
|
# idx is the first index at which self.rad.variables["time"].to_numpy() >= time_end
|
|
356
|
-
idx = np.searchsorted(self.rad.indexes["time"].to_numpy(), time_end)
|
|
356
|
+
idx = np.searchsorted(self.rad.indexes["time"].to_numpy(), time_end).item()
|
|
357
357
|
sl = slice(max(0, idx - 1), idx + 1)
|
|
358
358
|
logger.debug("Select rad slice %s", sl)
|
|
359
359
|
rad = MetDataset(self.rad.data.isel(time=sl), copy=False)
|
|
@@ -362,7 +362,7 @@ class CocipGrid(models.Model):
|
|
|
362
362
|
current_times = rad.indexes["time"].to_numpy()
|
|
363
363
|
all_times = self.rad.indexes["time"].to_numpy()
|
|
364
364
|
# idx is the first index at which all_times >= time_end
|
|
365
|
-
idx = np.searchsorted(all_times, time_end)
|
|
365
|
+
idx = np.searchsorted(all_times, time_end).item()
|
|
366
366
|
sl = slice(max(0, idx - 1), idx + 1)
|
|
367
367
|
|
|
368
368
|
# case 1: cannot re-use end of current rad as start of new rad
|
|
@@ -446,9 +446,9 @@ class CocipGrid(models.Model):
|
|
|
446
446
|
if ap_model := self.params["aircraft_performance"]:
|
|
447
447
|
attrs["ap_model"] = type(ap_model).__name__
|
|
448
448
|
|
|
449
|
-
if isinstance(azimuth,
|
|
449
|
+
if isinstance(azimuth, np.floating | np.integer):
|
|
450
450
|
attrs["azimuth"] = azimuth.item()
|
|
451
|
-
elif isinstance(azimuth,
|
|
451
|
+
elif isinstance(azimuth, float | int):
|
|
452
452
|
attrs["azimuth"] = azimuth
|
|
453
453
|
|
|
454
454
|
if isinstance(self.source, MetDataset):
|
|
@@ -897,7 +897,7 @@ def _setdefault_from_params(key: str, vector: GeoVectorDataset, params: dict[str
|
|
|
897
897
|
if scalar is None:
|
|
898
898
|
return
|
|
899
899
|
|
|
900
|
-
if not isinstance(scalar,
|
|
900
|
+
if not isinstance(scalar, int | float):
|
|
901
901
|
msg = (
|
|
902
902
|
f"Parameter {key} must be a scalar. For non-scalar values, directly "
|
|
903
903
|
"set the data on the 'source'."
|
|
@@ -312,7 +312,7 @@ class PSFlight(AircraftPerformance):
|
|
|
312
312
|
atyp_param.wing_surface_area,
|
|
313
313
|
q_fuel,
|
|
314
314
|
)
|
|
315
|
-
elif isinstance(fuel_flow,
|
|
315
|
+
elif isinstance(fuel_flow, int | float):
|
|
316
316
|
fuel_flow = np.full_like(true_airspeed, fuel_flow)
|
|
317
317
|
|
|
318
318
|
# Flight phase
|
|
@@ -339,11 +339,11 @@ class PSFlight(AircraftPerformance):
|
|
|
339
339
|
|
|
340
340
|
# XXX: Explicitly broadcast scalar inputs as needed to keep a consistent
|
|
341
341
|
# output spec.
|
|
342
|
-
if isinstance(aircraft_mass,
|
|
342
|
+
if isinstance(aircraft_mass, int | float):
|
|
343
343
|
aircraft_mass = np.full_like(true_airspeed, aircraft_mass)
|
|
344
|
-
if isinstance(engine_efficiency,
|
|
344
|
+
if isinstance(engine_efficiency, int | float):
|
|
345
345
|
engine_efficiency = np.full_like(true_airspeed, engine_efficiency)
|
|
346
|
-
if isinstance(thrust,
|
|
346
|
+
if isinstance(thrust, int | float):
|
|
347
347
|
thrust = np.full_like(true_airspeed, thrust)
|
|
348
348
|
|
|
349
349
|
return AircraftPerformanceData(
|
pycontrails/models/sac.py
CHANGED
|
@@ -150,7 +150,7 @@ class SAC(Model):
|
|
|
150
150
|
if scale_humidity:
|
|
151
151
|
for k, v in humidity_scaling.description.items():
|
|
152
152
|
self.source.attrs[f"humidity_scaling_{k}"] = v
|
|
153
|
-
if isinstance(engine_efficiency,
|
|
153
|
+
if isinstance(engine_efficiency, int | float):
|
|
154
154
|
self.source.attrs["engine_efficiency"] = engine_efficiency
|
|
155
155
|
|
|
156
156
|
return self.source
|
|
@@ -236,7 +236,7 @@ def T_sat_liquid(G: ArrayLike) -> ArrayLike:
|
|
|
236
236
|
# This comment is pasted several places in `pycontrails` -- they should all be
|
|
237
237
|
# addressed at the same time.
|
|
238
238
|
log_ = np.log(G - 0.053)
|
|
239
|
-
return -46.46 - constants.absolute_zero + 9.43 * log_ + 0.72 * log_**2 # type: ignore[return-value]
|
|
239
|
+
return -46.46 - constants.absolute_zero + 9.43 * log_ + 0.72 * log_**2 # type: ignore[return-value]
|
|
240
240
|
|
|
241
241
|
|
|
242
242
|
def _e_sat_liquid_prime(T: ArrayScalarLike) -> ArrayScalarLike:
|
pycontrails/physics/thermo.py
CHANGED
|
@@ -163,7 +163,7 @@ def e_sat_liquid(T: ArrayScalarLike) -> ArrayScalarLike:
|
|
|
163
163
|
# 6.1121 * np.exp((18.678 * (T - 273.15) / 234.5) * (T - 273.15) / (257.14 + (T - 273.15)))
|
|
164
164
|
|
|
165
165
|
# Magnus Tetens (Murray, 1967)
|
|
166
|
-
# 6.1078 * np.exp(17.269388 * (T - 273.16) / (T
|
|
166
|
+
# 6.1078 * np.exp(17.269388 * (T - 273.16) / (T - 35.86))
|
|
167
167
|
|
|
168
168
|
# Guide to Meteorological Instruments and Methods of Observation (CIMO Guide) (WMO, 2008)
|
|
169
169
|
# 6.112 * np.exp(17.62 * (T - 273.15) / (243.12 + T - 273.15))
|
pycontrails/utils/json.py
CHANGED
|
@@ -48,23 +48,21 @@ class NumpyEncoder(json.JSONEncoder):
|
|
|
48
48
|
"""
|
|
49
49
|
if isinstance(
|
|
50
50
|
obj,
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
np.uint64,
|
|
63
|
-
),
|
|
51
|
+
np.int_
|
|
52
|
+
| np.intc
|
|
53
|
+
| np.intp
|
|
54
|
+
| np.int8
|
|
55
|
+
| np.int16
|
|
56
|
+
| np.int32
|
|
57
|
+
| np.int64
|
|
58
|
+
| np.uint8
|
|
59
|
+
| np.uint16
|
|
60
|
+
| np.uint32
|
|
61
|
+
| np.uint64,
|
|
64
62
|
):
|
|
65
63
|
return int(obj)
|
|
66
64
|
|
|
67
|
-
if isinstance(obj,
|
|
65
|
+
if isinstance(obj, np.float16 | np.float32 | np.float64):
|
|
68
66
|
return float(obj)
|
|
69
67
|
|
|
70
68
|
# TODO: this is not easily reversible - np.timedelta64(str(np.timedelta64(1, "h"))) raises
|
|
@@ -74,10 +72,10 @@ class NumpyEncoder(json.JSONEncoder):
|
|
|
74
72
|
if isinstance(obj, (np.datetime64)):
|
|
75
73
|
return str(obj)
|
|
76
74
|
|
|
77
|
-
if isinstance(obj,
|
|
75
|
+
if isinstance(obj, np.complex64 | np.complex128):
|
|
78
76
|
return {"real": obj.real, "imag": obj.imag}
|
|
79
77
|
|
|
80
|
-
if isinstance(obj,
|
|
78
|
+
if isinstance(obj, np.ndarray):
|
|
81
79
|
return obj.tolist()
|
|
82
80
|
|
|
83
81
|
if isinstance(obj, (np.bool_)):
|
|
@@ -86,7 +84,7 @@ class NumpyEncoder(json.JSONEncoder):
|
|
|
86
84
|
if isinstance(obj, (np.void)):
|
|
87
85
|
return None
|
|
88
86
|
|
|
89
|
-
if isinstance(obj,
|
|
87
|
+
if isinstance(obj, pd.Series | pd.Index):
|
|
90
88
|
return obj.to_numpy().tolist()
|
|
91
89
|
|
|
92
90
|
try:
|
|
@@ -146,7 +144,7 @@ def dataframe_to_geojson_points(
|
|
|
146
144
|
)
|
|
147
145
|
|
|
148
146
|
# downselect dataframe
|
|
149
|
-
cols = ["longitude", "latitude", "altitude", "time"
|
|
147
|
+
cols = ["longitude", "latitude", "altitude", "time", *properties]
|
|
150
148
|
df = df[cols]
|
|
151
149
|
|
|
152
150
|
# filter out coords with nan values, or filter just on "filter_nan" labels
|
pycontrails/utils/types.py
CHANGED
|
@@ -3,8 +3,9 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
import functools
|
|
6
|
+
from collections.abc import Callable
|
|
6
7
|
from datetime import datetime
|
|
7
|
-
from typing import Any,
|
|
8
|
+
from typing import Any, TypeVar
|
|
8
9
|
|
|
9
10
|
import numpy as np
|
|
10
11
|
import numpy.typing as npt
|
|
@@ -12,11 +13,11 @@ import pandas as pd
|
|
|
12
13
|
import xarray as xr
|
|
13
14
|
|
|
14
15
|
#: Array like (np.ndarray, xr.DataArray)
|
|
15
|
-
ArrayLike = TypeVar("ArrayLike", np.ndarray, xr.DataArray,
|
|
16
|
+
ArrayLike = TypeVar("ArrayLike", np.ndarray, xr.DataArray, xr.DataArray | np.ndarray)
|
|
16
17
|
|
|
17
18
|
#: Array or Float (np.ndarray, float)
|
|
18
19
|
ArrayOrFloat = TypeVar(
|
|
19
|
-
"ArrayOrFloat", npt.NDArray[np.float64], float,
|
|
20
|
+
"ArrayOrFloat", npt.NDArray[np.float64], float, float | npt.NDArray[np.float64]
|
|
20
21
|
)
|
|
21
22
|
|
|
22
23
|
#: Array like input (np.ndarray, xr.DataArray, np.float64, float)
|
|
@@ -26,8 +27,8 @@ ArrayScalarLike = TypeVar(
|
|
|
26
27
|
xr.DataArray,
|
|
27
28
|
np.float64,
|
|
28
29
|
float,
|
|
29
|
-
|
|
30
|
-
|
|
30
|
+
np.ndarray | float,
|
|
31
|
+
xr.DataArray | np.ndarray,
|
|
31
32
|
)
|
|
32
33
|
|
|
33
34
|
#: Datetime like input (datetime, pd.Timestamp, np.datetime64)
|
|
@@ -71,7 +72,7 @@ def support_arraylike(
|
|
|
71
72
|
return ret
|
|
72
73
|
|
|
73
74
|
# Keep python native numeric types native
|
|
74
|
-
if isinstance(arr,
|
|
75
|
+
if isinstance(arr, float | int | np.float64):
|
|
75
76
|
return ret.item()
|
|
76
77
|
|
|
77
78
|
# Recreate pd.Series
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: pycontrails
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.53.1
|
|
4
4
|
Summary: Python library for modeling aviation climate impacts
|
|
5
5
|
Author-email: Breakthrough Energy <py@contrails.org>
|
|
6
6
|
License: Apache-2.0
|
|
@@ -14,16 +14,16 @@ Classifier: Intended Audience :: Science/Research
|
|
|
14
14
|
Classifier: License :: OSI Approved :: Apache Software License
|
|
15
15
|
Classifier: Operating System :: OS Independent
|
|
16
16
|
Classifier: Programming Language :: Python :: 3
|
|
17
|
-
Classifier: Programming Language :: Python :: 3.9
|
|
18
17
|
Classifier: Programming Language :: Python :: 3.10
|
|
19
18
|
Classifier: Programming Language :: Python :: 3.11
|
|
20
19
|
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
21
21
|
Classifier: Programming Language :: Python :: 3 :: Only
|
|
22
22
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
23
23
|
Classifier: Topic :: Scientific/Engineering :: Atmospheric Science
|
|
24
24
|
Classifier: Topic :: Scientific/Engineering :: GIS
|
|
25
25
|
Classifier: Typing :: Typed
|
|
26
|
-
Requires-Python: >=3.
|
|
26
|
+
Requires-Python: >=3.10
|
|
27
27
|
Description-Content-Type: text/markdown
|
|
28
28
|
License-File: LICENSE
|
|
29
29
|
License-File: NOTICE
|
|
@@ -36,7 +36,7 @@ Requires-Dist: xarray>=2022.3
|
|
|
36
36
|
Provides-Extra: complete
|
|
37
37
|
Requires-Dist: pycontrails[ecmwf,gcp,gfs,jupyter,pyproj,sat,vis,zarr]; extra == "complete"
|
|
38
38
|
Provides-Extra: dev
|
|
39
|
-
Requires-Dist: black[jupyter]==24.
|
|
39
|
+
Requires-Dist: black[jupyter]==24.8.0; extra == "dev"
|
|
40
40
|
Requires-Dist: dep-license; extra == "dev"
|
|
41
41
|
Requires-Dist: fastparquet>=0.8; extra == "dev"
|
|
42
42
|
Requires-Dist: ipdb>=0.13; extra == "dev"
|
|
@@ -50,7 +50,7 @@ Requires-Dist: pyarrow>=5.0; extra == "dev"
|
|
|
50
50
|
Requires-Dist: pytest>=8.2; extra == "dev"
|
|
51
51
|
Requires-Dist: pytest-cov>=2.11; extra == "dev"
|
|
52
52
|
Requires-Dist: requests>=2.25; extra == "dev"
|
|
53
|
-
Requires-Dist: ruff==0.5.
|
|
53
|
+
Requires-Dist: ruff==0.5.7; extra == "dev"
|
|
54
54
|
Requires-Dist: setuptools; extra == "dev"
|
|
55
55
|
Provides-Extra: docs
|
|
56
56
|
Requires-Dist: doc8>=1.1; extra == "docs"
|
|
@@ -141,7 +141,7 @@ Documentation and examples available at [py.contrails.org](https://py.contrails.
|
|
|
141
141
|
|
|
142
142
|
### Install with pip
|
|
143
143
|
|
|
144
|
-
You can install pycontrails from PyPI with `pip` (Python 3.
|
|
144
|
+
You can install pycontrails from PyPI with `pip` (Python 3.10 or later required):
|
|
145
145
|
|
|
146
146
|
```bash
|
|
147
147
|
$ pip install pycontrails
|
|
@@ -1,67 +1,67 @@
|
|
|
1
|
-
pycontrails-0.
|
|
2
|
-
pycontrails-0.
|
|
3
|
-
pycontrails-0.
|
|
4
|
-
pycontrails-0.
|
|
5
|
-
pycontrails-0.
|
|
6
|
-
pycontrails-0.
|
|
7
|
-
pycontrails/_version.py,sha256=
|
|
1
|
+
pycontrails-0.53.1.dist-info/RECORD,,
|
|
2
|
+
pycontrails-0.53.1.dist-info/LICENSE,sha256=gJ-h7SFFD1mCfR6a7HILvEtodDT6Iig8bLXdgqR6ucA,10175
|
|
3
|
+
pycontrails-0.53.1.dist-info/WHEEL,sha256=jFHkobo6rN-a6CTGUlJ_m4H0MrXln-JQnZbz6nohX5Y,110
|
|
4
|
+
pycontrails-0.53.1.dist-info/NOTICE,sha256=gKI8DcN1WhiXB2SFRKDogcjONldGubTvBxiOYdC4CXU,1926
|
|
5
|
+
pycontrails-0.53.1.dist-info/top_level.txt,sha256=dwaYXVcMhF92QWtAYcLvL0k02vyBqwhsv92lYs2V6zQ,23
|
|
6
|
+
pycontrails-0.53.1.dist-info/METADATA,sha256=zCTO7zFOkvqI0JzbmFthdOuZs7Mdt5vl66qd3z7_x0Q,9173
|
|
7
|
+
pycontrails/_version.py,sha256=vV71ixZB5LSC5mFURNjkJpZgkyEImidyN2Ez0p0YzQk,413
|
|
8
8
|
pycontrails/__init__.py,sha256=O2T9kXCMhcELcMZz7HEnwiBhh4Gfcj-yG1HtrotOKHQ,2001
|
|
9
9
|
pycontrails/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
10
|
-
pycontrails/core/vector.py,sha256=
|
|
11
|
-
pycontrails/core/models.py,sha256=
|
|
12
|
-
pycontrails/core/interpolation.py,sha256=
|
|
10
|
+
pycontrails/core/vector.py,sha256=v_oO7if8SbNPMRci0TR8hLGSdAZ_fGZOVIT47e4hA58,71541
|
|
11
|
+
pycontrails/core/models.py,sha256=mB3fhmBorFxt7uEhBFcuu0PIMWmBRB4KBRsPiFpPcvo,39282
|
|
12
|
+
pycontrails/core/interpolation.py,sha256=yxVLO9lzNcNFeLwDyrQ7yfz4JEHLHTpgIRBrcOezsXg,25617
|
|
13
13
|
pycontrails/core/fleet.py,sha256=wqYY_2xD9X-Og0_oxU8ZPqTHYDau9TOPLQcmEnB1kiQ,16140
|
|
14
|
-
pycontrails/core/rgi_cython.cpython-310-darwin.so,sha256
|
|
15
|
-
pycontrails/core/flight.py,sha256=
|
|
14
|
+
pycontrails/core/rgi_cython.cpython-310-darwin.so,sha256=w28LODiNKFpZoyTEVnI2XWIdXtNqUJSt5Nuqxm6BDxc,295344
|
|
15
|
+
pycontrails/core/flight.py,sha256=_1K6tgwAJZwe18xmkOE7l0JQXzr6XgiqRQGzS6qHpxk,85159
|
|
16
16
|
pycontrails/core/fuel.py,sha256=kJZ3P1lPm1L6rdPREM55XQ-VfJ_pt35cP4sO2Nnvmjs,4332
|
|
17
17
|
pycontrails/core/polygon.py,sha256=gosyZBX1XBKD2EcHycIZb7uM-xGs8rCfdpiSZlhc2Hc,18028
|
|
18
|
-
pycontrails/core/cache.py,sha256=
|
|
18
|
+
pycontrails/core/cache.py,sha256=ly2Prq5CUxxc2pClZUXDeH-E8zkj3zZkLoKpdKUCyGs,27984
|
|
19
19
|
pycontrails/core/__init__.py,sha256=x1z6x8w3sYmEqYcNWyWHuNkS9lPUPbHUoYJZs1K0q98,856
|
|
20
|
-
pycontrails/core/flightplan.py,sha256=
|
|
21
|
-
pycontrails/core/met.py,sha256=
|
|
20
|
+
pycontrails/core/flightplan.py,sha256=UO4vL087d5TZMlU984-FxfotGTxFbqK78w2fLDRiel4,7335
|
|
21
|
+
pycontrails/core/met.py,sha256=0lGZqGu-_EnulU9Df05xo0I-IYX2MRQXvJ7PgCjU6p0,101342
|
|
22
22
|
pycontrails/core/aircraft_performance.py,sha256=4KnLj0zK-mk8Oo3As1CXUkQWBQGMeDdrKi5TeOhOmUA,26107
|
|
23
23
|
pycontrails/core/airports.py,sha256=aeyAXVkioIRomrP79UtNrxindL4f1DJyXFaojZCuBBw,6758
|
|
24
|
-
pycontrails/core/met_var.py,sha256=
|
|
24
|
+
pycontrails/core/met_var.py,sha256=GC5ijw4oGuIefmFOSz4vmxMEBj_SVs5Z75IMhDP56Cw,9183
|
|
25
25
|
pycontrails/core/coordinates.py,sha256=0ySsHtqTon7GMbuwmmxMbI92j3ueMteJZh4xxNm5zto,5391
|
|
26
26
|
pycontrails/datalib/goes.py,sha256=Muh_pqAXSqUlM4ssStUT9QmPxGPEKK21LHFroaqTq7k,26533
|
|
27
|
-
pycontrails/datalib/landsat.py,sha256=
|
|
27
|
+
pycontrails/datalib/landsat.py,sha256=WBOXcVgkoWmEM1jeUnOX1IKBOzzUbHWFU3lL3pJ8rfE,19682
|
|
28
28
|
pycontrails/datalib/spire.py,sha256=66SnMdA8KOS69USjKmqrJmTKPK08Ehih9tnlsCt-AJw,25331
|
|
29
29
|
pycontrails/datalib/__init__.py,sha256=hW9NWdFPC3y_2vHMteQ7GgQdop3917MkDaf5ZhU2RBY,369
|
|
30
30
|
pycontrails/datalib/sentinel.py,sha256=Rzsp5Hv6Rh3XVEfvFeofmClId4Eq2KhdYiEhIqFPE3U,17222
|
|
31
|
-
pycontrails/datalib/_met_utils/metsource.py,sha256=
|
|
31
|
+
pycontrails/datalib/_met_utils/metsource.py,sha256=1MLLnMDHlzXgRD-oeuZObgOoY8sGde81hluTm8oILPg,23965
|
|
32
32
|
pycontrails/datalib/ecmwf/arco_era5.py,sha256=YuoPmPlP9TpZ6qhUPLbb30y3D3dTNDasTLZqP5MAWtw,18624
|
|
33
|
-
pycontrails/datalib/ecmwf/era5.py,sha256=
|
|
34
|
-
pycontrails/datalib/ecmwf/era5_model_level.py,sha256=
|
|
33
|
+
pycontrails/datalib/ecmwf/era5.py,sha256=MvL8TgC8Wg-Q3pZSGVsvGRNL-HT_ndQT7CRdv2bGK7k,19016
|
|
34
|
+
pycontrails/datalib/ecmwf/era5_model_level.py,sha256=8ewGjs0LU_sySPMCk0B1xcAeMGydDx31AOXao9vrbNE,19519
|
|
35
35
|
pycontrails/datalib/ecmwf/hres.py,sha256=p_l0ytCEEWGam7G7aVynpLmH4H4LQNeVe0Ay7Tw6fp8,28240
|
|
36
|
-
pycontrails/datalib/ecmwf/variables.py,sha256=
|
|
37
|
-
pycontrails/datalib/ecmwf/hres_model_level.py,sha256
|
|
36
|
+
pycontrails/datalib/ecmwf/variables.py,sha256=jsyHxQ8YTjLA_28DrKvplFn7pOC4T6SrO4j9d2wpkic,9563
|
|
37
|
+
pycontrails/datalib/ecmwf/hres_model_level.py,sha256=-xh6BIEvsyddqJjuV7tygV85H5J8k4gCUKjj2kdkJkY,19147
|
|
38
38
|
pycontrails/datalib/ecmwf/__init__.py,sha256=kwgk9P4RYdLgOYcBLXX5rWz1T_yL7aO8nt2Eb1PB-eE,1455
|
|
39
|
-
pycontrails/datalib/ecmwf/common.py,sha256=
|
|
39
|
+
pycontrails/datalib/ecmwf/common.py,sha256=PIkEdYEmlmwxQ7v4TenW_BaHX7mslnmdJW3iZYXb7Kg,3904
|
|
40
40
|
pycontrails/datalib/ecmwf/model_levels.py,sha256=x83WJtjC6OnHcUsiNgvYIrVX4lY-pkXR-YlvUo9vYis,2712
|
|
41
41
|
pycontrails/datalib/ecmwf/ifs.py,sha256=2heema398PoEVCfiTZSBawN25PXAa_CpWm_pGLZ1GuY,10662
|
|
42
42
|
pycontrails/datalib/ecmwf/static/model_level_dataframe_v20240418.csv,sha256=PmvGLRzn6uuCKSwiasSuVcehvvmSaqP7cnLuN6hhCQQ,9788
|
|
43
|
-
pycontrails/datalib/_leo_utils/vis.py,sha256
|
|
43
|
+
pycontrails/datalib/_leo_utils/vis.py,sha256=-fLcm1D5cP6lThVHovV3MJSiadWyTUAvYDMvr4drMU4,1802
|
|
44
44
|
pycontrails/datalib/_leo_utils/search.py,sha256=r87T2OV4qH1pYI2YznvsBL042f4RKxD3OA2snd3-kDI,8687
|
|
45
45
|
pycontrails/datalib/_leo_utils/static/bq_roi_query.sql,sha256=xq6-tJyz0-bUwW0KjQymqygjH3WlQBmyBtP7Ci7SBe8,260
|
|
46
|
-
pycontrails/datalib/gfs/gfs.py,sha256=
|
|
46
|
+
pycontrails/datalib/gfs/gfs.py,sha256=fmYFf7KOGr91sFFCfThG61A3vauNis8TGIAZItIOsrY,21649
|
|
47
47
|
pycontrails/datalib/gfs/variables.py,sha256=KESzB1sTD3hsU8T-qZFD29oFM3l2ZcEtjAE_H7BHKIE,2861
|
|
48
48
|
pycontrails/datalib/gfs/__init__.py,sha256=tWxgqmlW8Uo07J-3fBTXPrteatzTka9mSXomhWy3NVA,684
|
|
49
|
-
pycontrails/ext/synthetic_flight.py,sha256=
|
|
49
|
+
pycontrails/ext/synthetic_flight.py,sha256=ypv_vUwpG-DyzD-uswk9MrJdj-k4NgsoJGJPvRkTFuo,16692
|
|
50
50
|
pycontrails/ext/cirium.py,sha256=DFPfRwLDwddpucAPRQhyT4bDGh0VvvoViMUd3pidam8,415
|
|
51
51
|
pycontrails/ext/empirical_grid.py,sha256=WSC266aKsQLzCmtrZJCpLdDBykZ9rlFE9xEXmZjbgHo,4362
|
|
52
52
|
pycontrails/ext/bada.py,sha256=j4Tj7oWSV_6UxYYa9_OjC1yTVzJMQdNRDI4aUQam_xM,1063
|
|
53
53
|
pycontrails/utils/iteration.py,sha256=q_vb39VjxRr4hqTyPYko3gK4sboJOJf_Evq6m_2DL-g,319
|
|
54
54
|
pycontrails/utils/__init__.py,sha256=Gt_57sBgfliFSxx9sDpuchykFDxmM11Wg9xAeSqPcnI,32
|
|
55
|
-
pycontrails/utils/types.py,sha256=
|
|
55
|
+
pycontrails/utils/types.py,sha256=QeJQwpdyjd3OGU9bz86mIuZvvqgV3WoF0QpAZenD6u8,4769
|
|
56
56
|
pycontrails/utils/temp.py,sha256=lGU0b_R8ze4yKlsOusHIIBaoNFBrmrB3vBjgHRlfcXk,1109
|
|
57
|
-
pycontrails/utils/json.py,sha256=
|
|
57
|
+
pycontrails/utils/json.py,sha256=b3JQ6cZ4Uy4-5e-uVAXr8t-KXC_bj_a1qHmXM0Nof58,5914
|
|
58
58
|
pycontrails/utils/dependencies.py,sha256=ATP45xYdUbIyGFzgbOe5SbokMytvB84TcexUEFnEUZE,2559
|
|
59
59
|
pycontrails/models/pcc.py,sha256=7hIlg_4-F6Ce7KVFyuIZBZY6uDr1h4KRMqBDlpGkzHE,11116
|
|
60
60
|
pycontrails/models/tau_cirrus.py,sha256=yNYw4ukT68w2ATGFZr3p8AZxB6A2xufXQq7XP2U51y0,5026
|
|
61
61
|
pycontrails/models/__init__.py,sha256=dQTOLQb7RdUdUwslt5se__5y_ymbInBexQmNrmAeOdE,33
|
|
62
62
|
pycontrails/models/issr.py,sha256=k8yCKCtKLW0ECC0QIs-ID1zbr6vHJfeBm9Shq3oaA3U,7351
|
|
63
|
-
pycontrails/models/sac.py,sha256=
|
|
64
|
-
pycontrails/models/accf.py,sha256=
|
|
63
|
+
pycontrails/models/sac.py,sha256=lV1Or0AaLxuS1Zo5V8h5c1fkSKC-hKEgiFm7bmmusWw,15946
|
|
64
|
+
pycontrails/models/accf.py,sha256=uPu8EB30Zbd3kd-3BuTnJkFNzpFqQ4u0HftFOJOX51s,12568
|
|
65
65
|
pycontrails/models/dry_advection.py,sha256=jToOXFwv0V-ZbgpJBvUgTq-AiKP4El_uq1SWS6Oi6YY,16556
|
|
66
66
|
pycontrails/models/pcr.py,sha256=ZzbEuTOuDdUmmL5T3Wk3HL-O8XzX3HMnn98WcPbASaU,5348
|
|
67
67
|
pycontrails/models/emissions/__init__.py,sha256=N_EE768TNRDbdmXaxly2Pwun7UmVBTVPc4k89VBz5ys,478
|
|
@@ -74,16 +74,16 @@ pycontrails/models/emissions/static/default-engine-uids.csv,sha256=3blb0aqtM8YRs
|
|
|
74
74
|
pycontrails/models/apcemm/__init__.py,sha256=M-hrJklbSgBckclm526MiBAhpKPLHgJbB58ArbJuGIk,175
|
|
75
75
|
pycontrails/models/apcemm/inputs.py,sha256=88GylkiaymEW_XZeFxLsICI9wV6kl8wVYsuyTe8zIQ8,6585
|
|
76
76
|
pycontrails/models/apcemm/utils.py,sha256=xlEVe0RKFXrqDr4V77mbb2HxY8IK42EX4K86tN1sLQs,17094
|
|
77
|
-
pycontrails/models/apcemm/apcemm.py,sha256=
|
|
77
|
+
pycontrails/models/apcemm/apcemm.py,sha256=PGuTSfeeA5kOzdTIeHV0svTht7walVoQ8foqf5iR8Ls,39937
|
|
78
78
|
pycontrails/models/apcemm/static/apcemm_yaml_template.yaml,sha256=uAZkc57OUvDMjgX6F5f6hgDh3Hgg1NbHWRUFSiv0DEI,6745
|
|
79
79
|
pycontrails/models/humidity_scaling/humidity_scaling.py,sha256=9619kSgNIBjkM8vFWcwny7t4mhg00pX0aKabrDGPn7I,36658
|
|
80
80
|
pycontrails/models/humidity_scaling/__init__.py,sha256=nqsab_j9BCwMbTfCn4BjXMdhItlvNKkgUJ9-lb8RyIo,1119
|
|
81
81
|
pycontrails/models/humidity_scaling/quantiles/era5-pressure-level-quantiles.pq,sha256=tfYhbafF9Z-gGCg6VQ1YBlOaK_01e65Dc6s9b-hQ6Zo,286375
|
|
82
82
|
pycontrails/models/humidity_scaling/quantiles/era5-model-level-quantiles.pq,sha256=pShCvNUo0NYtAHhT9IBRuj38X9jejdlKfv-ZoOKmtKI,35943
|
|
83
|
-
pycontrails/models/cocip/radiative_forcing.py,sha256=
|
|
83
|
+
pycontrails/models/cocip/radiative_forcing.py,sha256=aA4ZHaVOsg0lro04LwwKaBf3mXljRAzbwQpDLaxk4qU,44873
|
|
84
84
|
pycontrails/models/cocip/wind_shear.py,sha256=p8d3iaNzxPA3MoxFEM1ZDKt0aticoD6U9cv0QmbuBzs,3860
|
|
85
|
-
pycontrails/models/cocip/cocip.py,sha256=
|
|
86
|
-
pycontrails/models/cocip/output_formats.py,sha256=
|
|
85
|
+
pycontrails/models/cocip/cocip.py,sha256=zUvWQi77N13mx6nlIEbpvmEMPkdJoU2lsfK3iWovGXM,100089
|
|
86
|
+
pycontrails/models/cocip/output_formats.py,sha256=7P0j-UX4NNw56Gkd3ZsWDt0ctorJTZ4aPmUiibAh1FM,83641
|
|
87
87
|
pycontrails/models/cocip/__init__.py,sha256=jd-9Tq20s1kwQBlxsYfZLi3hlT5MnWOY2XsPazq1fgE,962
|
|
88
88
|
pycontrails/models/cocip/cocip_params.py,sha256=kKTeF1vVQr361XBR79q4mQHYI7UUQ6C5Ik5Z5pJDtag,12703
|
|
89
89
|
pycontrails/models/cocip/wake_vortex.py,sha256=i_OF193KK5BCMdVCgK0_4Aqn55f6rnL4WDWEac8um-w,14421
|
|
@@ -92,7 +92,7 @@ pycontrails/models/cocip/radiative_heating.py,sha256=YRpwfXgFnf89iuJiIM96q-jbdcM
|
|
|
92
92
|
pycontrails/models/cocip/contrail_properties.py,sha256=tycCxKf8j9GvVYDQBPxjtp6xLll-r00C0XW-w1jGbMI,55594
|
|
93
93
|
pycontrails/models/cocip/unterstrasser_wake_vortex.py,sha256=kDxFpAIkcqqhGmwXoxv3_cSESj1Ur45GbLJF56IACJs,14573
|
|
94
94
|
pycontrails/models/ps_model/__init__.py,sha256=5L-HympF1gJaZ6xiNkIQJygJhkDxM3-ejS_T2z-83hQ,495
|
|
95
|
-
pycontrails/models/ps_model/ps_model.py,sha256=
|
|
95
|
+
pycontrails/models/ps_model/ps_model.py,sha256=Cj79eJxHfq6205zNPxrwSs_Cx337MU3qvc7ds8sHAjA,33013
|
|
96
96
|
pycontrails/models/ps_model/ps_aircraft_params.py,sha256=-PfT2JC6RckVi_zTDVTqAMyaS-id6I2klUoXoEXreAc,13077
|
|
97
97
|
pycontrails/models/ps_model/ps_operational_limits.py,sha256=_vFJiPqGuZJRzwuY10-z07-7eEyomnpxPm_Js1Cd5So,16832
|
|
98
98
|
pycontrails/models/ps_model/ps_grid.py,sha256=AqZCEytWhrNbyujlJTufI4cxDonkPchGnrB3IvtRID4,18667
|
|
@@ -100,10 +100,10 @@ pycontrails/models/ps_model/static/ps-synonym-list-20240524.csv,sha256=ksrpQTHkx
|
|
|
100
100
|
pycontrails/models/ps_model/static/ps-aircraft-params-20240524.csv,sha256=3eNhSwzut0gon04k2EYKKaXRvQSUlau3yBAbHS0EBao,25784
|
|
101
101
|
pycontrails/models/cocipgrid/cocip_grid_params.py,sha256=l4vBPrOKCJDz5Y1uMjmOGVyUcSWgfZtFWbjW968OPz8,5875
|
|
102
102
|
pycontrails/models/cocipgrid/__init__.py,sha256=ar6bF_8Pusbb-myujz_q5ntFylQTNH8yiM8fxP7Zk30,262
|
|
103
|
-
pycontrails/models/cocipgrid/cocip_grid.py,sha256=
|
|
103
|
+
pycontrails/models/cocipgrid/cocip_grid.py,sha256=B1-f3D62fg3OeLp7xnxANAsneg9JRiiRVMqs-REFaII,94347
|
|
104
104
|
pycontrails/physics/geo.py,sha256=9ZWIXyEEgrBNqsoeBBlYLTA-8GUTgyc-jgeVgchxXa8,30288
|
|
105
105
|
pycontrails/physics/units.py,sha256=j-G5AC9eWIvv2MTOq9lUOoOQKFNJJuHzWLanHRji2tE,12272
|
|
106
106
|
pycontrails/physics/constants.py,sha256=pHQQmccMUwuNnY4hFtm3L8G2rnUQcfJnroyQr8HAVeM,3146
|
|
107
107
|
pycontrails/physics/__init__.py,sha256=_1eWbEy6evEWdfJCEkwDiSdpiDNzNWEPVqaPekHyhwU,44
|
|
108
|
-
pycontrails/physics/thermo.py,sha256=
|
|
108
|
+
pycontrails/physics/thermo.py,sha256=sWGpKa12daSpqZYNgyXd8Ii5nfA_1Mm5mMbnM5GsW-E,12787
|
|
109
109
|
pycontrails/physics/jet.py,sha256=JX-o5dDjyITMNUIOcD_7UBt_4ldc9v-5gI6bmwf0wQ4,25624
|
|
File without changes
|
|
File without changes
|