pycontrails 0.53.0__cp313-cp313-win_amd64.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.

Files changed (109) hide show
  1. pycontrails/__init__.py +70 -0
  2. pycontrails/_version.py +16 -0
  3. pycontrails/core/__init__.py +30 -0
  4. pycontrails/core/aircraft_performance.py +641 -0
  5. pycontrails/core/airports.py +226 -0
  6. pycontrails/core/cache.py +881 -0
  7. pycontrails/core/coordinates.py +174 -0
  8. pycontrails/core/fleet.py +470 -0
  9. pycontrails/core/flight.py +2312 -0
  10. pycontrails/core/flightplan.py +220 -0
  11. pycontrails/core/fuel.py +140 -0
  12. pycontrails/core/interpolation.py +721 -0
  13. pycontrails/core/met.py +2833 -0
  14. pycontrails/core/met_var.py +307 -0
  15. pycontrails/core/models.py +1181 -0
  16. pycontrails/core/polygon.py +549 -0
  17. pycontrails/core/rgi_cython.cp313-win_amd64.pyd +0 -0
  18. pycontrails/core/vector.py +2191 -0
  19. pycontrails/datalib/__init__.py +12 -0
  20. pycontrails/datalib/_leo_utils/search.py +250 -0
  21. pycontrails/datalib/_leo_utils/static/bq_roi_query.sql +6 -0
  22. pycontrails/datalib/_leo_utils/vis.py +59 -0
  23. pycontrails/datalib/_met_utils/metsource.py +743 -0
  24. pycontrails/datalib/ecmwf/__init__.py +53 -0
  25. pycontrails/datalib/ecmwf/arco_era5.py +527 -0
  26. pycontrails/datalib/ecmwf/common.py +109 -0
  27. pycontrails/datalib/ecmwf/era5.py +538 -0
  28. pycontrails/datalib/ecmwf/era5_model_level.py +482 -0
  29. pycontrails/datalib/ecmwf/hres.py +782 -0
  30. pycontrails/datalib/ecmwf/hres_model_level.py +495 -0
  31. pycontrails/datalib/ecmwf/ifs.py +284 -0
  32. pycontrails/datalib/ecmwf/model_levels.py +79 -0
  33. pycontrails/datalib/ecmwf/static/model_level_dataframe_v20240418.csv +139 -0
  34. pycontrails/datalib/ecmwf/variables.py +256 -0
  35. pycontrails/datalib/gfs/__init__.py +28 -0
  36. pycontrails/datalib/gfs/gfs.py +646 -0
  37. pycontrails/datalib/gfs/variables.py +100 -0
  38. pycontrails/datalib/goes.py +772 -0
  39. pycontrails/datalib/landsat.py +568 -0
  40. pycontrails/datalib/sentinel.py +512 -0
  41. pycontrails/datalib/spire.py +739 -0
  42. pycontrails/ext/bada.py +41 -0
  43. pycontrails/ext/cirium.py +14 -0
  44. pycontrails/ext/empirical_grid.py +140 -0
  45. pycontrails/ext/synthetic_flight.py +426 -0
  46. pycontrails/models/__init__.py +1 -0
  47. pycontrails/models/accf.py +406 -0
  48. pycontrails/models/apcemm/__init__.py +8 -0
  49. pycontrails/models/apcemm/apcemm.py +983 -0
  50. pycontrails/models/apcemm/inputs.py +226 -0
  51. pycontrails/models/apcemm/static/apcemm_yaml_template.yaml +183 -0
  52. pycontrails/models/apcemm/utils.py +437 -0
  53. pycontrails/models/cocip/__init__.py +29 -0
  54. pycontrails/models/cocip/cocip.py +2617 -0
  55. pycontrails/models/cocip/cocip_params.py +299 -0
  56. pycontrails/models/cocip/cocip_uncertainty.py +285 -0
  57. pycontrails/models/cocip/contrail_properties.py +1517 -0
  58. pycontrails/models/cocip/output_formats.py +2261 -0
  59. pycontrails/models/cocip/radiative_forcing.py +1262 -0
  60. pycontrails/models/cocip/radiative_heating.py +520 -0
  61. pycontrails/models/cocip/unterstrasser_wake_vortex.py +403 -0
  62. pycontrails/models/cocip/wake_vortex.py +396 -0
  63. pycontrails/models/cocip/wind_shear.py +120 -0
  64. pycontrails/models/cocipgrid/__init__.py +9 -0
  65. pycontrails/models/cocipgrid/cocip_grid.py +2573 -0
  66. pycontrails/models/cocipgrid/cocip_grid_params.py +138 -0
  67. pycontrails/models/dry_advection.py +486 -0
  68. pycontrails/models/emissions/__init__.py +21 -0
  69. pycontrails/models/emissions/black_carbon.py +594 -0
  70. pycontrails/models/emissions/emissions.py +1353 -0
  71. pycontrails/models/emissions/ffm2.py +336 -0
  72. pycontrails/models/emissions/static/default-engine-uids.csv +239 -0
  73. pycontrails/models/emissions/static/edb-gaseous-v29b-engines.csv +596 -0
  74. pycontrails/models/emissions/static/edb-nvpm-v29b-engines.csv +215 -0
  75. pycontrails/models/humidity_scaling/__init__.py +37 -0
  76. pycontrails/models/humidity_scaling/humidity_scaling.py +1025 -0
  77. pycontrails/models/humidity_scaling/quantiles/era5-model-level-quantiles.pq +0 -0
  78. pycontrails/models/humidity_scaling/quantiles/era5-pressure-level-quantiles.pq +0 -0
  79. pycontrails/models/issr.py +210 -0
  80. pycontrails/models/pcc.py +327 -0
  81. pycontrails/models/pcr.py +154 -0
  82. pycontrails/models/ps_model/__init__.py +17 -0
  83. pycontrails/models/ps_model/ps_aircraft_params.py +376 -0
  84. pycontrails/models/ps_model/ps_grid.py +505 -0
  85. pycontrails/models/ps_model/ps_model.py +1017 -0
  86. pycontrails/models/ps_model/ps_operational_limits.py +540 -0
  87. pycontrails/models/ps_model/static/ps-aircraft-params-20240524.csv +68 -0
  88. pycontrails/models/ps_model/static/ps-synonym-list-20240524.csv +103 -0
  89. pycontrails/models/sac.py +459 -0
  90. pycontrails/models/tau_cirrus.py +168 -0
  91. pycontrails/physics/__init__.py +1 -0
  92. pycontrails/physics/constants.py +116 -0
  93. pycontrails/physics/geo.py +989 -0
  94. pycontrails/physics/jet.py +837 -0
  95. pycontrails/physics/thermo.py +451 -0
  96. pycontrails/physics/units.py +472 -0
  97. pycontrails/py.typed +0 -0
  98. pycontrails/utils/__init__.py +1 -0
  99. pycontrails/utils/dependencies.py +66 -0
  100. pycontrails/utils/iteration.py +13 -0
  101. pycontrails/utils/json.py +188 -0
  102. pycontrails/utils/temp.py +50 -0
  103. pycontrails/utils/types.py +165 -0
  104. pycontrails-0.53.0.dist-info/LICENSE +178 -0
  105. pycontrails-0.53.0.dist-info/METADATA +181 -0
  106. pycontrails-0.53.0.dist-info/NOTICE +43 -0
  107. pycontrails-0.53.0.dist-info/RECORD +109 -0
  108. pycontrails-0.53.0.dist-info/WHEEL +5 -0
  109. pycontrails-0.53.0.dist-info/top_level.txt +3 -0
@@ -0,0 +1,406 @@
1
+ """Algorithmic Climate Change Functions."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import warnings
6
+ from dataclasses import dataclass
7
+ from typing import Any, overload
8
+
9
+ import xarray as xr
10
+
11
+ import pycontrails
12
+ from pycontrails.core.flight import Flight
13
+ from pycontrails.core.met import MetDataset, standardize_variables
14
+ from pycontrails.core.met_var import (
15
+ AirTemperature,
16
+ EastwardWind,
17
+ Geopotential,
18
+ NorthwardWind,
19
+ RelativeHumidity,
20
+ SpecificHumidity,
21
+ )
22
+ from pycontrails.core.models import Model, ModelParams
23
+ from pycontrails.core.vector import GeoVectorDataset
24
+ from pycontrails.datalib import ecmwf
25
+ from pycontrails.utils import dependencies
26
+
27
+
28
+ def wide_body_jets() -> set[str]:
29
+ """Return a set of wide body jets."""
30
+ return {
31
+ "A332",
32
+ "A333",
33
+ "A338",
34
+ "A339",
35
+ "A342",
36
+ "A343",
37
+ "A345",
38
+ "A356",
39
+ "A359",
40
+ "A388",
41
+ "B762",
42
+ "B763",
43
+ "B764",
44
+ "B772",
45
+ "B773",
46
+ "B778",
47
+ "B779",
48
+ "B788",
49
+ "B789",
50
+ }
51
+
52
+
53
+ def regional_jets() -> set[str]:
54
+ """Return a set of regional jets."""
55
+ return {
56
+ "CRJ1",
57
+ "CRJ2",
58
+ "CRJ7",
59
+ "CRJ8",
60
+ "CRJ9",
61
+ "CRJX",
62
+ "E135",
63
+ "E145",
64
+ "E170",
65
+ "E190",
66
+ }
67
+
68
+
69
+ @dataclass
70
+ class ACCFParams(ModelParams):
71
+ """Default ACCF model parameters.
72
+
73
+ See `config-user.yml` definition at
74
+ https://github.com/dlr-pa/climaccf
75
+ """
76
+
77
+ lat_bound: tuple[float, float] | None = None
78
+ lon_bound: tuple[float, float] | None = None
79
+
80
+ efficacy: bool = True
81
+ efficacy_option: str = "lee_2021"
82
+
83
+ accf_v: str = "V1.0"
84
+
85
+ ch4_scaling: float = 1.0
86
+ co2_scaling: float = 1.0
87
+ cont_scaling: float = 1.0
88
+ h2o_scaling: float = 1.0
89
+ o3_scaling: float = 1.0
90
+
91
+ forecast_step: float = 6.0
92
+
93
+ sep_ri_rw: bool = False
94
+
95
+ climate_indicator: str = "ATR"
96
+
97
+ horizontal_resolution: float = 0.5
98
+
99
+ emission_scenario: str = "pulse"
100
+
101
+ time_horizon: int = 20
102
+
103
+ pfca: str = "PCFA-ISSR"
104
+
105
+ merged: bool = True
106
+
107
+ #: RHI Threshold
108
+ issr_rhi_threshold: float = 0.9
109
+ issr_temp_threshold: float = 235
110
+
111
+ sac_ei_h2o: float = 1.25
112
+ sac_q: float = 43000000.0
113
+ sac_eta: float = 0.3
114
+
115
+ nox_ei: str = "TTV"
116
+
117
+ PMO: bool = False
118
+
119
+
120
+ class ACCF(Model):
121
+ """Compute Algorithmic Climate Change Functions (ACCF).
122
+
123
+ This class is a wrapper over the DLR / UMadrid library
124
+ `climaccf <https://github.com/dlr-pa/climaccf>`_,
125
+ `DOI: 10.5281/zenodo.6977272 <https://doi.org/10.5281/zenodo.6977272>`_
126
+
127
+ Parameters
128
+ ----------
129
+ met : MetDataset
130
+ Dataset containing "air_temperature" and "specific_humidity" variables
131
+ surface : MetDataset, optional
132
+ Dataset containing "surface_solar_downward_radiation" and
133
+ "top_net_thermal_radiation" variables
134
+
135
+ References
136
+ ----------
137
+ - :cite:`dietmullerDlrpaClimaccfDataset2022`
138
+ - :cite:`dietmullerPythonLibraryComputing2022`
139
+
140
+ """
141
+
142
+ name = "accr"
143
+ long_name = "algorithmic climate change functions"
144
+ met_variables = (
145
+ AirTemperature,
146
+ SpecificHumidity,
147
+ ecmwf.PotentialVorticity,
148
+ Geopotential,
149
+ RelativeHumidity,
150
+ NorthwardWind,
151
+ EastwardWind,
152
+ ecmwf.PotentialVorticity,
153
+ )
154
+ sur_variables = (ecmwf.SurfaceSolarDownwardRadiation, ecmwf.TopNetThermalRadiation)
155
+ default_params = ACCFParams
156
+
157
+ short_vars = frozenset(v.short_name for v in (*met_variables, *sur_variables))
158
+
159
+ # This variable won't get used since we are not writing the output
160
+ # anywhere, but the library will complain if it's not defined
161
+ path_lib = "./"
162
+
163
+ def __init__(
164
+ self,
165
+ met: MetDataset,
166
+ surface: MetDataset | None = None,
167
+ params: dict[str, Any] | None = None,
168
+ **params_kwargs: Any,
169
+ ) -> None:
170
+ # Normalize ECMWF variables
171
+ met = standardize_variables(met, self.met_variables)
172
+
173
+ # Ignore humidity scaling warning
174
+ with warnings.catch_warnings():
175
+ warnings.filterwarnings("ignore", module="pycontrails.core.models")
176
+ super().__init__(met, params=params, **params_kwargs)
177
+
178
+ if surface:
179
+ surface = surface.copy()
180
+ surface = standardize_variables(surface, self.sur_variables)
181
+ surface.data = _rad_instantaneous_to_accumulated(surface.data)
182
+ self.surface = surface
183
+
184
+ @overload
185
+ def eval(self, source: Flight, **params: Any) -> Flight: ...
186
+
187
+ @overload
188
+ def eval(self, source: GeoVectorDataset, **params: Any) -> GeoVectorDataset: ...
189
+
190
+ @overload
191
+ def eval(self, source: MetDataset | None = ..., **params: Any) -> MetDataset: ...
192
+
193
+ def eval(
194
+ self, source: GeoVectorDataset | Flight | MetDataset | None = None, **params: Any
195
+ ) -> GeoVectorDataset | Flight | MetDataset:
196
+ """Evaluate accfs along flight trajectory or on meteorology grid.
197
+
198
+ Parameters
199
+ ----------
200
+ source : GeoVectorDataset | Flight | MetDataset | None, optional
201
+ Input GeoVectorDataset or Flight.
202
+ If None, evaluates at the :attr:`met` grid points.
203
+ **params : Any
204
+ Overwrite model parameters before eval
205
+
206
+ Returns
207
+ -------
208
+ GeoVectorDataset | Flight | MetDataArray
209
+ Returns `np.nan` if interpolating outside meteorology grid.
210
+
211
+ Raises
212
+ ------
213
+ NotImplementedError
214
+ Raises if input ``source`` is not supported.
215
+ """
216
+ try:
217
+ from climaccf.accf import GeTaCCFs
218
+ except ModuleNotFoundError as e:
219
+ dependencies.raise_module_not_found_error(
220
+ name="ACCF.eval method",
221
+ package_name="climaccf",
222
+ module_not_found_error=e,
223
+ pycontrails_optional_package="accf",
224
+ )
225
+
226
+ self.update_params(params)
227
+ self.set_source(source)
228
+
229
+ if isinstance(self.source, GeoVectorDataset):
230
+ self.downselect_met()
231
+ if hasattr(self, "surface"):
232
+ self.surface = self.source.downselect_met(self.surface)
233
+
234
+ if isinstance(self.source, MetDataset):
235
+ # Overwrite horizontal resolution to match met
236
+ longitude = self.source.data["longitude"].values
237
+ if longitude.size > 1:
238
+ hres = abs(longitude[1] - longitude[0])
239
+ self.params["horizontal_resolution"] = float(hres)
240
+
241
+ else:
242
+ latitude = self.source.data["latitude"].values
243
+ if latitude.size > 1:
244
+ hres = abs(latitude[1] - latitude[0])
245
+ self.params["horizontal_resolution"] = float(hres)
246
+
247
+ p_settings = _get_accf_config(self.params)
248
+
249
+ self.set_source_met()
250
+ self._generate_weather_store(p_settings)
251
+
252
+ # check aircraft type and set in config if needed
253
+ if self.params["nox_ei"] != "TTV":
254
+ if isinstance(self.source, Flight):
255
+ ac = self.source.attrs["aircraft_type"]
256
+ if ac in wide_body_jets():
257
+ p_settings["ac_type"] = "wide-body"
258
+ elif ac in regional_jets():
259
+ p_settings["ac_type"] = "regional"
260
+ else:
261
+ p_settings["ac_type"] = "single-aisle"
262
+ else:
263
+ p_settings["ac_type"] = "wide-body"
264
+
265
+ clim_imp = GeTaCCFs(self)
266
+ clim_imp.get_accfs(**p_settings)
267
+ aCCFs, _ = clim_imp.get_xarray()
268
+
269
+ # assign ACCF outputs to source
270
+ maCCFs = MetDataset(aCCFs)
271
+ for key, arr in maCCFs.data.items():
272
+ # skip met variables
273
+ if key in self.short_vars:
274
+ continue
275
+
276
+ assert isinstance(key, str)
277
+ if isinstance(self.source, GeoVectorDataset):
278
+ self.source[key] = self.source.intersect_met(maCCFs[key])
279
+ else:
280
+ self.source[key] = arr
281
+
282
+ self.transfer_met_source_attrs()
283
+ self.source.attrs["pycontrails_version"] = pycontrails.__version__
284
+
285
+ return self.source
286
+
287
+ def _generate_weather_store(self, p_settings: dict[str, Any]) -> None:
288
+ from climaccf.weather_store import WeatherStore
289
+
290
+ # The library does not call the coordinates by name, it just slices the
291
+ # underlying data array, so we need to put them in the expected order.
292
+ # It also needs variables to have the ECMWF short name
293
+ if isinstance(self.met, MetDataset):
294
+ ds_met = self.met.data.transpose("time", "level", "latitude", "longitude")
295
+ name_dict = {v.standard_name: v.short_name for v in self.met_variables}
296
+ ds_met = ds_met.rename(name_dict)
297
+ else:
298
+ ds_met = None
299
+
300
+ if hasattr(self, "surface"):
301
+ ds_sur = self.surface.data.squeeze().transpose("time", "latitude", "longitude")
302
+ name_dict = {v.standard_name: v.short_name for v in self.sur_variables}
303
+ ds_sur = ds_sur.rename(name_dict)
304
+ else:
305
+ ds_sur = None
306
+
307
+ ws = WeatherStore(
308
+ ds_met,
309
+ ds_sur,
310
+ ll_resolution=p_settings["horizontal_resolution"],
311
+ forecast_step=p_settings["forecast_step"],
312
+ )
313
+
314
+ if p_settings["lat_bound"] and p_settings["lon_bound"]:
315
+ ws.reduce_domain(
316
+ {
317
+ "latitude": p_settings["lat_bound"],
318
+ "longitude": p_settings["lon_bound"],
319
+ }
320
+ )
321
+
322
+ self.ds = ws.get_xarray()
323
+ self.variable_names = ws.variable_names
324
+ self.pre_variable_names = ws.pre_variable_names
325
+ self.coordinate_names = ws.coordinate_names
326
+ self.pre_coordinate_names = ws.pre_coordinate_names
327
+ self.coordinates_bool = ws.coordinates_bool
328
+ self.aCCF_bool = ws.aCCF_bool
329
+ self.axes = ws.axes
330
+ self.var_xr = ws.var_xr
331
+
332
+
333
+ def _get_accf_config(params: dict[str, Any]) -> dict[str, Any]:
334
+ # a good portion of these will get ignored since we are not producing an
335
+ # output file, but the library will complain if they aren't defined
336
+ return {
337
+ "lat_bound": params["lat_bound"],
338
+ "lon_bound": params["lon_bound"],
339
+ "time_bound": None,
340
+ "horizontal_resolution": params["horizontal_resolution"],
341
+ "forecast_step": params["forecast_step"],
342
+ "NOx_aCCF": True,
343
+ "NOx&inverse_EIs": params["nox_ei"],
344
+ "output_format": "netCDF",
345
+ "mean": False,
346
+ "std": False,
347
+ "merged": params["merged"],
348
+ "aCCF-V": params["accf_v"],
349
+ "efficacy": params["efficacy"],
350
+ "efficacy-option": params["efficacy_option"],
351
+ "emission_scenario": params["emission_scenario"],
352
+ "climate_indicator": params["climate_indicator"],
353
+ "TimeHorizon": params["time_horizon"],
354
+ "ac_type": "wide-body",
355
+ "sep_ri_rw": params["sep_ri_rw"],
356
+ "PMO": params["PMO"],
357
+ "aCCF-scalingF": {
358
+ "CH4": params["ch4_scaling"],
359
+ "CO2": params["co2_scaling"],
360
+ "Cont.": params["cont_scaling"],
361
+ "H2O": params["h2o_scaling"],
362
+ "O3": params["o3_scaling"],
363
+ },
364
+ "PCFA": params["pfca"],
365
+ "PCFA-ISSR": {
366
+ "rhi_threshold": params["issr_rhi_threshold"],
367
+ "temp_threshold": params["issr_temp_threshold"],
368
+ },
369
+ "PCFA-SAC": {
370
+ "EI_H2O": params["sac_ei_h2o"],
371
+ "Q": params["sac_q"],
372
+ "eta": params["sac_eta"],
373
+ },
374
+ "Chotspots": False,
375
+ "hotspots_binary": True,
376
+ "color": "Reds",
377
+ "geojson": False,
378
+ "save_path": "./",
379
+ "save_format": "netCDF",
380
+ }
381
+
382
+
383
+ def _rad_instantaneous_to_accumulated(ds: xr.Dataset) -> xr.Dataset:
384
+ """Convert instantaneous radiation to accumulated radiation."""
385
+
386
+ for name, da in ds.items():
387
+ try:
388
+ unit = da.attrs["units"]
389
+ except KeyError as e:
390
+ msg = (
391
+ f"Radiation data contains '{name}' variable "
392
+ "but units are not specified. Provide units in the "
393
+ f"rad['{name}'].attrs passed into ACCF."
394
+ )
395
+ raise KeyError(msg) from e
396
+
397
+ if unit == "J m**-2":
398
+ continue
399
+ if unit != "W m**-2":
400
+ msg = f"Unexpected units '{unit}' for '{name}'. Expected 'J m**-2' or 'W m**-2'."
401
+ raise ValueError(msg)
402
+
403
+ # Convert from W m**-2 to J m**-2
404
+ ds[name] = da.assign_attrs(units="J m**-2") * 3600.0
405
+
406
+ return ds
@@ -0,0 +1,8 @@
1
+ """Contrail Cirrus Prediction (CoCiP) modeling support."""
2
+
3
+ from pycontrails.models.apcemm.apcemm import APCEMM, APCEMMParams
4
+
5
+ __all__ = [
6
+ "APCEMM",
7
+ "APCEMMParams",
8
+ ]