pycontrails 0.59.0__cp314-cp314-macosx_10_15_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 +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 +2936 -0
- pycontrails/core/met_var.py +387 -0
- pycontrails/core/models.py +1321 -0
- pycontrails/core/polygon.py +549 -0
- pycontrails/core/rgi_cython.cpython-314-darwin.so +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 +764 -0
- pycontrails/datalib/gruan.py +343 -0
- pycontrails/datalib/himawari/__init__.py +27 -0
- pycontrails/datalib/himawari/header_struct.py +266 -0
- pycontrails/datalib/himawari/himawari.py +671 -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.59.0.dist-info/METADATA +179 -0
- pycontrails-0.59.0.dist-info/RECORD +123 -0
- pycontrails-0.59.0.dist-info/WHEEL +6 -0
- pycontrails-0.59.0.dist-info/licenses/LICENSE +178 -0
- pycontrails-0.59.0.dist-info/licenses/NOTICE +43 -0
- pycontrails-0.59.0.dist-info/top_level.txt +3 -0
|
@@ -0,0 +1,599 @@
|
|
|
1
|
+
"""Non-volatile particulate matter (nvPM) calculations."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import numpy as np
|
|
6
|
+
import numpy.typing as npt
|
|
7
|
+
|
|
8
|
+
from pycontrails.physics import constants, jet, units
|
|
9
|
+
from pycontrails.utils.types import ArrayScalarLike
|
|
10
|
+
|
|
11
|
+
# ---------------------------------------------------------
|
|
12
|
+
# nvPM Mass Emissions Index: Formation and Oxidation Method
|
|
13
|
+
# ---------------------------------------------------------
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def mass_emissions_index_fox(
|
|
17
|
+
air_pressure: npt.NDArray[np.floating],
|
|
18
|
+
air_temperature: npt.NDArray[np.floating],
|
|
19
|
+
true_airspeed: npt.NDArray[np.floating],
|
|
20
|
+
fuel_flow_per_engine: npt.NDArray[np.floating],
|
|
21
|
+
thrust_setting: npt.NDArray[np.floating],
|
|
22
|
+
pressure_ratio: float,
|
|
23
|
+
*,
|
|
24
|
+
comp_efficiency: float = 0.9,
|
|
25
|
+
) -> npt.NDArray[np.floating]:
|
|
26
|
+
r"""
|
|
27
|
+
Calculate the black carbon mass emissions index using the Formation and Oxidation Method (FOX).
|
|
28
|
+
|
|
29
|
+
Parameters
|
|
30
|
+
----------
|
|
31
|
+
air_pressure: npt.NDArray[np.floating]
|
|
32
|
+
Pressure altitude at each waypoint, [:math:`Pa`]
|
|
33
|
+
air_temperature: npt.NDArray[np.floating]
|
|
34
|
+
Ambient temperature for each waypoint, [:math:`K`]
|
|
35
|
+
true_airspeed: npt.NDArray[np.floating]
|
|
36
|
+
True airspeed for each waypoint, [:math:`m s^{-1}`]
|
|
37
|
+
fuel_flow_per_engine: npt.NDArray[np.floating]
|
|
38
|
+
Fuel mass flow rate per engine, [:math:`kg s^{-1}`]
|
|
39
|
+
thrust_setting: npt.NDArray[np.floating]
|
|
40
|
+
Engine thrust setting, which is the fuel mass flow rate divided by
|
|
41
|
+
the maximum fuel mass flow rate
|
|
42
|
+
pressure_ratio: float
|
|
43
|
+
Engine pressure ratio from the ICAO EDB
|
|
44
|
+
comp_efficiency: float
|
|
45
|
+
Engine compressor efficiency, assumed to be 0.9
|
|
46
|
+
|
|
47
|
+
Returns
|
|
48
|
+
-------
|
|
49
|
+
npt.NDArray[np.floating]
|
|
50
|
+
Black carbon mass emissions index, [:math:`mg \ kg_{fuel}^{-1}`]
|
|
51
|
+
|
|
52
|
+
References
|
|
53
|
+
----------
|
|
54
|
+
- :cite:`stettlerGlobalCivilAviation2013`
|
|
55
|
+
"""
|
|
56
|
+
# Reference conditions: 100% thrust setting
|
|
57
|
+
p_3_ref = jet.combustor_inlet_pressure(pressure_ratio, constants.p_surface, 1.0)
|
|
58
|
+
t_3_ref = jet.combustor_inlet_temperature(
|
|
59
|
+
comp_efficiency, constants.T_msl, constants.p_surface, p_3_ref
|
|
60
|
+
)
|
|
61
|
+
t_fl_ref = flame_temperature(t_3_ref)
|
|
62
|
+
afr_ref = jet.air_to_fuel_ratio(1.0, cruise=False)
|
|
63
|
+
fuel_flow_max = fuel_flow_per_engine / thrust_setting
|
|
64
|
+
c_bc_ref = bc_mass_concentration_fox(fuel_flow_max, t_fl_ref, afr_ref)
|
|
65
|
+
|
|
66
|
+
# Cruise conditions
|
|
67
|
+
mach_num = units.tas_to_mach_number(true_airspeed, air_temperature)
|
|
68
|
+
t_2_cru = jet.compressor_inlet_temperature(air_temperature, mach_num)
|
|
69
|
+
p_2_cru = jet.compressor_inlet_pressure(air_pressure, mach_num)
|
|
70
|
+
p_3_cru = jet.combustor_inlet_pressure(pressure_ratio, p_2_cru, thrust_setting)
|
|
71
|
+
t_3_cru = jet.combustor_inlet_temperature(comp_efficiency, t_2_cru, p_2_cru, p_3_cru)
|
|
72
|
+
t_fl_cru = flame_temperature(t_3_cru)
|
|
73
|
+
afr_cru = jet.air_to_fuel_ratio(thrust_setting, cruise=True, T_compressor_inlet=t_2_cru)
|
|
74
|
+
c_bc_cru = bc_mass_concentration_cruise_fox(
|
|
75
|
+
c_bc_ref=c_bc_ref,
|
|
76
|
+
t_fl_cru=t_fl_cru,
|
|
77
|
+
t_fl_ref=t_fl_ref,
|
|
78
|
+
p_3_cru=p_3_cru,
|
|
79
|
+
p_3_ref=p_3_ref,
|
|
80
|
+
afr_cru=afr_cru,
|
|
81
|
+
afr_ref=afr_ref,
|
|
82
|
+
)
|
|
83
|
+
q_exhaust_cru = exhaust_gas_volume_per_kg_fuel(afr_cru)
|
|
84
|
+
return bc_mass_emissions_index(c_bc_cru, q_exhaust_cru)
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def flame_temperature(t_3: ArrayScalarLike) -> ArrayScalarLike:
|
|
88
|
+
"""
|
|
89
|
+
Calculate the flame temperature at the combustion chamber (t_fl).
|
|
90
|
+
|
|
91
|
+
Parameters
|
|
92
|
+
----------
|
|
93
|
+
t_3: ArrayScalarLike
|
|
94
|
+
Combustor inlet temperature, [:math:`K`]
|
|
95
|
+
|
|
96
|
+
Returns
|
|
97
|
+
-------
|
|
98
|
+
ArrayScalarLike
|
|
99
|
+
Flame temperature at the combustion chamber, [:math:`K`]
|
|
100
|
+
"""
|
|
101
|
+
return 0.9 * t_3 + 2120.0
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def bc_mass_concentration_fox(
|
|
105
|
+
fuel_flow: npt.NDArray[np.floating],
|
|
106
|
+
t_fl: npt.NDArray[np.floating] | float,
|
|
107
|
+
afr: npt.NDArray[np.floating] | float,
|
|
108
|
+
) -> npt.NDArray[np.floating]:
|
|
109
|
+
"""Calculate the black carbon mass concentration for ground conditions (``c_bc_ref``).
|
|
110
|
+
|
|
111
|
+
This quantity is computed at the instrument sampling point without correcting
|
|
112
|
+
for particle line losses.
|
|
113
|
+
|
|
114
|
+
Parameters
|
|
115
|
+
----------
|
|
116
|
+
fuel_flow: npt.NDArray[np.floating]
|
|
117
|
+
Fuel mass flow rate, [:math:`kg s^{-1}`]
|
|
118
|
+
t_fl: npt.NDArray[np.floating] | float
|
|
119
|
+
Flame temperature at the combustion chamber, [:math:`K`]
|
|
120
|
+
afr: npt.NDArray[np.floating] | float
|
|
121
|
+
Air-to-fuel ratio
|
|
122
|
+
|
|
123
|
+
Returns
|
|
124
|
+
-------
|
|
125
|
+
npt.NDArray[np.floating]:
|
|
126
|
+
Black carbon mass concentration for ground conditions, [:math:`mg m^{-3}`]
|
|
127
|
+
"""
|
|
128
|
+
# avoid float32 -> float64 promotion
|
|
129
|
+
coeff = 356.0 * np.exp(np.float32(-6390.0) / t_fl) - 608.0 * afr * np.exp(
|
|
130
|
+
np.float32(-19778.0) / t_fl
|
|
131
|
+
)
|
|
132
|
+
return fuel_flow * coeff
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def bc_mass_concentration_cruise_fox(
|
|
136
|
+
c_bc_ref: npt.NDArray[np.floating],
|
|
137
|
+
t_fl_cru: npt.NDArray[np.floating],
|
|
138
|
+
t_fl_ref: npt.NDArray[np.floating] | float,
|
|
139
|
+
p_3_cru: npt.NDArray[np.floating],
|
|
140
|
+
p_3_ref: npt.NDArray[np.floating] | float,
|
|
141
|
+
afr_cru: npt.NDArray[np.floating],
|
|
142
|
+
afr_ref: npt.NDArray[np.floating] | float,
|
|
143
|
+
) -> npt.NDArray[np.floating]:
|
|
144
|
+
"""Calculate the black carbon mass concentration for cruise conditions (``c_bc_cru``).
|
|
145
|
+
|
|
146
|
+
This quantity is computed at the instrument sampling point without correcting
|
|
147
|
+
for particle line losses.
|
|
148
|
+
|
|
149
|
+
Parameters
|
|
150
|
+
----------
|
|
151
|
+
c_bc_ref: npt.NDArray[np.floating]
|
|
152
|
+
Black carbon mass concentration at reference conditions, [:math:`mg m^{-3}`]
|
|
153
|
+
t_fl_cru: npt.NDArray[np.floating]
|
|
154
|
+
Flame temperature at cruise conditions, [:math:`K`]
|
|
155
|
+
t_fl_ref: npt.NDArray[np.floating] | float
|
|
156
|
+
Flame temperature at reference conditions, [:math:`K`]
|
|
157
|
+
p_3_cru: npt.NDArray[np.floating]
|
|
158
|
+
Combustor inlet pressure at cruise conditions, [:math:`Pa`]
|
|
159
|
+
p_3_ref: npt.NDArray[np.floating] | float
|
|
160
|
+
Combustor inlet pressure at reference conditions, [:math:`Pa`]
|
|
161
|
+
afr_cru: npt.NDArray[np.floating]
|
|
162
|
+
Air-to-fuel ratio at cruise conditions
|
|
163
|
+
afr_ref: npt.NDArray[np.floating] | float
|
|
164
|
+
Air-to-fuel ratio at reference conditions
|
|
165
|
+
|
|
166
|
+
Returns
|
|
167
|
+
-------
|
|
168
|
+
npt.NDArray[np.floating]:
|
|
169
|
+
Black carbon mass concentration for cruise conditions, [:math:`mg m^{-3}`]
|
|
170
|
+
"""
|
|
171
|
+
scaling_factor = dopelheuer_lecht_scaling_factor(
|
|
172
|
+
t_fl_cru=t_fl_cru,
|
|
173
|
+
t_fl_ref=t_fl_ref,
|
|
174
|
+
p_3_cru=p_3_cru,
|
|
175
|
+
p_3_ref=p_3_ref,
|
|
176
|
+
afr_cru=afr_cru,
|
|
177
|
+
afr_ref=afr_ref,
|
|
178
|
+
)
|
|
179
|
+
return c_bc_ref * scaling_factor
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
def dopelheuer_lecht_scaling_factor(
|
|
183
|
+
t_fl_cru: npt.NDArray[np.floating],
|
|
184
|
+
t_fl_ref: npt.NDArray[np.floating] | float,
|
|
185
|
+
p_3_cru: npt.NDArray[np.floating],
|
|
186
|
+
p_3_ref: npt.NDArray[np.floating] | float,
|
|
187
|
+
afr_cru: npt.NDArray[np.floating],
|
|
188
|
+
afr_ref: npt.NDArray[np.floating] | float,
|
|
189
|
+
) -> npt.NDArray[np.floating]:
|
|
190
|
+
"""Estimate scaling factor to convert the reference BC mass concentration from ground to cruise.
|
|
191
|
+
|
|
192
|
+
Parameters
|
|
193
|
+
----------
|
|
194
|
+
t_fl_cru: npt.NDArray[np.floating]
|
|
195
|
+
Flame temperature at cruise conditions, [:math:`K`]
|
|
196
|
+
t_fl_ref: npt.NDArray[np.floating] | float
|
|
197
|
+
Flame temperature at reference conditions, [:math:`K`]
|
|
198
|
+
p_3_cru: npt.NDArray[np.floating]
|
|
199
|
+
Combustor inlet pressure at cruise conditions, [:math:`Pa`]
|
|
200
|
+
p_3_ref: npt.NDArray[np.floating] | float
|
|
201
|
+
Combustor inlet pressure at reference conditions, [:math:`Pa`]
|
|
202
|
+
afr_cru: npt.NDArray[np.floating]
|
|
203
|
+
Air-to-fuel ratio at cruise conditions
|
|
204
|
+
afr_ref: npt.NDArray[np.floating] | float
|
|
205
|
+
Air-to-fuel ratio at reference conditions
|
|
206
|
+
|
|
207
|
+
Returns
|
|
208
|
+
-------
|
|
209
|
+
npt.NDArray[np.floating]
|
|
210
|
+
Dopelheuer & Lecht scaling factor
|
|
211
|
+
|
|
212
|
+
References
|
|
213
|
+
----------
|
|
214
|
+
- :cite:`dopelheuerInfluenceEnginePerformance1998`
|
|
215
|
+
"""
|
|
216
|
+
exp_term = np.exp(20000.0 / t_fl_cru - 20000.0 / t_fl_ref)
|
|
217
|
+
return (afr_ref / afr_cru) ** 2.5 * (p_3_cru / p_3_ref) ** 1.35 * exp_term
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
# ----------------------------------------------------------------------------
|
|
221
|
+
# nvPM Mass Emissions Index: "Improved" Formation and Oxidation Method (ImFOX)
|
|
222
|
+
# ----------------------------------------------------------------------------
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
def mass_emissions_index_imfox(
|
|
226
|
+
fuel_flow_per_engine: npt.NDArray[np.floating],
|
|
227
|
+
thrust_setting: npt.NDArray[np.floating],
|
|
228
|
+
fuel_hydrogen: float,
|
|
229
|
+
) -> npt.NDArray[np.floating]:
|
|
230
|
+
r"""Calculate the BC mass EI using the "Improved" Formation and Oxidation Method (ImFOX).
|
|
231
|
+
|
|
232
|
+
Parameters
|
|
233
|
+
----------
|
|
234
|
+
fuel_flow_per_engine: npt.NDArray[np.floating]
|
|
235
|
+
Fuel mass flow rate per engine, [:math:`kg s^{-1}`]
|
|
236
|
+
thrust_setting: npt.NDArray[np.floating]
|
|
237
|
+
Engine thrust setting, which is the fuel mass flow rate divided by the
|
|
238
|
+
maximum fuel mass flow rate
|
|
239
|
+
fuel_hydrogen: float
|
|
240
|
+
Percentage of hydrogen mass content in the fuel (13.8% for conventional Jet A-1 fuel)
|
|
241
|
+
|
|
242
|
+
Returns
|
|
243
|
+
-------
|
|
244
|
+
npt.NDArray[np.floating]
|
|
245
|
+
Black carbon mass emissions index, [:math:`mg \ kg_{fuel}^{-1}`]
|
|
246
|
+
|
|
247
|
+
References
|
|
248
|
+
----------
|
|
249
|
+
- :cite:`abrahamsonPredictiveModelDevelopment2016`
|
|
250
|
+
"""
|
|
251
|
+
afr_cru = air_to_fuel_ratio_imfox(thrust_setting)
|
|
252
|
+
t_4_cru = turbine_inlet_temperature_imfox(afr_cru)
|
|
253
|
+
c_bc_cru = bc_mass_concentration_imfox(
|
|
254
|
+
fuel_flow_per_engine, afr_cru, t_4_cru, fuel_hydrogen=fuel_hydrogen
|
|
255
|
+
)
|
|
256
|
+
q_exhaust_cru = exhaust_gas_volume_per_kg_fuel(afr_cru)
|
|
257
|
+
return bc_mass_emissions_index(c_bc_cru, q_exhaust_cru)
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
def air_to_fuel_ratio_imfox(thrust_setting: npt.NDArray[np.floating]) -> npt.NDArray[np.floating]:
|
|
261
|
+
"""Calculate the air-to-fuel ratio at cruise conditions via Abrahamson's method.
|
|
262
|
+
|
|
263
|
+
See Eq. (11) in :cite:`abrahamsonPredictiveModelDevelopment2016`.
|
|
264
|
+
|
|
265
|
+
Parameters
|
|
266
|
+
----------
|
|
267
|
+
thrust_setting: npt.NDArray[np.floating]
|
|
268
|
+
Engine thrust setting, which is the fuel mass flow rate divided by
|
|
269
|
+
the maximum fuel mass flow rate
|
|
270
|
+
|
|
271
|
+
Returns
|
|
272
|
+
-------
|
|
273
|
+
npt.NDArray[np.floating]
|
|
274
|
+
Air-to-fuel ratio at cruise conditions
|
|
275
|
+
|
|
276
|
+
References
|
|
277
|
+
----------
|
|
278
|
+
- :cite:`abrahamsonPredictiveModelDevelopment2016`
|
|
279
|
+
"""
|
|
280
|
+
return 55.4 - 30.8 * thrust_setting
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
def turbine_inlet_temperature_imfox(afr: npt.NDArray[np.floating]) -> npt.NDArray[np.floating]:
|
|
284
|
+
"""Calculate the turbine inlet temperature using Abrahamson's method.
|
|
285
|
+
|
|
286
|
+
See Eq. (13) in :cite:`abrahamsonPredictiveModelDevelopment2016`.
|
|
287
|
+
|
|
288
|
+
Parameters
|
|
289
|
+
----------
|
|
290
|
+
afr: npt.NDArray[np.floating]
|
|
291
|
+
air-to-fuel ratio at cruise conditions
|
|
292
|
+
|
|
293
|
+
Returns
|
|
294
|
+
-------
|
|
295
|
+
npt.NDArray[np.floating]
|
|
296
|
+
turbine inlet temperature, [:math:`K`]
|
|
297
|
+
|
|
298
|
+
References
|
|
299
|
+
----------
|
|
300
|
+
- :cite:`abrahamsonPredictiveModelDevelopment2016`
|
|
301
|
+
"""
|
|
302
|
+
return 490.0 + 42266.0 / afr
|
|
303
|
+
|
|
304
|
+
|
|
305
|
+
def bc_mass_concentration_imfox(
|
|
306
|
+
fuel_flow_per_engine: npt.NDArray[np.floating],
|
|
307
|
+
afr: npt.NDArray[np.floating],
|
|
308
|
+
t_4: npt.NDArray[np.floating],
|
|
309
|
+
fuel_hydrogen: float,
|
|
310
|
+
) -> npt.NDArray[np.floating]:
|
|
311
|
+
"""Calculate the BC mass concentration for ground and cruise conditions with ImFOX methodology.
|
|
312
|
+
|
|
313
|
+
This quantity is computed at the instrument sampling point without
|
|
314
|
+
correcting for particle line losses.
|
|
315
|
+
|
|
316
|
+
Parameters
|
|
317
|
+
----------
|
|
318
|
+
fuel_flow_per_engine: npt.NDArray[np.floating]
|
|
319
|
+
fuel mass flow rate per engine, [:math:`kg s^{-1}`]
|
|
320
|
+
afr: npt.NDArray[np.floating]
|
|
321
|
+
air-to-fuel ratio
|
|
322
|
+
t_4: npt.NDArray[np.floating]
|
|
323
|
+
turbine inlet temperature, [:math:`K`]
|
|
324
|
+
fuel_hydrogen: float
|
|
325
|
+
percentage of hydrogen mass content in the fuel (13.8% for conventional Jet A-1 fuel)
|
|
326
|
+
|
|
327
|
+
Returns
|
|
328
|
+
-------
|
|
329
|
+
npt.NDArray[np.floating]
|
|
330
|
+
Black carbon mass concentration, [:math:`mg m^{-3}`]
|
|
331
|
+
"""
|
|
332
|
+
# avoid float32 -> float64 promotion
|
|
333
|
+
exp_term = np.exp(np.float32(13.6) - fuel_hydrogen)
|
|
334
|
+
formation_term = 295.0 * np.exp(np.float32(-6390.0) / t_4)
|
|
335
|
+
oxidation_term = 608.0 * afr * np.exp(np.float32(-19778.0) / t_4)
|
|
336
|
+
return fuel_flow_per_engine * exp_term * (formation_term - oxidation_term)
|
|
337
|
+
|
|
338
|
+
|
|
339
|
+
# ---------------------------------------------------------
|
|
340
|
+
# Commonly Used Black Carbon Mass Emissions Index Functions
|
|
341
|
+
# ---------------------------------------------------------
|
|
342
|
+
|
|
343
|
+
|
|
344
|
+
def exhaust_gas_volume_per_kg_fuel(afr: npt.NDArray[np.floating]) -> npt.NDArray[np.floating]:
|
|
345
|
+
"""
|
|
346
|
+
Calculate the volume of exhaust gas per mass of fuel burnt.
|
|
347
|
+
|
|
348
|
+
Parameters
|
|
349
|
+
----------
|
|
350
|
+
afr: npt.NDArray[np.floating]
|
|
351
|
+
Air-to-fuel ratio
|
|
352
|
+
|
|
353
|
+
Returns
|
|
354
|
+
-------
|
|
355
|
+
npt.NDArray[np.floating]
|
|
356
|
+
Volume of exhaust gas per mass of fuel burnt, [:math:`m^{3}/kg_{fuel}`]
|
|
357
|
+
|
|
358
|
+
References
|
|
359
|
+
----------
|
|
360
|
+
- :cite:`stettlerGlobalCivilAviation2013`
|
|
361
|
+
"""
|
|
362
|
+
return 0.776 * afr + 0.877
|
|
363
|
+
|
|
364
|
+
|
|
365
|
+
def bc_mass_emissions_index(
|
|
366
|
+
c_bc: npt.NDArray[np.floating], q_exhaust: npt.NDArray[np.floating]
|
|
367
|
+
) -> npt.NDArray[np.floating]:
|
|
368
|
+
"""
|
|
369
|
+
Calculate the black carbon mass emissions index.
|
|
370
|
+
|
|
371
|
+
Parameters
|
|
372
|
+
----------
|
|
373
|
+
c_bc: npt.NDArray[np.floating]
|
|
374
|
+
Black carbon mass concentration, [:math:`mg m^{-3}`]
|
|
375
|
+
q_exhaust: npt.NDArray[np.floating]
|
|
376
|
+
Volume of exhaust gas per mass of fuel burnt, [:math:`m^{3}/kg_{fuel}`]
|
|
377
|
+
|
|
378
|
+
Returns
|
|
379
|
+
-------
|
|
380
|
+
npt.NDArray[np.floating]
|
|
381
|
+
Black carbon mass emissions index, [:math:`mg/kg_{fuel}`]
|
|
382
|
+
|
|
383
|
+
References
|
|
384
|
+
----------
|
|
385
|
+
- :cite:`stettlerGlobalCivilAviation2013`
|
|
386
|
+
"""
|
|
387
|
+
return c_bc * q_exhaust
|
|
388
|
+
|
|
389
|
+
|
|
390
|
+
# ----------------------------------------------------------
|
|
391
|
+
# nvPM Number Emissions Index: Fractal Aggregates (FA) model
|
|
392
|
+
# ----------------------------------------------------------
|
|
393
|
+
|
|
394
|
+
|
|
395
|
+
def geometric_mean_diameter_sac(
|
|
396
|
+
air_pressure: npt.NDArray[np.floating],
|
|
397
|
+
air_temperature: npt.NDArray[np.floating],
|
|
398
|
+
true_airspeed: npt.NDArray[np.floating],
|
|
399
|
+
thrust_setting: npt.NDArray[np.floating],
|
|
400
|
+
pressure_ratio: float,
|
|
401
|
+
q_fuel: float,
|
|
402
|
+
*,
|
|
403
|
+
comp_efficiency: float = 0.9,
|
|
404
|
+
delta_loss: float = 5.75,
|
|
405
|
+
cruise: bool = True,
|
|
406
|
+
) -> npt.NDArray[np.floating]:
|
|
407
|
+
r"""Calculate the BC GMD for singular annular combustor (SAC) engines.
|
|
408
|
+
|
|
409
|
+
The BC (black carbon) GMD (geometric mean diameter) is estimated using
|
|
410
|
+
the non-dimensionalized engine thrust setting, the ratio of turbine inlet
|
|
411
|
+
to the compressor inlet temperature (``t4_t2``).
|
|
412
|
+
|
|
413
|
+
Parameters
|
|
414
|
+
----------
|
|
415
|
+
air_pressure: npt.NDArray[np.floating]
|
|
416
|
+
Pressure altitude at each waypoint, [:math:`Pa`]
|
|
417
|
+
air_temperature: npt.NDArray[np.floating]
|
|
418
|
+
Ambient temperature for each waypoint, [:math:`K`]
|
|
419
|
+
true_airspeed: npt.NDArray[np.floating]
|
|
420
|
+
True airspeed for each waypoint, [:math:`m s^{-1}`]
|
|
421
|
+
thrust_setting: npt.NDArray[np.floating]
|
|
422
|
+
Engine thrust setting, which is the fuel mass flow rate divided by the
|
|
423
|
+
maximum fuel mass flow rate
|
|
424
|
+
pressure_ratio: float
|
|
425
|
+
Engine pressure ratio from the ICAO EDB
|
|
426
|
+
q_fuel : float
|
|
427
|
+
Lower calorific value (LCV) of fuel, [:math:`J \ kg_{fuel}^{-1}`]
|
|
428
|
+
comp_efficiency: float
|
|
429
|
+
Engine compressor efficiency (assumed to be 0.9)
|
|
430
|
+
delta_loss: float
|
|
431
|
+
Correction factor accounting for particle line losses (assumed to be 5.75 nm), [:math:`nm`]
|
|
432
|
+
cruise: bool
|
|
433
|
+
Set to true when the aircraft is not on the ground.
|
|
434
|
+
|
|
435
|
+
Returns
|
|
436
|
+
-------
|
|
437
|
+
npt.NDArray[np.floating]
|
|
438
|
+
black carbon geometric mean diameter, [:math:`nm`]
|
|
439
|
+
|
|
440
|
+
References
|
|
441
|
+
----------
|
|
442
|
+
- :cite:`teohMitigatingClimateForcing2020`
|
|
443
|
+
"""
|
|
444
|
+
t4_t2 = jet.thrust_setting_nd(
|
|
445
|
+
true_airspeed,
|
|
446
|
+
thrust_setting,
|
|
447
|
+
air_temperature,
|
|
448
|
+
air_pressure,
|
|
449
|
+
pressure_ratio,
|
|
450
|
+
q_fuel,
|
|
451
|
+
comp_efficiency=comp_efficiency,
|
|
452
|
+
cruise=cruise,
|
|
453
|
+
)
|
|
454
|
+
return (2.5883 * t4_t2**2) - (5.3723 * t4_t2) + 16.721 - delta_loss
|
|
455
|
+
|
|
456
|
+
|
|
457
|
+
def number_emissions_index_fractal_aggregates(
|
|
458
|
+
nvpm_ei_m: npt.NDArray[np.floating],
|
|
459
|
+
gmd: npt.NDArray[np.floating],
|
|
460
|
+
*,
|
|
461
|
+
gsd: float | np.floating | npt.NDArray[np.floating] = np.float32(1.80), # avoid promotion
|
|
462
|
+
rho_bc: float | np.floating = np.float32(1770.0),
|
|
463
|
+
k_tem: float | np.floating = np.float32(1.621e-5),
|
|
464
|
+
d_tem: float | np.floating = np.float32(0.39),
|
|
465
|
+
d_fm: float | np.floating = np.float32(2.76),
|
|
466
|
+
) -> npt.NDArray[np.floating]:
|
|
467
|
+
"""
|
|
468
|
+
Estimate the black carbon number emission index using the fractal aggregates (FA) model.
|
|
469
|
+
|
|
470
|
+
The FA model estimates the number emissions index from the mass emissions index,
|
|
471
|
+
particle size distribution, and morphology.
|
|
472
|
+
|
|
473
|
+
Parameters
|
|
474
|
+
----------
|
|
475
|
+
nvpm_ei_m: npt.NDArray[np.floating]
|
|
476
|
+
Black carbon mass emissions index, [:math:`kg/kg_{fuel}`]
|
|
477
|
+
gmd: npt.NDArray[np.floating]
|
|
478
|
+
Black carbon geometric mean diameter, [:math:`m`]
|
|
479
|
+
gsd: float
|
|
480
|
+
Black carbon geometric standard deviation (assumed to be 1.80)
|
|
481
|
+
rho_bc: float
|
|
482
|
+
Black carbon material density (1770 kg/m**3), [:math:`kg m^{-3}`]
|
|
483
|
+
k_tem: float
|
|
484
|
+
Transmission electron microscopy prefactor coefficient (assumed to be 1.621e-5)
|
|
485
|
+
d_tem: float
|
|
486
|
+
Transmission electron microscopy exponent coefficient (assumed to be 0.39)
|
|
487
|
+
d_fm: float
|
|
488
|
+
Mass-mobility exponent (assumed to be 2.76)
|
|
489
|
+
|
|
490
|
+
Returns
|
|
491
|
+
-------
|
|
492
|
+
npt.NDArray[np.floating]
|
|
493
|
+
Black carbon number emissions index, [:math:`kg_{fuel}^{-1}`]
|
|
494
|
+
|
|
495
|
+
References
|
|
496
|
+
----------
|
|
497
|
+
- FA model: :cite:`teohMethodologyRelateBlack2019`
|
|
498
|
+
- ``gmd``, ``gsd``, ``d_fm``: :cite:`teohMitigatingClimateForcing2020`
|
|
499
|
+
- ``rho_bc``: :cite:`parkMeasurementInherentMaterial2004`
|
|
500
|
+
- ``k_tem``, ``d_tem``: :cite:`dastanpourObservationsCorrelationPrimary2014`
|
|
501
|
+
"""
|
|
502
|
+
phi = 3.0 * d_tem + (1.0 - d_tem) * d_fm
|
|
503
|
+
exponential_term = np.exp(0.5 * phi**2 * np.log(gsd) ** 2)
|
|
504
|
+
denom = rho_bc * (np.pi / 6.0) * k_tem ** (3.0 - d_fm) * gmd**phi * exponential_term
|
|
505
|
+
return nvpm_ei_m / denom
|
|
506
|
+
|
|
507
|
+
|
|
508
|
+
# -------------------------------------------------------------
|
|
509
|
+
# Scale nvPM emissions indices due to sustainable aviation fuel
|
|
510
|
+
# -------------------------------------------------------------
|
|
511
|
+
|
|
512
|
+
|
|
513
|
+
def nvpm_number_ei_pct_reduction_due_to_saf(
|
|
514
|
+
hydrogen_content: float | npt.NDArray[np.floating],
|
|
515
|
+
thrust_setting: npt.NDArray[np.floating],
|
|
516
|
+
) -> npt.NDArray[np.floating]:
|
|
517
|
+
"""
|
|
518
|
+
Adjust nvPM number emissions index to account for the effects of sustainable aviation fuels.
|
|
519
|
+
|
|
520
|
+
Parameters
|
|
521
|
+
----------
|
|
522
|
+
hydrogen_content: float
|
|
523
|
+
The percentage of hydrogen mass content in the fuel.
|
|
524
|
+
thrust_setting: npt.NDArray[np.floating]
|
|
525
|
+
Engine thrust setting, where the equivalent fuel mass flow rate per engine at
|
|
526
|
+
sea level, :math:`[0 - 1]`.
|
|
527
|
+
|
|
528
|
+
Returns
|
|
529
|
+
-------
|
|
530
|
+
npt.NDArray[np.floating]
|
|
531
|
+
Percentage reduction in nvPM number emissions index
|
|
532
|
+
|
|
533
|
+
References
|
|
534
|
+
----------
|
|
535
|
+
- :cite:`teohTargetedUseSustainable2022`
|
|
536
|
+
- :cite:`bremEffectsFuelAromatic2015`
|
|
537
|
+
"""
|
|
538
|
+
a0 = -114.21
|
|
539
|
+
a1 = 1.06
|
|
540
|
+
a2 = 0.5
|
|
541
|
+
return _template_saf_reduction(hydrogen_content, thrust_setting, a0, a1, a2)
|
|
542
|
+
|
|
543
|
+
|
|
544
|
+
def nvpm_mass_ei_pct_reduction_due_to_saf(
|
|
545
|
+
hydrogen_content: float | npt.NDArray[np.floating],
|
|
546
|
+
thrust_setting: npt.NDArray[np.floating],
|
|
547
|
+
) -> npt.NDArray[np.floating]:
|
|
548
|
+
"""
|
|
549
|
+
Adjust nvPM mass emissions index to account for the effects of sustainable aviation fuels.
|
|
550
|
+
|
|
551
|
+
For fuel with hydrogen mass content > 14.3, the adjustment factor is adopted from
|
|
552
|
+
Teoh et al. (2022), which was used to calculate the change in nvPM EIn.
|
|
553
|
+
|
|
554
|
+
Parameters
|
|
555
|
+
----------
|
|
556
|
+
hydrogen_content: float
|
|
557
|
+
The percentage of hydrogen mass content in the fuel.
|
|
558
|
+
thrust_setting: npt.NDArray[np.floating]
|
|
559
|
+
Engine thrust setting, where the equivalent fuel mass flow rate per engine at
|
|
560
|
+
sea level, :math:`[0 - 1]`.
|
|
561
|
+
|
|
562
|
+
Returns
|
|
563
|
+
-------
|
|
564
|
+
npt.NDArray[np.floating]
|
|
565
|
+
Percentage reduction in nvPM number emissions index
|
|
566
|
+
|
|
567
|
+
References
|
|
568
|
+
----------
|
|
569
|
+
- :cite:`teohTargetedUseSustainable2022`
|
|
570
|
+
- :cite:`bremEffectsFuelAromatic2015`
|
|
571
|
+
"""
|
|
572
|
+
a0 = -124.05
|
|
573
|
+
a1 = 1.02
|
|
574
|
+
a2 = 0.6
|
|
575
|
+
return _template_saf_reduction(hydrogen_content, thrust_setting, a0, a1, a2)
|
|
576
|
+
|
|
577
|
+
|
|
578
|
+
def _template_saf_reduction(
|
|
579
|
+
hydrogen_content: float | npt.NDArray[np.floating],
|
|
580
|
+
thrust_setting: npt.NDArray[np.floating],
|
|
581
|
+
a0: float,
|
|
582
|
+
a1: float,
|
|
583
|
+
a2: float,
|
|
584
|
+
) -> npt.NDArray[np.floating]:
|
|
585
|
+
# Thrust setting cannot be computed when engine data is not provided in
|
|
586
|
+
# the ICAO EDB, so set default to 45% thrust.
|
|
587
|
+
thrust_setting = np.nan_to_num(thrust_setting, nan=0.45)
|
|
588
|
+
delta_h = hydrogen_content - 13.80
|
|
589
|
+
d_nvpm_ein_pct = (a0 + a1 * (thrust_setting * 100.0)) * delta_h
|
|
590
|
+
|
|
591
|
+
# Adjust when delta_h is large
|
|
592
|
+
if isinstance(delta_h, np.ndarray):
|
|
593
|
+
filt = delta_h > 0.5
|
|
594
|
+
d_nvpm_ein_pct[filt] *= np.exp(0.5 * (a2 - delta_h[filt]))
|
|
595
|
+
elif delta_h > 0.5:
|
|
596
|
+
d_nvpm_ein_pct *= np.exp(0.5 * (a2 - delta_h))
|
|
597
|
+
|
|
598
|
+
d_nvpm_ein_pct.clip(min=-90.0, max=0.0, out=d_nvpm_ein_pct)
|
|
599
|
+
return d_nvpm_ein_pct
|