pycontrails 0.53.0__cp313-cp313-macosx_10_13_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/__init__.py +70 -0
- pycontrails/_version.py +16 -0
- pycontrails/core/__init__.py +30 -0
- pycontrails/core/aircraft_performance.py +641 -0
- pycontrails/core/airports.py +226 -0
- pycontrails/core/cache.py +881 -0
- pycontrails/core/coordinates.py +174 -0
- pycontrails/core/fleet.py +470 -0
- pycontrails/core/flight.py +2312 -0
- pycontrails/core/flightplan.py +220 -0
- pycontrails/core/fuel.py +140 -0
- pycontrails/core/interpolation.py +721 -0
- pycontrails/core/met.py +2833 -0
- pycontrails/core/met_var.py +307 -0
- pycontrails/core/models.py +1181 -0
- pycontrails/core/polygon.py +549 -0
- pycontrails/core/rgi_cython.cpython-313-darwin.so +0 -0
- pycontrails/core/vector.py +2191 -0
- pycontrails/datalib/__init__.py +12 -0
- pycontrails/datalib/_leo_utils/search.py +250 -0
- pycontrails/datalib/_leo_utils/static/bq_roi_query.sql +6 -0
- pycontrails/datalib/_leo_utils/vis.py +59 -0
- pycontrails/datalib/_met_utils/metsource.py +743 -0
- pycontrails/datalib/ecmwf/__init__.py +53 -0
- pycontrails/datalib/ecmwf/arco_era5.py +527 -0
- pycontrails/datalib/ecmwf/common.py +109 -0
- pycontrails/datalib/ecmwf/era5.py +538 -0
- pycontrails/datalib/ecmwf/era5_model_level.py +482 -0
- pycontrails/datalib/ecmwf/hres.py +782 -0
- pycontrails/datalib/ecmwf/hres_model_level.py +495 -0
- pycontrails/datalib/ecmwf/ifs.py +284 -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 +256 -0
- pycontrails/datalib/gfs/__init__.py +28 -0
- pycontrails/datalib/gfs/gfs.py +646 -0
- pycontrails/datalib/gfs/variables.py +100 -0
- pycontrails/datalib/goes.py +772 -0
- pycontrails/datalib/landsat.py +568 -0
- pycontrails/datalib/sentinel.py +512 -0
- pycontrails/datalib/spire.py +739 -0
- pycontrails/ext/bada.py +41 -0
- pycontrails/ext/cirium.py +14 -0
- pycontrails/ext/empirical_grid.py +140 -0
- pycontrails/ext/synthetic_flight.py +426 -0
- pycontrails/models/__init__.py +1 -0
- pycontrails/models/accf.py +406 -0
- pycontrails/models/apcemm/__init__.py +8 -0
- pycontrails/models/apcemm/apcemm.py +983 -0
- pycontrails/models/apcemm/inputs.py +226 -0
- pycontrails/models/apcemm/static/apcemm_yaml_template.yaml +183 -0
- pycontrails/models/apcemm/utils.py +437 -0
- pycontrails/models/cocip/__init__.py +29 -0
- pycontrails/models/cocip/cocip.py +2617 -0
- pycontrails/models/cocip/cocip_params.py +299 -0
- pycontrails/models/cocip/cocip_uncertainty.py +285 -0
- pycontrails/models/cocip/contrail_properties.py +1517 -0
- pycontrails/models/cocip/output_formats.py +2261 -0
- pycontrails/models/cocip/radiative_forcing.py +1262 -0
- pycontrails/models/cocip/radiative_heating.py +520 -0
- pycontrails/models/cocip/unterstrasser_wake_vortex.py +403 -0
- pycontrails/models/cocip/wake_vortex.py +396 -0
- pycontrails/models/cocip/wind_shear.py +120 -0
- pycontrails/models/cocipgrid/__init__.py +9 -0
- pycontrails/models/cocipgrid/cocip_grid.py +2573 -0
- pycontrails/models/cocipgrid/cocip_grid_params.py +138 -0
- pycontrails/models/dry_advection.py +486 -0
- pycontrails/models/emissions/__init__.py +21 -0
- pycontrails/models/emissions/black_carbon.py +594 -0
- pycontrails/models/emissions/emissions.py +1353 -0
- pycontrails/models/emissions/ffm2.py +336 -0
- pycontrails/models/emissions/static/default-engine-uids.csv +239 -0
- pycontrails/models/emissions/static/edb-gaseous-v29b-engines.csv +596 -0
- pycontrails/models/emissions/static/edb-nvpm-v29b-engines.csv +215 -0
- pycontrails/models/humidity_scaling/__init__.py +37 -0
- pycontrails/models/humidity_scaling/humidity_scaling.py +1025 -0
- pycontrails/models/humidity_scaling/quantiles/era5-model-level-quantiles.pq +0 -0
- pycontrails/models/humidity_scaling/quantiles/era5-pressure-level-quantiles.pq +0 -0
- pycontrails/models/issr.py +210 -0
- pycontrails/models/pcc.py +327 -0
- pycontrails/models/pcr.py +154 -0
- pycontrails/models/ps_model/__init__.py +17 -0
- pycontrails/models/ps_model/ps_aircraft_params.py +376 -0
- pycontrails/models/ps_model/ps_grid.py +505 -0
- pycontrails/models/ps_model/ps_model.py +1017 -0
- pycontrails/models/ps_model/ps_operational_limits.py +540 -0
- pycontrails/models/ps_model/static/ps-aircraft-params-20240524.csv +68 -0
- pycontrails/models/ps_model/static/ps-synonym-list-20240524.csv +103 -0
- pycontrails/models/sac.py +459 -0
- pycontrails/models/tau_cirrus.py +168 -0
- pycontrails/physics/__init__.py +1 -0
- pycontrails/physics/constants.py +116 -0
- pycontrails/physics/geo.py +989 -0
- pycontrails/physics/jet.py +837 -0
- pycontrails/physics/thermo.py +451 -0
- pycontrails/physics/units.py +472 -0
- pycontrails/py.typed +0 -0
- pycontrails/utils/__init__.py +1 -0
- pycontrails/utils/dependencies.py +66 -0
- pycontrails/utils/iteration.py +13 -0
- pycontrails/utils/json.py +188 -0
- pycontrails/utils/temp.py +50 -0
- pycontrails/utils/types.py +165 -0
- pycontrails-0.53.0.dist-info/LICENSE +178 -0
- pycontrails-0.53.0.dist-info/METADATA +181 -0
- pycontrails-0.53.0.dist-info/NOTICE +43 -0
- pycontrails-0.53.0.dist-info/RECORD +109 -0
- pycontrails-0.53.0.dist-info/WHEEL +5 -0
- pycontrails-0.53.0.dist-info/top_level.txt +3 -0
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
"""ECWMF IFS forecast data access."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import logging
|
|
6
|
+
import pathlib
|
|
7
|
+
import warnings
|
|
8
|
+
from datetime import datetime
|
|
9
|
+
from typing import Any
|
|
10
|
+
|
|
11
|
+
LOG = logging.getLogger(__name__)
|
|
12
|
+
|
|
13
|
+
import numpy as np
|
|
14
|
+
import pandas as pd
|
|
15
|
+
import xarray as xr
|
|
16
|
+
from overrides import overrides
|
|
17
|
+
|
|
18
|
+
from pycontrails.core import met
|
|
19
|
+
from pycontrails.datalib._met_utils import metsource
|
|
20
|
+
from pycontrails.datalib.ecmwf.variables import ECMWF_VARIABLES
|
|
21
|
+
from pycontrails.physics import constants
|
|
22
|
+
from pycontrails.utils.types import DatetimeLike
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class IFS(metsource.MetDataSource):
|
|
26
|
+
"""
|
|
27
|
+
ECMWF Integrated Forecasting System (IFS) data source.
|
|
28
|
+
|
|
29
|
+
.. warning::
|
|
30
|
+
|
|
31
|
+
This data source is not fully implemented.
|
|
32
|
+
|
|
33
|
+
Parameters
|
|
34
|
+
----------
|
|
35
|
+
time : metsource.TimeInput | None
|
|
36
|
+
The time range for data retrieval, either a single datetime or (start, end) datetime range.
|
|
37
|
+
Input must be a single datetime-like or tuple of datetime-like
|
|
38
|
+
(datetime, :class:`pandas.Timestamp`, :class:`numpy.datetime64`)
|
|
39
|
+
specifying the (start, end) of the date range, inclusive.
|
|
40
|
+
If None, all time coordinates will be loaded.
|
|
41
|
+
variables : metsource.VariableInput
|
|
42
|
+
Variable name (i.e. "air_temperature", ["air_temperature, relative_humidity"])
|
|
43
|
+
See :attr:`pressure_level_variables` for the list of available variables.
|
|
44
|
+
pressure_levels : metsource.PressureLevelInput, optional
|
|
45
|
+
Pressure level bounds for data (min, max), in hPa (mbar)
|
|
46
|
+
Set to -1 for to download surface level parameters.
|
|
47
|
+
Defaults to -1.
|
|
48
|
+
paths : str | list[str] | pathlib.Path | list[pathlib.Path] | None, optional
|
|
49
|
+
UNSUPPORTED FOR IFS
|
|
50
|
+
forecast_path: str | pathlib.Path | None, optional
|
|
51
|
+
Path to local forecast files.
|
|
52
|
+
Defaults to None
|
|
53
|
+
forecast_date: DatetimeLike, optional
|
|
54
|
+
Forecast date to load specific netcdf files.
|
|
55
|
+
Defaults to None
|
|
56
|
+
|
|
57
|
+
Notes
|
|
58
|
+
-----
|
|
59
|
+
This takes an average pressure of the model level to create
|
|
60
|
+
pressure level dimensions.
|
|
61
|
+
"""
|
|
62
|
+
|
|
63
|
+
__slots__ = ("forecast_date", "forecast_path")
|
|
64
|
+
|
|
65
|
+
#: Root path of IFS data
|
|
66
|
+
forecast_path: pathlib.Path
|
|
67
|
+
|
|
68
|
+
#: Forecast datetime of IFS forecast
|
|
69
|
+
forecast_date: pd.Timestamp
|
|
70
|
+
|
|
71
|
+
def __init__(
|
|
72
|
+
self,
|
|
73
|
+
time: metsource.TimeInput | None,
|
|
74
|
+
variables: metsource.VariableInput,
|
|
75
|
+
pressure_levels: metsource.PressureLevelInput = -1,
|
|
76
|
+
paths: str | list[str] | pathlib.Path | list[pathlib.Path] | None = None,
|
|
77
|
+
grid: float | None = None,
|
|
78
|
+
forecast_path: str | pathlib.Path | None = None,
|
|
79
|
+
forecast_date: DatetimeLike | None = None,
|
|
80
|
+
) -> None:
|
|
81
|
+
self.paths = paths # TODO: this is currently unused
|
|
82
|
+
self.grid = grid # TODO: this is currently unused
|
|
83
|
+
|
|
84
|
+
# path to forecast files
|
|
85
|
+
if forecast_path is None:
|
|
86
|
+
raise ValueError("Forecast path input is required for IFS")
|
|
87
|
+
self.forecast_path = pathlib.Path(forecast_path)
|
|
88
|
+
|
|
89
|
+
# TODO: automatically select a forecast_date from input time range?
|
|
90
|
+
self.forecast_date = pd.to_datetime(forecast_date).to_pydatetime()
|
|
91
|
+
|
|
92
|
+
# parse inputs
|
|
93
|
+
self.timesteps = metsource.parse_timesteps(time, freq="3h")
|
|
94
|
+
self.pressure_levels = metsource.parse_pressure_levels(pressure_levels, None)
|
|
95
|
+
self.variables = metsource.parse_variables(variables, self.supported_variables)
|
|
96
|
+
|
|
97
|
+
def __repr__(self) -> str:
|
|
98
|
+
base = super().__repr__()
|
|
99
|
+
return f"{base}\n\tForecast date: {self.forecast_date}"
|
|
100
|
+
|
|
101
|
+
@property
|
|
102
|
+
def supported_variables(self) -> list[met.MetVariable]:
|
|
103
|
+
"""IFS parameters available.
|
|
104
|
+
|
|
105
|
+
Returns
|
|
106
|
+
-------
|
|
107
|
+
list[MetVariable] | None
|
|
108
|
+
List of MetVariable available in datasource
|
|
109
|
+
"""
|
|
110
|
+
return ECMWF_VARIABLES
|
|
111
|
+
|
|
112
|
+
@property
|
|
113
|
+
def supported_pressure_levels(self) -> None:
|
|
114
|
+
"""IFS does not provide constant pressure levels and instead uses model levels.
|
|
115
|
+
|
|
116
|
+
Returns
|
|
117
|
+
-------
|
|
118
|
+
list[int]
|
|
119
|
+
"""
|
|
120
|
+
return None
|
|
121
|
+
|
|
122
|
+
@overrides
|
|
123
|
+
def open_metdataset(
|
|
124
|
+
self,
|
|
125
|
+
dataset: xr.Dataset | None = None,
|
|
126
|
+
xr_kwargs: dict[str, Any] | None = None,
|
|
127
|
+
**kwargs: Any,
|
|
128
|
+
) -> met.MetDataset:
|
|
129
|
+
xr_kwargs = xr_kwargs or {}
|
|
130
|
+
|
|
131
|
+
# short-circuit dataset or file paths if provided
|
|
132
|
+
if self.paths is not None or dataset is not None:
|
|
133
|
+
raise NotImplementedError("IFS input paths or input dataset is not supported")
|
|
134
|
+
|
|
135
|
+
# load / merge datasets
|
|
136
|
+
ds = self._open_ifs_dataset(**xr_kwargs)
|
|
137
|
+
|
|
138
|
+
# drop ancillary vars
|
|
139
|
+
ds = ds.drop_vars(names=["hyai", "hybi"])
|
|
140
|
+
|
|
141
|
+
# downselect dataset if only a subset of times, pressure levels, or variables are requested
|
|
142
|
+
if self.timesteps:
|
|
143
|
+
ds = ds.sel(time=self.timesteps)
|
|
144
|
+
else:
|
|
145
|
+
# set timesteps from dataset "time" coordinates
|
|
146
|
+
# np.datetime64 doesn't covert to list[datetime] unless its unit is us
|
|
147
|
+
self.timesteps = ds["time"].values.astype("datetime64[us]").tolist()
|
|
148
|
+
|
|
149
|
+
# downselect hyam/hybm coefficients by the "lev" coordinate
|
|
150
|
+
# (this is a 1-indexed verison of nhym)
|
|
151
|
+
ds["hyam"] = ds["hyam"][dict(nhym=(ds["lev"] - 1).astype(int))]
|
|
152
|
+
ds["hybm"] = ds["hybm"][dict(nhym=(ds["lev"] - 1).astype(int))]
|
|
153
|
+
|
|
154
|
+
# calculate air_pressure (Pa) by hybrid sigma pressure
|
|
155
|
+
ds["air_pressure"] = ds["hyam"] + (ds["hybm"] * ds["surface_pressure"])
|
|
156
|
+
ds["air_pressure"].attrs["units"] = "Pa"
|
|
157
|
+
ds["air_pressure"].attrs["long_name"] = "Air pressure"
|
|
158
|
+
|
|
159
|
+
# calculate virtual temperature (t_virtual)
|
|
160
|
+
# the temperature at which dry air would have the same density
|
|
161
|
+
# as the moist air at a given pressure
|
|
162
|
+
ds["t_virtual"] = ds["t"] * (1 + ds["q"] * ((constants.R_v / constants.R_d) - 1))
|
|
163
|
+
ds["t_virtual"].attrs["units"] = "K"
|
|
164
|
+
ds["t_virtual"].attrs["long_name"] = "Virtual Temperature"
|
|
165
|
+
|
|
166
|
+
# calculate geopotential
|
|
167
|
+
if "z" in self.variable_shortnames:
|
|
168
|
+
ds["z"] = self._calc_geopotential(ds)
|
|
169
|
+
|
|
170
|
+
# take the mean of the air pressure to create quasi-gridded level coordinate
|
|
171
|
+
ds = ds.assign_coords(
|
|
172
|
+
{"level": ("lev", (ds["air_pressure"].mean(dim=["time", "lat", "lon"]) / 100).values)}
|
|
173
|
+
)
|
|
174
|
+
ds = ds.swap_dims({"lev": "level"})
|
|
175
|
+
ds = ds.drop_vars(names=["lev"])
|
|
176
|
+
|
|
177
|
+
# rename dimensions
|
|
178
|
+
ds = ds.rename({"lat": "latitude", "lon": "longitude"})
|
|
179
|
+
|
|
180
|
+
# downselect variables
|
|
181
|
+
ds = ds[self.variable_shortnames]
|
|
182
|
+
|
|
183
|
+
# TODO: fix this correctly
|
|
184
|
+
if "level" not in ds.dims:
|
|
185
|
+
ds = ds.expand_dims({"level": [-1]})
|
|
186
|
+
|
|
187
|
+
# harmonize variable names
|
|
188
|
+
ds = met.standardize_variables(ds, self.variables)
|
|
189
|
+
|
|
190
|
+
self.set_metadata(ds)
|
|
191
|
+
return met.MetDataset(ds, **kwargs)
|
|
192
|
+
|
|
193
|
+
@overrides
|
|
194
|
+
def set_metadata(self, ds: xr.Dataset | met.MetDataset) -> None:
|
|
195
|
+
ds.attrs.update(
|
|
196
|
+
provider="ECMWF",
|
|
197
|
+
dataset="IFS",
|
|
198
|
+
product="forecast",
|
|
199
|
+
)
|
|
200
|
+
|
|
201
|
+
@overrides
|
|
202
|
+
def download_dataset(self, times: list[datetime]) -> None:
|
|
203
|
+
raise NotImplementedError("IFS download is not supported")
|
|
204
|
+
|
|
205
|
+
@overrides
|
|
206
|
+
def cache_dataset(self, dataset: xr.Dataset) -> None:
|
|
207
|
+
raise NotImplementedError("IFS dataset caching not supported")
|
|
208
|
+
|
|
209
|
+
@overrides
|
|
210
|
+
def create_cachepath(self, t: datetime) -> str:
|
|
211
|
+
raise NotImplementedError("IFS download is not supported")
|
|
212
|
+
|
|
213
|
+
def _open_ifs_dataset(self, **xr_kwargs: Any) -> xr.Dataset:
|
|
214
|
+
# get the path to each IFS file for each forecast date
|
|
215
|
+
date_str = self.forecast_date.strftime("%Y%m%d")
|
|
216
|
+
path_full = f"{self.forecast_path}/FC_{date_str}_00_144.nc"
|
|
217
|
+
path_fl = f"{self.forecast_path}/FC_{date_str}_00_144_fl.nc"
|
|
218
|
+
path_surface = f"{self.forecast_path}/FC_{date_str}_00_144_sur.nc"
|
|
219
|
+
path_rad = f"{self.forecast_path}/FC_{date_str}_00_144_rad.nc"
|
|
220
|
+
|
|
221
|
+
# load each dataset
|
|
222
|
+
LOG.debug(f"Loading IFS forecast date {date_str}")
|
|
223
|
+
|
|
224
|
+
# load each dataset
|
|
225
|
+
xr_kwargs.setdefault("chunks", metsource.DEFAULT_CHUNKS)
|
|
226
|
+
xr_kwargs.setdefault("engine", metsource.NETCDF_ENGINE)
|
|
227
|
+
ds_full = xr.open_dataset(path_full, **xr_kwargs)
|
|
228
|
+
ds_fl = xr.open_dataset(path_fl, **xr_kwargs)
|
|
229
|
+
ds_surface = xr.open_dataset(path_surface, **xr_kwargs)
|
|
230
|
+
ds_rad = xr.open_dataset(path_rad, **xr_kwargs)
|
|
231
|
+
|
|
232
|
+
# calculate surface pressure from ln(surface pressure) var, squeeze out "lev" dim
|
|
233
|
+
ds_full["surface_pressure"] = np.exp(ds_full["lnsp"]).squeeze()
|
|
234
|
+
ds_full["surface_pressure"].attrs["units"] = "Pa"
|
|
235
|
+
ds_full["surface_pressure"].attrs["long_name"] = "Surface air pressure"
|
|
236
|
+
|
|
237
|
+
# swap dim names for consistency
|
|
238
|
+
ds_full = ds_full.drop_vars(names=["lnsp", "lev"])
|
|
239
|
+
ds_full = ds_full.rename({"lev_2": "lev"})
|
|
240
|
+
|
|
241
|
+
# drop vars so all datasets can merge
|
|
242
|
+
ds_fl = ds_fl.drop_vars(names=["hyai", "hybi", "hyam", "hybm"])
|
|
243
|
+
|
|
244
|
+
# merge all datasets using the "ds_fl" dimensions as the join keys
|
|
245
|
+
ds = xr.merge([ds_fl, ds_full, ds_surface, ds_rad], join="left") # order matters!
|
|
246
|
+
|
|
247
|
+
return ds
|
|
248
|
+
|
|
249
|
+
def _calc_geopotential(self, ds: xr.Dataset) -> xr.DataArray:
|
|
250
|
+
warnings.warn(
|
|
251
|
+
"The geopotential calculation implementation may assume the underlying grid "
|
|
252
|
+
"starts at ground level. This may not be the case for IFS data. It may be "
|
|
253
|
+
"better to use geometric height (altitude) instead of geopotential for downstream "
|
|
254
|
+
"applications (tau cirrus, etc.).",
|
|
255
|
+
UserWarning,
|
|
256
|
+
)
|
|
257
|
+
|
|
258
|
+
# TODO: this could be done via a mapping on the "lev" dimension
|
|
259
|
+
# groupby("lev")
|
|
260
|
+
|
|
261
|
+
z_level = ds["z"].copy()
|
|
262
|
+
p_level = ds["surface_pressure"].copy()
|
|
263
|
+
geopotential = xr.zeros_like(ds["t"])
|
|
264
|
+
|
|
265
|
+
geopotential.attrs["standard_name"] = "geopotential"
|
|
266
|
+
geopotential.attrs["units"] = "m**2 s**-2"
|
|
267
|
+
geopotential.attrs["long_name"] = "Geopotential"
|
|
268
|
+
|
|
269
|
+
# iterate through level layers from the bottom up
|
|
270
|
+
for k in ds["lev"][::-1]:
|
|
271
|
+
d_log_p = np.log(p_level / ds["air_pressure"].loc[dict(lev=k)])
|
|
272
|
+
|
|
273
|
+
denom = p_level - ds["air_pressure"].loc[dict(lev=k)]
|
|
274
|
+
alpha = 1 - d_log_p * ds["air_pressure"].loc[dict(lev=k)] / denom
|
|
275
|
+
|
|
276
|
+
geopotential.loc[dict(lev=k)] = (
|
|
277
|
+
z_level + ds["t_virtual"].loc[dict(lev=k)] * alpha * constants.R_d
|
|
278
|
+
)
|
|
279
|
+
|
|
280
|
+
# Update values for next loop
|
|
281
|
+
z_level = geopotential.loc[dict(lev=k)].copy()
|
|
282
|
+
p_level = ds["air_pressure"].loc[dict(lev=k)].copy()
|
|
283
|
+
|
|
284
|
+
return geopotential
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
"""Utilities for working with ECMWF model-level data.
|
|
2
|
+
|
|
3
|
+
This module requires the following additional dependency:
|
|
4
|
+
|
|
5
|
+
- `lxml <https://lxml.de/>`_
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import pathlib
|
|
9
|
+
|
|
10
|
+
import pandas as pd
|
|
11
|
+
|
|
12
|
+
from pycontrails.physics import units
|
|
13
|
+
from pycontrails.utils import dependencies
|
|
14
|
+
|
|
15
|
+
_path_to_static = pathlib.Path(__file__).parent / "static"
|
|
16
|
+
MODEL_LEVELS_PATH = _path_to_static / "model_level_dataframe_v20240418.csv"
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def pressure_levels_at_model_levels(alt_ft_min: float, alt_ft_max: float) -> list[int]:
|
|
20
|
+
"""Return the pressure levels at each model level assuming a constant surface pressure.
|
|
21
|
+
|
|
22
|
+
The pressure levels are rounded to the nearest hPa.
|
|
23
|
+
|
|
24
|
+
Parameters
|
|
25
|
+
----------
|
|
26
|
+
alt_ft_min : float
|
|
27
|
+
Minimum altitude, [:math:`ft`].
|
|
28
|
+
alt_ft_max : float
|
|
29
|
+
Maximum altitude, [:math:`ft`].
|
|
30
|
+
|
|
31
|
+
Returns
|
|
32
|
+
-------
|
|
33
|
+
list[int]
|
|
34
|
+
List of pressure levels, [:math:`hPa`].
|
|
35
|
+
"""
|
|
36
|
+
df = pd.read_csv(MODEL_LEVELS_PATH)
|
|
37
|
+
alt_m_min = units.ft_to_m(alt_ft_min)
|
|
38
|
+
alt_m_max = units.ft_to_m(alt_ft_max)
|
|
39
|
+
filt = df["Geometric Altitude [m]"].between(alt_m_min, alt_m_max)
|
|
40
|
+
return df.loc[filt, "pf [hPa]"].round().astype(int).tolist()
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def _cache_model_level_dataframe() -> pd.DataFrame:
|
|
44
|
+
"""Regenerate static model level data file.
|
|
45
|
+
|
|
46
|
+
Read the ERA5 model level definitions published by ECMWF
|
|
47
|
+
and cache it in a static file for use by this module.
|
|
48
|
+
This should only be used by model developers, and only if ECMWF model
|
|
49
|
+
level definitions change. ``MODEL_LEVEL_PATH`` must be manually
|
|
50
|
+
updated to use newly-cached files.
|
|
51
|
+
|
|
52
|
+
Requires the lxml package to be installed.
|
|
53
|
+
"""
|
|
54
|
+
import os
|
|
55
|
+
from datetime import datetime
|
|
56
|
+
|
|
57
|
+
url = "https://confluence.ecmwf.int/display/UDOC/L137+model+level+definitions"
|
|
58
|
+
try:
|
|
59
|
+
df = pd.read_html(url, na_values="-", index_col="n")[0]
|
|
60
|
+
today = datetime.now()
|
|
61
|
+
new_file_path = _path_to_static / f"model_level_dataframe_v{today.strftime('%Y%m%d')}.csv"
|
|
62
|
+
if os.path.exists(new_file_path):
|
|
63
|
+
msg = f"Static file already exists at {new_file_path}"
|
|
64
|
+
raise ValueError(msg)
|
|
65
|
+
df.to_csv(new_file_path)
|
|
66
|
+
|
|
67
|
+
except ImportError as exc:
|
|
68
|
+
if "lxml" in exc.msg:
|
|
69
|
+
dependencies.raise_module_not_found_error(
|
|
70
|
+
"model_level_utils._read_model_level_dataframe function",
|
|
71
|
+
package_name="lxml",
|
|
72
|
+
module_not_found_error=exc,
|
|
73
|
+
extra=(
|
|
74
|
+
"Alternatively, if instantiating a model-level ECMWF datalib, you can provide "
|
|
75
|
+
"the 'pressure_levels' parameter directly to avoid the need to read the "
|
|
76
|
+
"ECMWF model level definitions."
|
|
77
|
+
),
|
|
78
|
+
)
|
|
79
|
+
raise
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
n,a [Pa],b,ph [hPa],pf [hPa],Geopotential Altitude [m],Geometric Altitude [m],Temperature [K],Density [kg/m^3]
|
|
2
|
+
0,0.0,0.0,0.0,,,,,
|
|
3
|
+
1,2.000365,0.0,0.02,0.01,79301.79,80301.65,198.05,1.8e-05
|
|
4
|
+
2,3.102241,0.0,0.031,0.0255,73721.58,74584.91,209.21,4.2e-05
|
|
5
|
+
3,4.666084,0.0,0.0467,0.0388,71115.75,71918.79,214.42,6.3e-05
|
|
6
|
+
4,6.827977,0.0,0.0683,0.0575,68618.43,69365.77,221.32,9e-05
|
|
7
|
+
5,9.746966,0.0,0.0975,0.0829,66210.99,66906.53,228.06,0.000127
|
|
8
|
+
6,13.605424,0.0,0.1361,0.1168,63890.03,64537.43,234.56,0.000173
|
|
9
|
+
7,18.608931,0.0,0.1861,0.1611,61651.77,62254.39,240.83,0.000233
|
|
10
|
+
8,24.985718,0.0,0.2499,0.218,59492.5,60053.46,246.87,0.000308
|
|
11
|
+
9,32.98571,0.0,0.3299,0.2899,57408.61,57930.78,252.71,0.0004
|
|
12
|
+
10,42.879242,0.0,0.4288,0.3793,55396.62,55882.68,258.34,0.000512
|
|
13
|
+
11,54.955463,0.0,0.5496,0.4892,53453.2,53905.62,263.78,0.000646
|
|
14
|
+
12,69.520576,0.0,0.6952,0.6224,51575.15,51996.21,269.04,0.000806
|
|
15
|
+
13,86.895882,0.0,0.869,0.7821,49767.41,50159.36,270.65,0.001007
|
|
16
|
+
14,107.415741,0.0,1.0742,0.9716,48048.7,48413.94,270.65,0.001251
|
|
17
|
+
15,131.425507,0.0,1.3143,1.1942,46416.22,46756.98,269.02,0.001546
|
|
18
|
+
16,159.279404,0.0,1.5928,1.4535,44881.17,45199.69,264.72,0.001913
|
|
19
|
+
17,191.338562,0.0,1.9134,1.7531,43440.23,43738.55,260.68,0.002343
|
|
20
|
+
18,227.968948,0.0,2.2797,2.0965,42085.0,42364.93,256.89,0.002843
|
|
21
|
+
19,269.539581,0.0,2.6954,2.4875,40808.05,41071.2,253.31,0.003421
|
|
22
|
+
20,316.420746,0.0,3.1642,2.9298,39602.76,39850.56,249.94,0.004084
|
|
23
|
+
21,368.982361,0.0,3.6898,3.427,38463.25,38696.94,246.75,0.004838
|
|
24
|
+
22,427.592499,0.0,4.2759,3.9829,37384.22,37604.95,243.73,0.005693
|
|
25
|
+
23,492.616028,0.0,4.9262,4.601,36360.94,36569.72,240.86,0.006655
|
|
26
|
+
24,564.413452,0.0,5.6441,5.2851,35389.15,35586.89,238.14,0.007731
|
|
27
|
+
25,643.339905,0.0,6.4334,6.0388,34465.0,34652.52,235.55,0.008931
|
|
28
|
+
26,729.744141,0.0,7.2974,6.8654,33585.02,33763.05,233.09,0.010261
|
|
29
|
+
27,823.967834,0.0,8.2397,7.7686,32746.04,32915.27,230.74,0.011729
|
|
30
|
+
28,926.34491,0.0,9.2634,8.7516,31945.53,32106.57,228.6,0.013337
|
|
31
|
+
29,1037.201172,0.0,10.372,9.8177,31177.59,31330.96,227.83,0.015012
|
|
32
|
+
30,1156.853638,0.0,11.5685,10.9703,30438.54,30584.71,227.09,0.016829
|
|
33
|
+
31,1285.610352,0.0,12.8561,12.2123,29726.69,29866.09,226.38,0.018793
|
|
34
|
+
32,1423.770142,0.0,14.2377,13.5469,29040.48,29173.5,225.69,0.02091
|
|
35
|
+
33,1571.622925,0.0,15.7162,14.977,28378.46,28505.47,225.03,0.023186
|
|
36
|
+
34,1729.448975,0.0,17.2945,16.5054,27739.29,27860.64,224.39,0.025624
|
|
37
|
+
35,1897.519287,0.0,18.9752,18.1348,27121.74,27237.73,223.77,0.028232
|
|
38
|
+
36,2076.095947,0.0,20.761,19.8681,26524.63,26635.56,223.17,0.031013
|
|
39
|
+
37,2265.431641,0.0,22.6543,21.7076,25946.9,26053.04,222.6,0.033972
|
|
40
|
+
38,2465.770508,0.0,24.6577,23.656,25387.55,25489.15,222.04,0.037115
|
|
41
|
+
39,2677.348145,0.0,26.7735,25.7156,24845.63,24942.93,221.5,0.040445
|
|
42
|
+
40,2900.391357,0.0,29.0039,27.8887,24320.28,24413.5,220.97,0.043967
|
|
43
|
+
41,3135.119385,0.0,31.3512,30.1776,23810.67,23900.02,220.46,0.047685
|
|
44
|
+
42,3381.743652,0.0,33.8174,32.5843,23316.04,23401.71,219.97,0.051604
|
|
45
|
+
43,3640.468262,0.0,36.4047,35.1111,22835.68,22917.85,219.49,0.055727
|
|
46
|
+
44,3911.490479,0.0,39.1149,37.7598,22368.91,22447.75,219.02,0.060059
|
|
47
|
+
45,4194.930664,0.0,41.9493,40.5321,21915.16,21990.82,218.57,0.064602
|
|
48
|
+
46,4490.817383,0.0,44.9082,43.4287,21473.98,21546.62,218.12,0.069359
|
|
49
|
+
47,4799.149414,0.0,47.9915,46.4498,21045.0,21114.77,217.7,0.07433
|
|
50
|
+
48,5119.89502,0.0,51.199,49.5952,20627.87,20694.9,217.28,0.079516
|
|
51
|
+
49,5452.990723,0.0,54.5299,52.8644,20222.24,20286.66,216.87,0.084916
|
|
52
|
+
50,5798.344727,0.0,57.9834,56.2567,19827.95,19889.88,216.65,0.090458
|
|
53
|
+
51,6156.074219,0.0,61.5607,59.7721,19443.55,19503.09,216.65,0.09611
|
|
54
|
+
52,6526.946777,0.0,65.2695,63.4151,19068.35,19125.61,216.65,0.101968
|
|
55
|
+
53,6911.870605,0.0,69.1187,67.1941,18701.27,18756.34,216.65,0.108045
|
|
56
|
+
54,7311.869141,0.0,73.1187,71.1187,18341.27,18394.25,216.65,0.114355
|
|
57
|
+
55,7727.412109,7e-06,77.281,75.1999,17987.41,18038.35,216.65,0.120917
|
|
58
|
+
56,8159.354004,2.4e-05,81.6182,79.4496,17638.78,17687.77,216.65,0.127751
|
|
59
|
+
57,8608.525391,5.9e-05,86.145,83.8816,17294.53,17341.62,216.65,0.134877
|
|
60
|
+
58,9076.400391,0.000112,90.8774,88.5112,16953.83,16999.08,216.65,0.142321
|
|
61
|
+
59,9562.682617,0.000199,95.828,93.3527,16616.09,16659.55,216.65,0.150106
|
|
62
|
+
60,10065.978516,0.00034,101.0047,98.4164,16281.1,16322.83,216.65,0.158248
|
|
63
|
+
61,10584.631836,0.000562,106.4153,103.71,15948.85,15988.88,216.65,0.16676
|
|
64
|
+
62,11116.662109,0.00089,112.0681,109.2417,15619.3,15657.7,216.65,0.175655
|
|
65
|
+
63,11660.067383,0.001353,117.9714,115.0198,15292.44,15329.24,216.65,0.184946
|
|
66
|
+
64,12211.547852,0.001992,124.1337,121.0526,14968.24,15003.5,216.65,0.194646
|
|
67
|
+
65,12766.873047,0.002857,130.5637,127.3487,14646.68,14680.44,216.65,0.20477
|
|
68
|
+
66,13324.668945,0.003971,137.2703,133.917,14327.75,14360.05,216.65,0.215331
|
|
69
|
+
67,13881.331055,0.005378,144.2624,140.7663,14011.41,14042.3,216.65,0.226345
|
|
70
|
+
68,14432.139648,0.007133,151.5493,147.9058,13697.65,13727.18,216.65,0.237825
|
|
71
|
+
69,14975.615234,0.009261,159.1403,155.3448,13386.45,13414.65,216.65,0.249786
|
|
72
|
+
70,15508.256836,0.011806,167.045,163.0927,13077.79,13104.7,216.65,0.262244
|
|
73
|
+
71,16026.115234,0.014816,175.2731,171.1591,12771.64,12797.3,216.65,0.275215
|
|
74
|
+
72,16527.322266,0.018318,183.8344,179.5537,12467.99,12492.44,216.65,0.288713
|
|
75
|
+
73,17008.789063,0.022355,192.7389,188.2867,12166.81,12190.1,216.65,0.302755
|
|
76
|
+
74,17467.613281,0.026964,201.9969,197.3679,11868.08,11890.24,216.65,0.317357
|
|
77
|
+
75,17901.621094,0.032176,211.6186,206.8078,11571.79,11592.86,216.65,0.332536
|
|
78
|
+
76,18308.433594,0.038026,221.6146,216.6166,11277.92,11297.93,216.65,0.348308
|
|
79
|
+
77,18685.71875,0.044548,231.9954,226.805,10986.7,11005.69,216.74,0.364545
|
|
80
|
+
78,19031.289063,0.051773,242.7719,237.3837,10696.22,10714.22,218.62,0.378253
|
|
81
|
+
79,19343.511719,0.059728,253.9549,248.3634,10405.61,10422.64,220.51,0.392358
|
|
82
|
+
80,19620.042969,0.068448,265.5556,259.7553,10114.89,10130.98,222.4,0.406868
|
|
83
|
+
81,19859.390625,0.077958,277.5852,271.5704,9824.08,9839.26,224.29,0.42179
|
|
84
|
+
82,20059.931641,0.088286,290.0548,283.82,9533.2,9547.49,226.18,0.43713
|
|
85
|
+
83,20219.664063,0.099462,302.9762,296.5155,9242.26,9255.7,228.08,0.452897
|
|
86
|
+
84,20337.863281,0.111505,316.3607,309.6684,8951.3,8963.9,229.97,0.469097
|
|
87
|
+
85,20412.308594,0.124448,330.2202,323.2904,8660.32,8672.11,231.86,0.485737
|
|
88
|
+
86,20442.078125,0.138313,344.5663,337.3932,8369.35,8380.36,233.75,0.502825
|
|
89
|
+
87,20425.71875,0.153125,359.4111,351.9887,8078.41,8088.67,235.64,0.520367
|
|
90
|
+
88,20361.816406,0.16891,374.7666,367.0889,7787.51,7797.04,237.53,0.53837
|
|
91
|
+
89,20249.511719,0.185689,390.645,382.7058,7496.68,7505.51,239.42,0.556842
|
|
92
|
+
90,20087.085938,0.203491,407.0583,398.8516,7205.93,7214.09,241.31,0.57579
|
|
93
|
+
91,19874.025391,0.222333,424.019,415.5387,6915.29,6922.8,243.2,0.595219
|
|
94
|
+
92,19608.572266,0.242244,441.5395,432.7792,6624.76,6631.66,245.09,0.615138
|
|
95
|
+
93,19290.226563,0.263242,459.6321,450.5858,6334.38,6340.68,246.98,0.635553
|
|
96
|
+
94,18917.460938,0.285354,478.3096,468.9708,6044.15,6049.89,248.86,0.656471
|
|
97
|
+
95,18489.707031,0.308598,497.5845,487.947,5754.1,5759.3,250.75,0.677899
|
|
98
|
+
96,18006.925781,0.332939,517.4198,507.5021,5464.6,5469.3,252.63,0.699815
|
|
99
|
+
97,17471.839844,0.358254,537.7195,527.5696,5176.77,5180.98,254.5,0.722139
|
|
100
|
+
98,16888.6875,0.384363,558.343,548.0312,4892.26,4896.02,256.35,0.744735
|
|
101
|
+
99,16262.046875,0.411125,579.1926,568.7678,4612.58,4615.92,258.17,0.767472
|
|
102
|
+
100,15596.695313,0.438391,600.1668,589.6797,4338.77,4341.73,259.95,0.790242
|
|
103
|
+
101,14898.453125,0.466003,621.1624,610.6646,4071.8,4074.41,261.68,0.812937
|
|
104
|
+
102,14173.324219,0.4938,642.0764,631.6194,3812.53,3814.82,263.37,0.835453
|
|
105
|
+
103,13427.769531,0.521619,662.8084,652.4424,3561.7,3563.69,265.0,0.857686
|
|
106
|
+
104,12668.257813,0.549301,683.262,673.0352,3319.94,3321.67,266.57,0.879541
|
|
107
|
+
105,11901.339844,0.576692,703.3467,693.3043,3087.75,3089.25,268.08,0.900929
|
|
108
|
+
106,11133.304688,0.603648,722.9795,713.1631,2865.54,2866.83,269.52,0.921768
|
|
109
|
+
107,10370.175781,0.630036,742.0855,732.5325,2653.58,2654.69,270.9,0.941988
|
|
110
|
+
108,9617.515625,0.655736,760.5996,751.3426,2452.04,2452.99,272.21,0.961527
|
|
111
|
+
109,8880.453125,0.680643,778.4661,769.5329,2260.99,2261.8,273.45,0.980334
|
|
112
|
+
110,8163.375,0.704669,795.6396,787.0528,2080.41,2081.09,274.63,0.998368
|
|
113
|
+
111,7470.34375,0.727739,812.0847,803.8622,1910.19,1910.76,275.73,1.015598
|
|
114
|
+
112,6804.421875,0.749797,827.7756,819.9302,1750.14,1750.63,276.77,1.032005
|
|
115
|
+
113,6168.53125,0.770798,842.6959,835.2358,1600.04,1600.44,277.75,1.047576
|
|
116
|
+
114,5564.382813,0.790717,856.8376,849.7668,1459.58,1459.91,278.66,1.06231
|
|
117
|
+
115,4993.796875,0.809536,870.2004,863.519,1328.43,1328.7,279.52,1.076209
|
|
118
|
+
116,4457.375,0.827256,882.791,876.4957,1206.21,1206.44,280.31,1.089286
|
|
119
|
+
117,3955.960938,0.843881,894.6222,888.7066,1092.54,1092.73,281.05,1.101558
|
|
120
|
+
118,3489.234375,0.859432,905.7116,900.1669,987.0,987.15,281.73,1.113047
|
|
121
|
+
119,3057.265625,0.873929,916.0815,910.8965,889.17,889.29,282.37,1.123777
|
|
122
|
+
120,2659.140625,0.887408,925.7571,920.9193,798.62,798.72,282.96,1.133779
|
|
123
|
+
121,2294.242188,0.8999,934.7666,930.2618,714.94,715.02,283.5,1.143084
|
|
124
|
+
122,1961.5,0.911448,943.1399,938.9532,637.7,637.76,284.0,1.151724
|
|
125
|
+
123,1659.476563,0.922096,950.9082,947.024,566.49,566.54,284.47,1.159733
|
|
126
|
+
124,1387.546875,0.931881,958.1037,954.5059,500.91,500.95,284.89,1.167147
|
|
127
|
+
125,1143.25,0.94086,964.7584,961.4311,440.58,440.61,285.29,1.173999
|
|
128
|
+
126,926.507813,0.949064,970.9046,967.8315,385.14,385.16,285.65,1.180323
|
|
129
|
+
127,734.992188,0.95655,976.5737,973.7392,334.22,334.24,285.98,1.186154
|
|
130
|
+
128,568.0625,0.963352,981.7968,979.1852,287.51,287.52,286.28,1.191523
|
|
131
|
+
129,424.414063,0.969513,986.6036,984.2002,244.68,244.69,286.56,1.196462
|
|
132
|
+
130,302.476563,0.975078,991.023,988.8133,205.44,205.44,286.81,1.201001
|
|
133
|
+
131,202.484375,0.980072,995.0824,993.0527,169.5,169.51,287.05,1.205168
|
|
134
|
+
132,122.101563,0.984542,998.8081,996.9452,136.62,136.62,287.26,1.208992
|
|
135
|
+
133,62.78125,0.9885,1002.225,1000.5165,106.54,106.54,287.46,1.212498
|
|
136
|
+
134,22.835938,0.991984,1005.3562,1003.7906,79.04,79.04,287.64,1.21571
|
|
137
|
+
135,3.757813,0.995003,1008.2239,1006.79,53.92,53.92,287.8,1.21865
|
|
138
|
+
136,0.0,0.99763,1010.8487,1009.5363,30.96,30.96,287.95,1.221341
|
|
139
|
+
137,0.0,1.0,1013.25,1012.0494,10.0,10.0,288.09,1.223803
|