pycontrails 0.58.0__cp314-cp314-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.
- pycontrails/__init__.py +70 -0
- pycontrails/_version.py +34 -0
- pycontrails/core/__init__.py +30 -0
- pycontrails/core/aircraft_performance.py +679 -0
- pycontrails/core/airports.py +228 -0
- pycontrails/core/cache.py +889 -0
- pycontrails/core/coordinates.py +174 -0
- pycontrails/core/fleet.py +483 -0
- pycontrails/core/flight.py +2185 -0
- pycontrails/core/flightplan.py +228 -0
- pycontrails/core/fuel.py +140 -0
- pycontrails/core/interpolation.py +702 -0
- pycontrails/core/met.py +2931 -0
- pycontrails/core/met_var.py +387 -0
- pycontrails/core/models.py +1321 -0
- pycontrails/core/polygon.py +549 -0
- pycontrails/core/rgi_cython.cp314-win_amd64.pyd +0 -0
- pycontrails/core/vector.py +2249 -0
- pycontrails/datalib/__init__.py +12 -0
- pycontrails/datalib/_met_utils/metsource.py +746 -0
- pycontrails/datalib/ecmwf/__init__.py +73 -0
- pycontrails/datalib/ecmwf/arco_era5.py +345 -0
- pycontrails/datalib/ecmwf/common.py +114 -0
- pycontrails/datalib/ecmwf/era5.py +554 -0
- pycontrails/datalib/ecmwf/era5_model_level.py +490 -0
- pycontrails/datalib/ecmwf/hres.py +804 -0
- pycontrails/datalib/ecmwf/hres_model_level.py +466 -0
- pycontrails/datalib/ecmwf/ifs.py +287 -0
- pycontrails/datalib/ecmwf/model_levels.py +435 -0
- pycontrails/datalib/ecmwf/static/model_level_dataframe_v20240418.csv +139 -0
- pycontrails/datalib/ecmwf/variables.py +268 -0
- pycontrails/datalib/geo_utils.py +261 -0
- pycontrails/datalib/gfs/__init__.py +28 -0
- pycontrails/datalib/gfs/gfs.py +656 -0
- pycontrails/datalib/gfs/variables.py +104 -0
- pycontrails/datalib/goes.py +757 -0
- pycontrails/datalib/himawari/__init__.py +27 -0
- pycontrails/datalib/himawari/header_struct.py +266 -0
- pycontrails/datalib/himawari/himawari.py +667 -0
- pycontrails/datalib/landsat.py +589 -0
- pycontrails/datalib/leo_utils/__init__.py +5 -0
- pycontrails/datalib/leo_utils/correction.py +266 -0
- pycontrails/datalib/leo_utils/landsat_metadata.py +300 -0
- pycontrails/datalib/leo_utils/search.py +250 -0
- pycontrails/datalib/leo_utils/sentinel_metadata.py +748 -0
- pycontrails/datalib/leo_utils/static/bq_roi_query.sql +6 -0
- pycontrails/datalib/leo_utils/vis.py +59 -0
- pycontrails/datalib/sentinel.py +650 -0
- pycontrails/datalib/spire/__init__.py +5 -0
- pycontrails/datalib/spire/exceptions.py +62 -0
- pycontrails/datalib/spire/spire.py +604 -0
- pycontrails/ext/bada.py +42 -0
- pycontrails/ext/cirium.py +14 -0
- pycontrails/ext/empirical_grid.py +140 -0
- pycontrails/ext/synthetic_flight.py +431 -0
- pycontrails/models/__init__.py +1 -0
- pycontrails/models/accf.py +425 -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 +2742 -0
- pycontrails/models/cocip/cocip_params.py +305 -0
- pycontrails/models/cocip/cocip_uncertainty.py +291 -0
- pycontrails/models/cocip/contrail_properties.py +1530 -0
- pycontrails/models/cocip/output_formats.py +2270 -0
- pycontrails/models/cocip/radiative_forcing.py +1260 -0
- pycontrails/models/cocip/radiative_heating.py +520 -0
- pycontrails/models/cocip/unterstrasser_wake_vortex.py +508 -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 +2552 -0
- pycontrails/models/cocipgrid/cocip_grid_params.py +138 -0
- pycontrails/models/dry_advection.py +602 -0
- pycontrails/models/emissions/__init__.py +21 -0
- pycontrails/models/emissions/black_carbon.py +599 -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/extended_k15.py +1327 -0
- pycontrails/models/humidity_scaling/__init__.py +37 -0
- pycontrails/models/humidity_scaling/humidity_scaling.py +1075 -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 +326 -0
- pycontrails/models/pcr.py +154 -0
- pycontrails/models/ps_model/__init__.py +18 -0
- pycontrails/models/ps_model/ps_aircraft_params.py +381 -0
- pycontrails/models/ps_model/ps_grid.py +701 -0
- pycontrails/models/ps_model/ps_model.py +1000 -0
- pycontrails/models/ps_model/ps_operational_limits.py +525 -0
- pycontrails/models/ps_model/static/ps-aircraft-params-20250328.csv +69 -0
- pycontrails/models/ps_model/static/ps-synonym-list-20250328.csv +104 -0
- pycontrails/models/sac.py +442 -0
- pycontrails/models/tau_cirrus.py +183 -0
- pycontrails/physics/__init__.py +1 -0
- pycontrails/physics/constants.py +117 -0
- pycontrails/physics/geo.py +1138 -0
- pycontrails/physics/jet.py +968 -0
- pycontrails/physics/static/iata-cargo-load-factors-20250221.csv +74 -0
- pycontrails/physics/static/iata-passenger-load-factors-20250221.csv +74 -0
- pycontrails/physics/thermo.py +551 -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 +187 -0
- pycontrails/utils/temp.py +50 -0
- pycontrails/utils/types.py +163 -0
- pycontrails-0.58.0.dist-info/METADATA +180 -0
- pycontrails-0.58.0.dist-info/RECORD +122 -0
- pycontrails-0.58.0.dist-info/WHEEL +5 -0
- pycontrails-0.58.0.dist-info/licenses/LICENSE +178 -0
- pycontrails-0.58.0.dist-info/licenses/NOTICE +43 -0
- pycontrails-0.58.0.dist-info/top_level.txt +3 -0
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
"""Default parameters for CoCiP models.
|
|
2
|
+
|
|
3
|
+
Used by :class:`Cocip` and :class:`CocipGrid`.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
import dataclasses
|
|
9
|
+
|
|
10
|
+
import numpy as np
|
|
11
|
+
import numpy.typing as npt
|
|
12
|
+
|
|
13
|
+
from pycontrails.core.aircraft_performance import AircraftPerformance
|
|
14
|
+
from pycontrails.core.models import AdvectionBuffers
|
|
15
|
+
from pycontrails.models.emissions.emissions import EmissionsParams
|
|
16
|
+
from pycontrails.models.humidity_scaling import HumidityScaling
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def _radius_threshold_um() -> npt.NDArray[np.float32]:
|
|
20
|
+
return np.array([5.0, 9.5, 23.0, 190.0, 310.0], dtype=np.float32)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def _habit_distributions() -> npt.NDArray[np.float32]:
|
|
24
|
+
return np.array(
|
|
25
|
+
[
|
|
26
|
+
[0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0],
|
|
27
|
+
[0.0, 0.3, 0.0, 0.0, 0.0, 0.0, 0.7, 0.0],
|
|
28
|
+
[0.0, 0.3, 0.0, 0.0, 0.3, 0.0, 0.4, 0.0],
|
|
29
|
+
[0.0, 0.5, 0.0, 0.0, 0.15, 0.35, 0.0, 0.0],
|
|
30
|
+
[0.0, 0.45, 0.45, 0.1, 0.0, 0.0, 0.0, 0.0],
|
|
31
|
+
[0.0, 0.0, 0.0, 0.03, 0.97, 0.0, 0.0, 0.0],
|
|
32
|
+
],
|
|
33
|
+
dtype=np.float32,
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def _habits() -> npt.NDArray[np.str_]:
|
|
38
|
+
return np.array(
|
|
39
|
+
[
|
|
40
|
+
"Sphere",
|
|
41
|
+
"Solid column",
|
|
42
|
+
"Hollow column",
|
|
43
|
+
"Rough aggregate",
|
|
44
|
+
"Rosette-6",
|
|
45
|
+
"Plate",
|
|
46
|
+
"Droxtal",
|
|
47
|
+
"Myhre",
|
|
48
|
+
]
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
@dataclasses.dataclass
|
|
53
|
+
class CocipParams(AdvectionBuffers):
|
|
54
|
+
"""Model parameters required by the CoCiP models."""
|
|
55
|
+
|
|
56
|
+
# -------------------------
|
|
57
|
+
# Implementation parameters
|
|
58
|
+
# -------------------------
|
|
59
|
+
|
|
60
|
+
#: Determines whether :meth:`Cocip.process_emissions` runs on model :meth:`Cocip.eval`
|
|
61
|
+
#: Set to ``False`` when input Flight includes emissions data.
|
|
62
|
+
process_emissions: bool = True
|
|
63
|
+
|
|
64
|
+
#: Apply Euler's method with a fixed step size of ``dt_integration``. Advected waypoints
|
|
65
|
+
#: are interpolated against met data once each ``dt_integration``.
|
|
66
|
+
dt_integration: np.timedelta64 = np.timedelta64(30, "m")
|
|
67
|
+
|
|
68
|
+
#: Difference in altitude between top and bottom layer for stratification calculations,
|
|
69
|
+
#: [:math:`m`]. Used to approximate derivative of "lagrangian_tendency_of_air_pressure" layer.
|
|
70
|
+
dz_m: float = 200.0
|
|
71
|
+
|
|
72
|
+
#: Vertical resolution (m) associated to met data.
|
|
73
|
+
#: Constant below applies to ECMWF data.
|
|
74
|
+
effective_vertical_resolution: float = 2000.0
|
|
75
|
+
|
|
76
|
+
#: Smoothing parameters for true airspeed.
|
|
77
|
+
#: Only used for Flight models.
|
|
78
|
+
#: Passed directly to :func:`scipy.signal.savgol_filter`.
|
|
79
|
+
#: See :meth:`pycontrails.Flight.segment_true_airspeed` for details.
|
|
80
|
+
smooth_true_airspeed: bool = True
|
|
81
|
+
smooth_true_airspeed_window_length: int = 7
|
|
82
|
+
smooth_true_airspeed_polyorder: int = 1
|
|
83
|
+
|
|
84
|
+
#: Humidity scaling
|
|
85
|
+
humidity_scaling: HumidityScaling | None = None
|
|
86
|
+
|
|
87
|
+
#: Experimental. If ``True``, attempt to reduce memory consumption during
|
|
88
|
+
#: aircraft performance and initial contrail formation/persistent calculations
|
|
89
|
+
#: by calling :meth:`MetDataArray.interpolate` with ``lowmem=True``.
|
|
90
|
+
#:
|
|
91
|
+
#: **IMPORTANT**:
|
|
92
|
+
#:
|
|
93
|
+
#: * Memory optimizations used when ``proprocess_lowmem=True`` are designed for
|
|
94
|
+
#: meteorology backed by dask arrays with a chunk size of 1 along
|
|
95
|
+
#: the time dimension. This option may degrade performance if dask if not used
|
|
96
|
+
#: or if chunks contain more than a single time step.
|
|
97
|
+
#: * The impact on runtime of setting ``preprocess_lowmem=True`` depends on how
|
|
98
|
+
#: meteorology data is chunked. Runtime is likely to increase if meteorology
|
|
99
|
+
#: data is chunked in time only, but may decrease if meteorology data is also
|
|
100
|
+
#: chunked in longitude, latitude, and level.
|
|
101
|
+
#: * Changes to data access patterns with ``preprocess_lowmem=True`` alter locations
|
|
102
|
+
#: where interpolation is in- vs out-of-bounds. As a consequence,
|
|
103
|
+
#: Cocip output with ``preprocess_lowmem=True`` is only guaranteed to match output
|
|
104
|
+
#: with ``preprocess_lowmem=False`` when run with ``interpolation_bounds_error=True``
|
|
105
|
+
#: to ensure no out-of-bounds interpolation occurs.
|
|
106
|
+
#:
|
|
107
|
+
#: .. versionadded:: 0.52.3
|
|
108
|
+
preprocess_lowmem: bool = False
|
|
109
|
+
|
|
110
|
+
# --------------
|
|
111
|
+
# Downselect met
|
|
112
|
+
# --------------
|
|
113
|
+
|
|
114
|
+
#: Compute ``"tau_cirrus"`` variable in pressure-level met data during model
|
|
115
|
+
#: initialization. Must be one of ``"auto"``, ``True``, or ``False``. If set to
|
|
116
|
+
#: ``"auto"``, ``"tau_cirrus"`` will be computed during model initialization
|
|
117
|
+
#: iff the met data is dask-backed. Otherwise, it will be computed during model
|
|
118
|
+
#: evaluation after the met data is downselected.
|
|
119
|
+
#:
|
|
120
|
+
#: .. versionadded:: 0.47.1
|
|
121
|
+
compute_tau_cirrus_in_model_init: bool | str = "auto"
|
|
122
|
+
|
|
123
|
+
# ---------
|
|
124
|
+
# Filtering
|
|
125
|
+
# ---------
|
|
126
|
+
|
|
127
|
+
#: Filter out waypoints if the don't satisfy the SAC criteria
|
|
128
|
+
#: Note that the SAC algorithm will still be run to calculate
|
|
129
|
+
#: ``T_critical_sac`` for use estimating initial ice particle number.
|
|
130
|
+
#: Passing in a non-default value is unusual, but is included
|
|
131
|
+
#: to allow for false negative calibration and model uncertainty studies.
|
|
132
|
+
filter_sac: bool = True
|
|
133
|
+
|
|
134
|
+
#: Filter out waypoints if they don't satisfy the initial persistent criteria
|
|
135
|
+
#: Passing in a non-default value is unusual, but is included
|
|
136
|
+
#: to allow for false negative calibration and model uncertainty studies.
|
|
137
|
+
filter_initially_persistent: bool = True
|
|
138
|
+
|
|
139
|
+
#: Continue evolving contrail waypoints ``persistent_buffer`` beyond
|
|
140
|
+
#: end of contrail life.
|
|
141
|
+
#: Passing in a non-default value is unusual, but is included
|
|
142
|
+
#: to allow for false negative calibration and model uncertainty studies.
|
|
143
|
+
persistent_buffer: np.timedelta64 | None = None
|
|
144
|
+
|
|
145
|
+
# -------
|
|
146
|
+
# Outputs
|
|
147
|
+
# -------
|
|
148
|
+
|
|
149
|
+
#: Add additional values to the flight and contrail that are not explicitly
|
|
150
|
+
#: necessary for calculation.
|
|
151
|
+
#: See also :attr:`CocipGridParams.verbose_outputs_formation` and
|
|
152
|
+
#: :attr:`CocipGridParams.verbose_outputs_evolution`.
|
|
153
|
+
verbose_outputs: bool = False
|
|
154
|
+
|
|
155
|
+
#: Add additional metric of ATR20 and global yearly mean RF to model output.
|
|
156
|
+
#: These are not standard CoCiP outputs but based on the derivation used
|
|
157
|
+
#: in the first supplement to :cite:`yinPredictingClimateImpact2023`. ATR20 is defined
|
|
158
|
+
#: as the average temperature response over a 20 year horizon.
|
|
159
|
+
#:
|
|
160
|
+
#: .. versionadded:: 0.50.0
|
|
161
|
+
compute_atr20: bool = False
|
|
162
|
+
|
|
163
|
+
#: Constant factor used to convert global- and year-mean RF, [:math:`W m^{-2}`],
|
|
164
|
+
#: to ATR20, [:math:`K`], given by :cite:`yinPredictingClimateImpact2023`.
|
|
165
|
+
#:
|
|
166
|
+
#: .. versionadded:: 0.50.0
|
|
167
|
+
global_rf_to_atr20_factor: float = 0.0151
|
|
168
|
+
|
|
169
|
+
# ----------------
|
|
170
|
+
# Model parameters
|
|
171
|
+
# ----------------
|
|
172
|
+
|
|
173
|
+
#: Initial wake vortex depth scaling factor.
|
|
174
|
+
#: This factor scales max contrail downward displacement after the wake vortex phase
|
|
175
|
+
#: to set the initial contrail depth.
|
|
176
|
+
#: Denoted :math:`C_{D0}` in eq (14) in :cite:`schumannContrailCirrusPrediction2012`.
|
|
177
|
+
initial_wake_vortex_depth: float = 0.5
|
|
178
|
+
|
|
179
|
+
#: Sedimentation impact factor. Denoted by :math:`f_{T}` in eq. (35) of
|
|
180
|
+
#: :cite:`schumannContrailCirrusPrediction2012`.
|
|
181
|
+
#: Schumann describes this as "an important adjustable parameter", and sets
|
|
182
|
+
#: it to 0.1 in the original publication. In :cite:`schumannAviationinducedCirrusRadiation2013`,
|
|
183
|
+
#: a value of 0.5 is referenced after comparing CoCiP predictions to observations.
|
|
184
|
+
sedimentation_impact_factor: float = 0.5
|
|
185
|
+
|
|
186
|
+
#: Default ``nvpm_ei_n`` value if no data provided and emissions calculations fails.
|
|
187
|
+
default_nvpm_ei_n: float = EmissionsParams.default_nvpm_ei_n
|
|
188
|
+
|
|
189
|
+
#: Parameter denoted by :math:`n` in eq. (39) of :cite:`schumannContrailCirrusPrediction2012`.
|
|
190
|
+
wind_shear_enhancement_exponent: float = 0.5
|
|
191
|
+
|
|
192
|
+
#: Multiply flight black carbon number by enhancement factor.
|
|
193
|
+
#: A value of 1.0 provides no scaling.
|
|
194
|
+
#: Primarily used to support uncertainty estimation.
|
|
195
|
+
nvpm_ei_n_enhancement_factor: float = 1.0
|
|
196
|
+
|
|
197
|
+
#: Lower bound for ``nvpm_ei_n`` to account for ambient aerosol
|
|
198
|
+
#: particles for newer engines, [:math:`kg^{-1}`]. Set to None to disable.
|
|
199
|
+
min_ice_particle_number_nvpm_ei_n: float | None = 1e13
|
|
200
|
+
|
|
201
|
+
#: Upper bound for contrail plume depth, constraining it to realistic values.
|
|
202
|
+
#: CoCiP only uses the ambient conditions at the mid-point of the Gaussian plume,
|
|
203
|
+
#: and the edges could be in subsaturated conditions and sublimate. Important when
|
|
204
|
+
#: :attr:`radiative_heating_effects` is enabled.
|
|
205
|
+
max_depth: float = 1500.0
|
|
206
|
+
|
|
207
|
+
#: Experimental. Improved ice crystal number survival fraction in the wake vortex phase.
|
|
208
|
+
#: Implement :cite:`lottermoserHighResolutionEarlyContrails2025`, who developed a
|
|
209
|
+
#: parametric model that estimates the survival fraction of the contrail ice crystal
|
|
210
|
+
#: number after the wake vortex phase based on the results from large eddy simulations.
|
|
211
|
+
#: This replicates Fig. 4 of :cite:`karcherFormationRadiativeForcing2018`.
|
|
212
|
+
#:
|
|
213
|
+
#: .. versionadded:: 0.50.1
|
|
214
|
+
#: .. versionchanged:: 0.54.7
|
|
215
|
+
unterstrasser_ice_survival_fraction: bool = False
|
|
216
|
+
|
|
217
|
+
#: Experimental. Radiative heating effects on contrail cirrus properties.
|
|
218
|
+
#: Terrestrial and solar radiances warm the contrail ice particles and cause
|
|
219
|
+
#: convective turbulence. This effect is expected to enhance vertical mixing
|
|
220
|
+
#: and reduce the lifetime of contrail cirrus. This parameter is experimental,
|
|
221
|
+
#: and the CoCiP implementation of this parameter may change.
|
|
222
|
+
#:
|
|
223
|
+
#: .. versionadded:: 0.28.9
|
|
224
|
+
radiative_heating_effects: bool = False
|
|
225
|
+
|
|
226
|
+
#: Experimental. Apply the extended K15 model to account for vPM activation.
|
|
227
|
+
#: See the preprint `<https://doi.org/10.5194/egusphere-2025-1717>`_ for details.
|
|
228
|
+
#:
|
|
229
|
+
#: .. versionadded:: 0.55.0
|
|
230
|
+
vpm_activation: bool = False
|
|
231
|
+
|
|
232
|
+
#: Experimental. Radiative effects due to contrail-contrail overlapping
|
|
233
|
+
#: Account for change in local contrail shortwave and longwave radiative forcing
|
|
234
|
+
#: due to contrail-contrail overlapping.
|
|
235
|
+
#:
|
|
236
|
+
#: .. versionadded:: 0.45.0
|
|
237
|
+
contrail_contrail_overlapping: bool = False
|
|
238
|
+
|
|
239
|
+
#: Experimental. Contrail-contrail overlapping altitude interval
|
|
240
|
+
#: If :attr:`contrail_contrail_overlapping` is set to True, contrails will be grouped into
|
|
241
|
+
#: altitude intervals, and all contrails within each altitude interval are treated as one
|
|
242
|
+
#: contrail layer where they do not overlap.
|
|
243
|
+
dz_overlap_m: float = 500.0
|
|
244
|
+
|
|
245
|
+
#: Radius threshold for regime bins, [:math:`\mu m`]
|
|
246
|
+
#: This is the row index label for ``habit_distributions``.
|
|
247
|
+
#: See Table 2 in :cite:`schumannEffectiveRadiusIce2011`.
|
|
248
|
+
radius_threshold_um: np.ndarray = dataclasses.field(default_factory=_radius_threshold_um)
|
|
249
|
+
|
|
250
|
+
#: Particle habit (shape) types.
|
|
251
|
+
#: This is the column index label for ``habit_distributions``.
|
|
252
|
+
#: See Table 2 in :cite:`schumannEffectiveRadiusIce2011`.
|
|
253
|
+
habits: np.ndarray = dataclasses.field(default_factory=_habits)
|
|
254
|
+
|
|
255
|
+
#: Mix of ice particle habits in each radius regime.
|
|
256
|
+
#: Rows indexes are ``radius_threshold_um`` elements.
|
|
257
|
+
#: Columns indexes are ``habits`` particle habit type.
|
|
258
|
+
#: See Table 2 from :cite:`schumannEffectiveRadiusIce2011`.
|
|
259
|
+
habit_distributions: np.ndarray = dataclasses.field(default_factory=_habit_distributions)
|
|
260
|
+
|
|
261
|
+
#: Scale shortwave radiative forcing.
|
|
262
|
+
#: Primarily used to support uncertainty estimation.
|
|
263
|
+
rf_sw_enhancement_factor: float = 1.0
|
|
264
|
+
|
|
265
|
+
#: Scale longwave radiative forcing.
|
|
266
|
+
#: Primarily used to support uncertainty estimation.
|
|
267
|
+
rf_lw_enhancement_factor: float = 1.0
|
|
268
|
+
|
|
269
|
+
# ---------------------------------------
|
|
270
|
+
# Conditions for end of contrail lifetime
|
|
271
|
+
# ---------------------------------------
|
|
272
|
+
|
|
273
|
+
#: Minimum altitude domain in simulation, [:math:`m`]
|
|
274
|
+
#: If set to ``None``, this check is disabled.
|
|
275
|
+
min_altitude_m: float | None = 6000.0
|
|
276
|
+
|
|
277
|
+
#: Maximum altitude domain in simulation, [:math:`m`]
|
|
278
|
+
#: If set to ``None``, this check is disabled.
|
|
279
|
+
max_altitude_m: float | None = 13000.0
|
|
280
|
+
|
|
281
|
+
#: Maximum contrail segment length in simulation to prevent unrealistic values, [:math:`m`].
|
|
282
|
+
max_seg_length_m: float = 40000.0
|
|
283
|
+
|
|
284
|
+
#: Max age of contrail evolution.
|
|
285
|
+
max_age: np.timedelta64 = np.timedelta64(20, "h")
|
|
286
|
+
|
|
287
|
+
#: Minimum contrail optical depth.
|
|
288
|
+
min_tau: float = 1e-6
|
|
289
|
+
|
|
290
|
+
#: Maximum contrail optical depth to prevent unrealistic values.
|
|
291
|
+
max_tau: float = 1e10
|
|
292
|
+
|
|
293
|
+
#: Minimum contrail ice particle number per volume of air.
|
|
294
|
+
min_n_ice_per_m3: float = 1e3
|
|
295
|
+
|
|
296
|
+
#: Maximum contrail ice particle number per volume of air to prevent unrealistic values.
|
|
297
|
+
max_n_ice_per_m3: float = 1e20
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
@dataclasses.dataclass
|
|
301
|
+
class CocipFlightParams(CocipParams):
|
|
302
|
+
"""Flight specific CoCiP parameters."""
|
|
303
|
+
|
|
304
|
+
#: Aircraft performance model
|
|
305
|
+
aircraft_performance: AircraftPerformance | None = None
|
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
"""Parameters for CoCiP uncertainty calculations."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import dataclasses
|
|
6
|
+
import logging
|
|
7
|
+
from typing import Any, ClassVar
|
|
8
|
+
|
|
9
|
+
import numpy as np
|
|
10
|
+
import numpy.typing as npt
|
|
11
|
+
from scipy import stats
|
|
12
|
+
from scipy.stats.distributions import rv_frozen
|
|
13
|
+
|
|
14
|
+
from pycontrails.models.cocip import cocip_params
|
|
15
|
+
from pycontrails.models.cocip.cocip_params import CocipParams
|
|
16
|
+
|
|
17
|
+
logger = logging.getLogger(__name__)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class habit_dirichlet(rv_frozen):
|
|
21
|
+
r"""Custom dirichlet distribution for habit weight distribution uncertainty.
|
|
22
|
+
|
|
23
|
+
Scale the habit distributions by a dirichlet distribution
|
|
24
|
+
with alpha parameter :math:`\alpha_{i} = 0.5 + \text{C} \text{G}_{i}`
|
|
25
|
+
where :math:`\text{G}_{i}` is the approximate habit weight distributions
|
|
26
|
+
defined in :attr:`CocipParams().habit_distributions`.
|
|
27
|
+
|
|
28
|
+
References
|
|
29
|
+
----------
|
|
30
|
+
- Table 2 in :cite:`schumannEffectiveRadiusIce2011`
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
def __init__(self, C: float = 96.0) -> None:
|
|
34
|
+
self.C = C
|
|
35
|
+
|
|
36
|
+
def rvs(self, *args: Any, **kwds: Any) -> npt.NDArray[np.float32]:
|
|
37
|
+
"""Generate sample set of habit distributions.
|
|
38
|
+
|
|
39
|
+
Sampled using dirichlet distribution.
|
|
40
|
+
|
|
41
|
+
Parameters
|
|
42
|
+
----------
|
|
43
|
+
*args : Any
|
|
44
|
+
Used to create a number of habit distributions
|
|
45
|
+
**kwds : Any
|
|
46
|
+
Passed through to :func:`scipy.stats.dirichlet.rvs()`
|
|
47
|
+
|
|
48
|
+
Returns
|
|
49
|
+
-------
|
|
50
|
+
npt.NDArray[np.float32]
|
|
51
|
+
Sampled habit weight distribution with the same shape
|
|
52
|
+
as :attr:`CocipParams().habit_distributions`
|
|
53
|
+
"""
|
|
54
|
+
if args or (kwds.get("size") is not None and kwds["size"] > 1):
|
|
55
|
+
raise ValueError("habit_dirichlet distribution only supports creating one rv at a time")
|
|
56
|
+
|
|
57
|
+
default_distributions = cocip_params._habit_distributions()
|
|
58
|
+
alpha_i = 0.5 + self.C * default_distributions
|
|
59
|
+
|
|
60
|
+
# In the first distribution, we assume all ice particles are droxtals
|
|
61
|
+
# There is no way to quantify the uncertainty in this assumption
|
|
62
|
+
# Consequently, we leave the first distribution in default_distributions
|
|
63
|
+
# alone, and only perturb the rest
|
|
64
|
+
distr_list = [stats.dirichlet(a) for a in alpha_i[1:]]
|
|
65
|
+
|
|
66
|
+
habit_weights = default_distributions.copy()
|
|
67
|
+
for i, distr in enumerate(distr_list, start=1):
|
|
68
|
+
habit_weights[i] = distr.rvs(**kwds)
|
|
69
|
+
|
|
70
|
+
return habit_weights
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
@dataclasses.dataclass
|
|
74
|
+
class CocipUncertaintyParams(CocipParams):
|
|
75
|
+
"""Model parameters for CoCiP epistemic uncertainty.
|
|
76
|
+
|
|
77
|
+
Any uncertainty parameters should end with the suffix `uncertainty`. See `__post_init__`.
|
|
78
|
+
|
|
79
|
+
Uncertainty parameters take a `scipy.stats.distributions.rv_frozen` distribution.
|
|
80
|
+
|
|
81
|
+
Default distributions have mean at CocipParam default values.
|
|
82
|
+
|
|
83
|
+
To retain specific parameters as defaults, set the value of the key to None.
|
|
84
|
+
|
|
85
|
+
Examples
|
|
86
|
+
--------
|
|
87
|
+
>>> import scipy.stats
|
|
88
|
+
>>> from pycontrails.models.cocip import CocipUncertaintyParams
|
|
89
|
+
|
|
90
|
+
>>> # Override the 'initial_wake_vortex_depth' field from
|
|
91
|
+
>>> # CocipParams with a uniform value in [0.4, 0.6]
|
|
92
|
+
>>> distr = scipy.stats.uniform(loc=0.4, scale=0.2)
|
|
93
|
+
>>> params = CocipUncertaintyParams(seed=123, initial_wake_vortex_depth_uncertainty=distr)
|
|
94
|
+
>>> params.initial_wake_vortex_depth
|
|
95
|
+
np.float64(0.41076420...)
|
|
96
|
+
|
|
97
|
+
>>> # Once seeded, calling the class again gives a new value
|
|
98
|
+
>>> params = CocipUncertaintyParams(initial_wake_vortex_depth=distr)
|
|
99
|
+
>>> params.initial_wake_vortex_depth
|
|
100
|
+
np.float64(0.43526372...)
|
|
101
|
+
|
|
102
|
+
>>> # To retain the default value, set the uncertainty to None
|
|
103
|
+
>>> params = CocipUncertaintyParams(rf_lw_enhancement_factor_uncertainty=None)
|
|
104
|
+
>>> params.rf_lw_enhancement_factor
|
|
105
|
+
1.0
|
|
106
|
+
"""
|
|
107
|
+
|
|
108
|
+
#: The random number generator is attached to the class (as opposed to an instance).
|
|
109
|
+
#: If many instances of :class:`CocipUncertaintyParams` are needed, it is best to seed
|
|
110
|
+
#: the random number generator once initially. Reseeding the generator will also
|
|
111
|
+
#: reset it, giving rise to duplicate random numbers.
|
|
112
|
+
rng: ClassVar[np.random.Generator] = np.random.default_rng(None)
|
|
113
|
+
|
|
114
|
+
#: Reseed the random generator defined in ``__post_init__``
|
|
115
|
+
seed: int | None = None
|
|
116
|
+
|
|
117
|
+
#: Schumann takes ``wind_shear_enhancement_exponent`` = 0.5 and discusses the case of 0 and 2/3
|
|
118
|
+
#: as possibilities.
|
|
119
|
+
#: With a value of 0, wind shear is not enhanced.
|
|
120
|
+
wind_shear_enhancement_exponent_uncertainty: rv_frozen | None = dataclasses.field(
|
|
121
|
+
default_factory=lambda: stats.triang(
|
|
122
|
+
loc=0.0, c=CocipParams.wind_shear_enhancement_exponent, scale=1.0
|
|
123
|
+
)
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
#: Schumann takes ``initial_wake_vortex_depth`` = 0.5 and discusses some
|
|
127
|
+
#: uncertainty in this value. This parameter should be non-negative.
|
|
128
|
+
initial_wake_vortex_depth_uncertainty: rv_frozen | None = dataclasses.field(
|
|
129
|
+
default_factory=lambda: stats.triang(
|
|
130
|
+
loc=0.3, c=CocipParams.initial_wake_vortex_depth, scale=0.4
|
|
131
|
+
)
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
#: Schumann takes a default value of 0.1 and describes it as an "important adjustable parameter"
|
|
135
|
+
#: Currently, `CocipParams` uses a default value of 0.5
|
|
136
|
+
sedimentation_impact_factor_uncertainty: rv_frozen | None = dataclasses.field(
|
|
137
|
+
default_factory=lambda: stats.norm(loc=CocipParams.sedimentation_impact_factor, scale=0.1)
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
#: Teoh 2022 (to appear) takes values between 70% decrease and 100% increase.
|
|
141
|
+
#: This coincides with the log normal distribution defined below.
|
|
142
|
+
nvpm_ei_n_enhancement_factor_uncertainty: rv_frozen | None = dataclasses.field(
|
|
143
|
+
default_factory=lambda: stats.lognorm(s=0.15, scale=1 / stats.lognorm(s=0.15).mean())
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
#: Scale shortwave radiative forcing.
|
|
147
|
+
#: Table 2 in :cite:`schumannParametricRadiativeForcing2012`
|
|
148
|
+
#: provides relative RMS error for SW/LW fit to the data generated
|
|
149
|
+
#: by `libRadTran <http://www.libradtran.org/doku.php>`_.
|
|
150
|
+
#: We use the average RMS error across all habit types (pg 1397) as the standard deviation
|
|
151
|
+
#: of a normally distributed scaling factor for SW forcing
|
|
152
|
+
rf_sw_enhancement_factor_uncertainty: rv_frozen | None = dataclasses.field(
|
|
153
|
+
default_factory=lambda: stats.norm(loc=CocipParams.rf_sw_enhancement_factor, scale=0.106)
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
#: Scale longwave radiative forcing.
|
|
157
|
+
#: Table 2 in :cite:`schumannParametricRadiativeForcing2012` provides relative error for SW/LW
|
|
158
|
+
#: fit to the data generated by `libRadTran <http://www.libradtran.org/doku.php>`_.
|
|
159
|
+
#: We use the average RMS error across all habit types (pg 1397) as the standard deviation
|
|
160
|
+
#: of a normally distributed scaling factor for LW forcing.
|
|
161
|
+
rf_lw_enhancement_factor_uncertainty: rv_frozen | None = dataclasses.field(
|
|
162
|
+
default_factory=lambda: stats.norm(loc=CocipParams.rf_lw_enhancement_factor, scale=0.071)
|
|
163
|
+
)
|
|
164
|
+
|
|
165
|
+
#: Scale the habit distributions by a dirichlet distribution
|
|
166
|
+
#: with alpha parameter :math:`\alpha_{i} = 0.5 + \text{C} \text{G}_{i}`
|
|
167
|
+
#: where :math:`\text{G}_{i}` is the approximate habit weight distributions
|
|
168
|
+
#: defined in :attr:`CocipParams().habit_distributions`.
|
|
169
|
+
#: Higher values of :math:`\text{C}` correspond to higher confidence in initial estimates.
|
|
170
|
+
habit_distributions_uncertainty: rv_frozen | None = dataclasses.field(
|
|
171
|
+
default_factory=habit_dirichlet
|
|
172
|
+
)
|
|
173
|
+
|
|
174
|
+
def __post_init__(self) -> None:
|
|
175
|
+
"""Override values of model parameters according to ranges."""
|
|
176
|
+
if self.seed is not None:
|
|
177
|
+
# Reset the class variable `rng`
|
|
178
|
+
logger.info("Reset %s random seed to %s", self.__class__.__name__, self.seed)
|
|
179
|
+
self.__class__.rng = np.random.default_rng(self.seed)
|
|
180
|
+
|
|
181
|
+
# Override defaults value on `CocipParams` with random parameters
|
|
182
|
+
for param, value in self.rvs().items():
|
|
183
|
+
setattr(self, param, value)
|
|
184
|
+
|
|
185
|
+
@property
|
|
186
|
+
def uncertainty_params(self) -> dict[str, rv_frozen]:
|
|
187
|
+
"""Get dictionary of uncertainty parameters.
|
|
188
|
+
|
|
189
|
+
Method checks for attributes ending in `"_uncertainty"`.
|
|
190
|
+
|
|
191
|
+
Returns
|
|
192
|
+
-------
|
|
193
|
+
dict[str, rv_frozen]
|
|
194
|
+
Uncertainty parameters and values
|
|
195
|
+
"""
|
|
196
|
+
# handle these differently starting in version 0.27.0
|
|
197
|
+
exclude = {"rhi_adj", "rhi_boost_exponent"}
|
|
198
|
+
|
|
199
|
+
out = {}
|
|
200
|
+
|
|
201
|
+
param_dict = dataclasses.asdict(self)
|
|
202
|
+
for uncertainty_param, dist in param_dict.items():
|
|
203
|
+
if uncertainty_param.endswith("_uncertainty") and dist is not None:
|
|
204
|
+
param = uncertainty_param.split("_uncertainty")[0]
|
|
205
|
+
if param not in exclude and param not in param_dict:
|
|
206
|
+
raise AttributeError(
|
|
207
|
+
f"Parameter {param} corresponding to uncertainty parameter "
|
|
208
|
+
f"{uncertainty_param} does not exist"
|
|
209
|
+
)
|
|
210
|
+
|
|
211
|
+
if not isinstance(dist, rv_frozen):
|
|
212
|
+
raise AttributeError(
|
|
213
|
+
f"Uncertainty parameter '{uncertainty_param}' must be instance of "
|
|
214
|
+
"'scipy.stats.distributions.rv_frozen'"
|
|
215
|
+
)
|
|
216
|
+
|
|
217
|
+
out[param] = dist
|
|
218
|
+
|
|
219
|
+
return out
|
|
220
|
+
|
|
221
|
+
def rvs(self, size: None | int = None) -> dict[str, np.float64 | npt.NDArray[np.floating]]:
|
|
222
|
+
"""Call each distribution's `rvs` method to generate random parameters.
|
|
223
|
+
|
|
224
|
+
Seed calls to `rvs` with class variable `rng`.
|
|
225
|
+
|
|
226
|
+
Parameters
|
|
227
|
+
----------
|
|
228
|
+
size : None | int, optional
|
|
229
|
+
If specified, an `array` of values is generated for each uncertainty parameter.
|
|
230
|
+
|
|
231
|
+
Returns
|
|
232
|
+
-------
|
|
233
|
+
dict[str, np.float64 | npt.NDArray[np.floating]]
|
|
234
|
+
Dictionary of random parameters. Dictionary keys consists of names of parameters in
|
|
235
|
+
`CocipParams` to be overridden by random value.
|
|
236
|
+
|
|
237
|
+
Examples
|
|
238
|
+
--------
|
|
239
|
+
>>> from pprint import pprint
|
|
240
|
+
>>> from pycontrails.models.cocip import CocipUncertaintyParams
|
|
241
|
+
>>> params = CocipUncertaintyParams(seed=456)
|
|
242
|
+
>>> pprint(params.rvs())
|
|
243
|
+
{'habit_distributions': array([[0.0000000e+00, 0.0000000e+00, 0.0000000e+00, 0.0000000e+00,
|
|
244
|
+
0.0000000e+00, 0.0000000e+00, 1.0000000e+00, 0.0000000e+00],
|
|
245
|
+
[1.5554131e-02, 2.1363135e-01, 7.7715185e-03, 1.7690966e-02,
|
|
246
|
+
3.1576434e-03, 3.2992734e-06, 7.4009895e-01, 2.0921326e-03],
|
|
247
|
+
[3.8193921e-03, 2.1235342e-01, 3.3554080e-04, 5.2846869e-04,
|
|
248
|
+
3.1945917e-01, 4.8709914e-04, 4.6250960e-01, 5.0730183e-04],
|
|
249
|
+
[5.7327619e-04, 4.7781631e-01, 4.2596990e-03, 6.7235163e-04,
|
|
250
|
+
1.4447135e-01, 3.6184600e-01, 1.0150939e-02, 2.1006212e-04],
|
|
251
|
+
[1.5397545e-02, 4.0522218e-01, 4.2781001e-01, 1.4331797e-01,
|
|
252
|
+
7.1088417e-04, 9.4511814e-04, 3.3900745e-03, 3.2062260e-03],
|
|
253
|
+
[7.9063961e-04, 3.0336906e-03, 7.7571563e-04, 2.0577813e-02,
|
|
254
|
+
9.4205803e-01, 4.3379897e-03, 3.6786550e-03, 2.4747452e-02]],
|
|
255
|
+
dtype=float32),
|
|
256
|
+
'initial_wake_vortex_depth': np.float64(0.39805019708566847),
|
|
257
|
+
'nvpm_ei_n_enhancement_factor': np.float64(0.9371878437312526),
|
|
258
|
+
'rf_lw_enhancement_factor': np.float64(1.1017491252832377),
|
|
259
|
+
'rf_sw_enhancement_factor': np.float64(0.99721639115012),
|
|
260
|
+
'sedimentation_impact_factor': np.float64(0.5071779847244678),
|
|
261
|
+
'wind_shear_enhancement_exponent': np.float64(0.34100931239701004)}
|
|
262
|
+
"""
|
|
263
|
+
return {
|
|
264
|
+
param: distr.rvs(size=size, random_state=self.rng)
|
|
265
|
+
for param, distr in self.uncertainty_params.items()
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
def as_dict(self) -> dict[str, Any]:
|
|
269
|
+
"""Convert object to dictionary.
|
|
270
|
+
|
|
271
|
+
Wrapper around :meth:`ModelBase.as_dict` that removes
|
|
272
|
+
uncertainty specific parameters.
|
|
273
|
+
|
|
274
|
+
Returns
|
|
275
|
+
-------
|
|
276
|
+
dict[str, Any]
|
|
277
|
+
Dictionary version of self.
|
|
278
|
+
"""
|
|
279
|
+
obj = super().as_dict()
|
|
280
|
+
|
|
281
|
+
# remove seed and _uncertainty attributes
|
|
282
|
+
# these will throw an error in `ModelBase._load_params()`
|
|
283
|
+
keys_to_remove = ["seed"]
|
|
284
|
+
for key in obj:
|
|
285
|
+
if key.endswith("_uncertainty"):
|
|
286
|
+
keys_to_remove.append(key)
|
|
287
|
+
|
|
288
|
+
for key in keys_to_remove:
|
|
289
|
+
obj.pop(key, None)
|
|
290
|
+
|
|
291
|
+
return obj
|