pycontrails 0.54.2__cp312-cp312-macosx_11_0_arm64.whl → 0.54.4__cp312-cp312-macosx_11_0_arm64.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 +2 -2
- pycontrails/_version.py +2 -2
- pycontrails/core/__init__.py +1 -1
- pycontrails/core/aircraft_performance.py +75 -61
- pycontrails/core/cache.py +7 -7
- pycontrails/core/fleet.py +25 -21
- pycontrails/core/flight.py +215 -301
- pycontrails/core/interpolation.py +56 -56
- pycontrails/core/met.py +48 -39
- pycontrails/core/models.py +25 -11
- pycontrails/core/polygon.py +15 -15
- pycontrails/core/rgi_cython.cpython-312-darwin.so +0 -0
- pycontrails/core/vector.py +22 -22
- pycontrails/datalib/_met_utils/metsource.py +8 -5
- pycontrails/datalib/ecmwf/__init__.py +14 -14
- pycontrails/datalib/ecmwf/common.py +1 -1
- pycontrails/datalib/ecmwf/era5.py +7 -7
- pycontrails/datalib/ecmwf/hres.py +3 -3
- pycontrails/datalib/ecmwf/ifs.py +1 -1
- pycontrails/datalib/ecmwf/variables.py +1 -0
- pycontrails/datalib/gfs/__init__.py +6 -6
- pycontrails/datalib/gfs/gfs.py +2 -2
- pycontrails/datalib/goes.py +5 -5
- pycontrails/datalib/landsat.py +5 -8
- pycontrails/datalib/sentinel.py +7 -11
- pycontrails/ext/bada.py +3 -2
- pycontrails/ext/empirical_grid.py +1 -1
- pycontrails/ext/synthetic_flight.py +3 -2
- pycontrails/models/accf.py +40 -19
- pycontrails/models/apcemm/apcemm.py +5 -4
- pycontrails/models/cocip/__init__.py +2 -2
- pycontrails/models/cocip/cocip.py +16 -17
- pycontrails/models/cocip/cocip_params.py +2 -11
- pycontrails/models/cocip/cocip_uncertainty.py +24 -18
- pycontrails/models/cocip/contrail_properties.py +331 -316
- pycontrails/models/cocip/output_formats.py +53 -53
- pycontrails/models/cocip/radiative_forcing.py +135 -131
- pycontrails/models/cocip/radiative_heating.py +135 -135
- pycontrails/models/cocip/unterstrasser_wake_vortex.py +90 -87
- pycontrails/models/cocip/wake_vortex.py +92 -92
- pycontrails/models/cocip/wind_shear.py +8 -8
- pycontrails/models/cocipgrid/cocip_grid.py +118 -107
- pycontrails/models/dry_advection.py +59 -58
- pycontrails/models/emissions/__init__.py +2 -2
- pycontrails/models/emissions/black_carbon.py +108 -108
- pycontrails/models/emissions/emissions.py +85 -85
- pycontrails/models/emissions/ffm2.py +35 -35
- pycontrails/models/humidity_scaling/humidity_scaling.py +23 -23
- pycontrails/models/ps_model/__init__.py +3 -2
- pycontrails/models/ps_model/ps_aircraft_params.py +11 -6
- pycontrails/models/ps_model/ps_grid.py +256 -60
- pycontrails/models/ps_model/ps_model.py +18 -21
- pycontrails/models/ps_model/ps_operational_limits.py +58 -69
- pycontrails/models/tau_cirrus.py +8 -1
- pycontrails/physics/geo.py +216 -67
- pycontrails/physics/jet.py +220 -90
- pycontrails/physics/static/iata-cargo-load-factors-20241115.csv +71 -0
- pycontrails/physics/static/iata-passenger-load-factors-20241115.csv +71 -0
- pycontrails/physics/units.py +14 -14
- pycontrails/utils/json.py +1 -2
- pycontrails/utils/types.py +12 -7
- {pycontrails-0.54.2.dist-info → pycontrails-0.54.4.dist-info}/METADATA +10 -10
- {pycontrails-0.54.2.dist-info → pycontrails-0.54.4.dist-info}/NOTICE +1 -1
- pycontrails-0.54.4.dist-info/RECORD +111 -0
- {pycontrails-0.54.2.dist-info → pycontrails-0.54.4.dist-info}/WHEEL +1 -1
- pycontrails-0.54.2.dist-info/RECORD +0 -109
- {pycontrails-0.54.2.dist-info → pycontrails-0.54.4.dist-info}/LICENSE +0 -0
- {pycontrails-0.54.2.dist-info → pycontrails-0.54.4.dist-info}/top_level.txt +0 -0
pycontrails/physics/jet.py
CHANGED
|
@@ -1,22 +1,28 @@
|
|
|
1
1
|
"""Jet aircraft trajectory and performance parameters.
|
|
2
2
|
|
|
3
3
|
This module includes common functions to calculate jet aircraft trajectory
|
|
4
|
-
and performance parameters, including fuel quantities, mass, thrust setting
|
|
5
|
-
and
|
|
4
|
+
and performance parameters, including fuel quantities, mass, thrust setting,
|
|
5
|
+
propulsion efficiency and load factors.
|
|
6
6
|
"""
|
|
7
7
|
|
|
8
8
|
from __future__ import annotations
|
|
9
9
|
|
|
10
|
+
import functools
|
|
10
11
|
import logging
|
|
12
|
+
import pathlib
|
|
11
13
|
|
|
12
14
|
import numpy as np
|
|
13
15
|
import numpy.typing as npt
|
|
16
|
+
import pandas as pd
|
|
14
17
|
|
|
15
18
|
from pycontrails.core import flight
|
|
16
19
|
from pycontrails.physics import constants, units
|
|
17
20
|
from pycontrails.utils.types import ArrayOrFloat, ArrayScalarLike
|
|
18
21
|
|
|
19
22
|
logger = logging.getLogger(__name__)
|
|
23
|
+
_path_to_static = pathlib.Path(__file__).parent / "static"
|
|
24
|
+
PLF_PATH = _path_to_static / "iata-passenger-load-factors-20241115.csv"
|
|
25
|
+
CLF_PATH = _path_to_static / "iata-cargo-load-factors-20241115.csv"
|
|
20
26
|
|
|
21
27
|
|
|
22
28
|
# -------------------
|
|
@@ -25,25 +31,25 @@ logger = logging.getLogger(__name__)
|
|
|
25
31
|
|
|
26
32
|
|
|
27
33
|
def acceleration(
|
|
28
|
-
true_airspeed: npt.NDArray[np.
|
|
29
|
-
) -> npt.NDArray[np.
|
|
34
|
+
true_airspeed: npt.NDArray[np.floating], segment_duration: npt.NDArray[np.floating]
|
|
35
|
+
) -> npt.NDArray[np.floating]:
|
|
30
36
|
r"""Calculate the acceleration/deceleration at each waypoint.
|
|
31
37
|
|
|
32
38
|
Parameters
|
|
33
39
|
----------
|
|
34
|
-
true_airspeed : npt.NDArray[np.
|
|
40
|
+
true_airspeed : npt.NDArray[np.floating]
|
|
35
41
|
True airspeed, [:math:`m \ s^{-1}`]
|
|
36
|
-
segment_duration : npt.NDArray[np.
|
|
42
|
+
segment_duration : npt.NDArray[np.floating]
|
|
37
43
|
Time difference between waypoints, [:math:`s`]
|
|
38
44
|
|
|
39
45
|
Returns
|
|
40
46
|
-------
|
|
41
|
-
npt.NDArray[np.
|
|
47
|
+
npt.NDArray[np.floating]
|
|
42
48
|
Acceleration/deceleration, [:math:`m \ s^{-2}`]
|
|
43
49
|
|
|
44
50
|
See Also
|
|
45
51
|
--------
|
|
46
|
-
|
|
52
|
+
pycontrails.Flight.segment_duration
|
|
47
53
|
"""
|
|
48
54
|
dv_dt = np.empty_like(true_airspeed)
|
|
49
55
|
dv_dt[:-1] = np.diff(true_airspeed) / segment_duration[:-1]
|
|
@@ -53,26 +59,26 @@ def acceleration(
|
|
|
53
59
|
|
|
54
60
|
|
|
55
61
|
def climb_descent_angle(
|
|
56
|
-
true_airspeed: npt.NDArray[np.
|
|
57
|
-
) -> npt.NDArray[np.
|
|
62
|
+
true_airspeed: npt.NDArray[np.floating], rocd: npt.NDArray[np.floating]
|
|
63
|
+
) -> npt.NDArray[np.floating]:
|
|
58
64
|
r"""Calculate angle between the horizontal plane and the actual flight path.
|
|
59
65
|
|
|
60
66
|
Parameters
|
|
61
67
|
----------
|
|
62
|
-
true_airspeed : npt.NDArray[np.
|
|
68
|
+
true_airspeed : npt.NDArray[np.floating]
|
|
63
69
|
True airspeed, [:math:`m \ s^{-1}`]
|
|
64
|
-
rocd : npt.NDArray[np.
|
|
70
|
+
rocd : npt.NDArray[np.floating]
|
|
65
71
|
Rate of climb/descent, [:math:`ft min^{-1}`]
|
|
66
72
|
|
|
67
73
|
Returns
|
|
68
74
|
-------
|
|
69
|
-
npt.NDArray[np.
|
|
75
|
+
npt.NDArray[np.floating]
|
|
70
76
|
Climb (positive value) or descent (negative value) angle, [:math:`\deg`]
|
|
71
77
|
|
|
72
78
|
See Also
|
|
73
79
|
--------
|
|
74
|
-
|
|
75
|
-
|
|
80
|
+
pycontrails.Flight.segment_rocd
|
|
81
|
+
pycontrails.Flight.segment_true_airspeed
|
|
76
82
|
"""
|
|
77
83
|
rocd_ms = units.ft_to_m(rocd) / 60.0
|
|
78
84
|
sin_theta = rocd_ms / true_airspeed
|
|
@@ -80,10 +86,10 @@ def climb_descent_angle(
|
|
|
80
86
|
|
|
81
87
|
|
|
82
88
|
def clip_mach_number(
|
|
83
|
-
true_airspeed: npt.NDArray[np.
|
|
84
|
-
air_temperature: npt.NDArray[np.
|
|
89
|
+
true_airspeed: npt.NDArray[np.floating],
|
|
90
|
+
air_temperature: npt.NDArray[np.floating],
|
|
85
91
|
max_mach_number: ArrayOrFloat,
|
|
86
|
-
) -> tuple[npt.NDArray[np.
|
|
92
|
+
) -> tuple[npt.NDArray[np.floating], npt.NDArray[np.floating]]:
|
|
87
93
|
r"""Compute the Mach number from the true airspeed and ambient temperature.
|
|
88
94
|
|
|
89
95
|
This method clips the computed Mach number to the value of ``max_mach_number``.
|
|
@@ -93,9 +99,9 @@ def clip_mach_number(
|
|
|
93
99
|
|
|
94
100
|
Parameters
|
|
95
101
|
----------
|
|
96
|
-
true_airspeed : npt.NDArray[np.
|
|
102
|
+
true_airspeed : npt.NDArray[np.floating]
|
|
97
103
|
Array of true airspeed, [:math:`m \ s^{-1}`]
|
|
98
|
-
air_temperature : npt.NDArray[np.
|
|
104
|
+
air_temperature : npt.NDArray[np.floating]
|
|
99
105
|
Array of ambient temperature, [:math: `K`]
|
|
100
106
|
max_mach_number : ArrayOrFloat
|
|
101
107
|
Maximum mach number associated to aircraft. If no clipping
|
|
@@ -103,10 +109,10 @@ def clip_mach_number(
|
|
|
103
109
|
|
|
104
110
|
Returns
|
|
105
111
|
-------
|
|
106
|
-
true_airspeed : npt.NDArray[np.
|
|
112
|
+
true_airspeed : npt.NDArray[np.floating]
|
|
107
113
|
Array of true airspeed, [:math:`m \ s^{-1}`]. All values are clipped at
|
|
108
114
|
``max_mach_number``.
|
|
109
|
-
mach_num : npt.NDArray[np.
|
|
115
|
+
mach_num : npt.NDArray[np.floating]
|
|
110
116
|
Array of Mach numbers, [:math:`Ma`]. All values are clipped at
|
|
111
117
|
``max_mach_number``.
|
|
112
118
|
"""
|
|
@@ -131,13 +137,13 @@ def clip_mach_number(
|
|
|
131
137
|
|
|
132
138
|
|
|
133
139
|
def overall_propulsion_efficiency(
|
|
134
|
-
true_airspeed: npt.NDArray[np.
|
|
135
|
-
F_thrust: npt.NDArray[np.
|
|
136
|
-
fuel_flow: npt.NDArray[np.
|
|
140
|
+
true_airspeed: npt.NDArray[np.floating],
|
|
141
|
+
F_thrust: npt.NDArray[np.floating],
|
|
142
|
+
fuel_flow: npt.NDArray[np.floating],
|
|
137
143
|
q_fuel: float,
|
|
138
144
|
is_descent: npt.NDArray[np.bool_] | bool | None,
|
|
139
145
|
threshold: float = 0.5,
|
|
140
|
-
) -> npt.NDArray[np.
|
|
146
|
+
) -> npt.NDArray[np.floating]:
|
|
141
147
|
r"""Calculate the overall propulsion efficiency (OPE).
|
|
142
148
|
|
|
143
149
|
Negative OPE values can occur during the descent phase and is clipped to a
|
|
@@ -146,22 +152,22 @@ def overall_propulsion_efficiency(
|
|
|
146
152
|
|
|
147
153
|
Parameters
|
|
148
154
|
----------
|
|
149
|
-
true_airspeed: npt.NDArray[np.
|
|
155
|
+
true_airspeed: npt.NDArray[np.floating]
|
|
150
156
|
True airspeed for each waypoint, [:math:`m s^{-1}`].
|
|
151
|
-
F_thrust: npt.NDArray[np.
|
|
157
|
+
F_thrust: npt.NDArray[np.floating]
|
|
152
158
|
Thrust force provided by the engine, [:math:`N`].
|
|
153
|
-
fuel_flow: npt.NDArray[np.
|
|
159
|
+
fuel_flow: npt.NDArray[np.floating]
|
|
154
160
|
Fuel mass flow rate, [:math:`kg s^{-1}`].
|
|
155
161
|
q_fuel : float
|
|
156
162
|
Lower calorific value (LCV) of fuel, [:math:`J \ kg_{fuel}^{-1}`].
|
|
157
|
-
is_descent : npt.NDArray[np.
|
|
163
|
+
is_descent : npt.NDArray[np.floating] | None
|
|
158
164
|
Boolean array that indicates if a waypoint is in a descent phase.
|
|
159
165
|
threshold : float
|
|
160
166
|
Upper bound for realistic engine efficiency.
|
|
161
167
|
|
|
162
168
|
Returns
|
|
163
169
|
-------
|
|
164
|
-
npt.NDArray[np.
|
|
170
|
+
npt.NDArray[np.floating]
|
|
165
171
|
Overall propulsion efficiency (OPE)
|
|
166
172
|
|
|
167
173
|
References
|
|
@@ -190,49 +196,49 @@ def overall_propulsion_efficiency(
|
|
|
190
196
|
|
|
191
197
|
|
|
192
198
|
def fuel_burn(
|
|
193
|
-
fuel_flow: npt.NDArray[np.
|
|
194
|
-
) -> npt.NDArray[np.
|
|
199
|
+
fuel_flow: npt.NDArray[np.floating], segment_duration: npt.NDArray[np.floating]
|
|
200
|
+
) -> npt.NDArray[np.floating]:
|
|
195
201
|
"""Calculate the fuel consumption at each waypoint.
|
|
196
202
|
|
|
197
203
|
Parameters
|
|
198
204
|
----------
|
|
199
|
-
fuel_flow: npt.NDArray[np.
|
|
205
|
+
fuel_flow: npt.NDArray[np.floating]
|
|
200
206
|
Fuel mass flow rate, [:math:`kg s^{-1}`]
|
|
201
|
-
segment_duration: npt.NDArray[np.
|
|
207
|
+
segment_duration: npt.NDArray[np.floating]
|
|
202
208
|
Time difference between waypoints, [:math:`s`]
|
|
203
209
|
|
|
204
210
|
Returns
|
|
205
211
|
-------
|
|
206
|
-
npt.NDArray[np.
|
|
212
|
+
npt.NDArray[np.floating]
|
|
207
213
|
Fuel consumption at each waypoint, [:math:`kg`]
|
|
208
214
|
"""
|
|
209
215
|
return fuel_flow * segment_duration
|
|
210
216
|
|
|
211
217
|
|
|
212
218
|
def equivalent_fuel_flow_rate_at_sea_level(
|
|
213
|
-
fuel_flow_cruise: npt.NDArray[np.
|
|
214
|
-
theta_amb: npt.NDArray[np.
|
|
215
|
-
delta_amb: npt.NDArray[np.
|
|
216
|
-
mach_num: npt.NDArray[np.
|
|
217
|
-
) -> npt.NDArray[np.
|
|
219
|
+
fuel_flow_cruise: npt.NDArray[np.floating],
|
|
220
|
+
theta_amb: npt.NDArray[np.floating],
|
|
221
|
+
delta_amb: npt.NDArray[np.floating],
|
|
222
|
+
mach_num: npt.NDArray[np.floating],
|
|
223
|
+
) -> npt.NDArray[np.floating]:
|
|
218
224
|
r"""Convert fuel mass flow rate at cruise conditions to equivalent flow rate at sea level.
|
|
219
225
|
|
|
220
226
|
Refer to Eq. (40) in :cite:`duboisFuelFlowMethod22006`.
|
|
221
227
|
|
|
222
228
|
Parameters
|
|
223
229
|
----------
|
|
224
|
-
fuel_flow_cruise : npt.NDArray[np.
|
|
230
|
+
fuel_flow_cruise : npt.NDArray[np.floating]
|
|
225
231
|
Fuel mass flow rate per engine, [:math:`kg s^{-1}`]
|
|
226
|
-
theta_amb : npt.NDArray[np.
|
|
232
|
+
theta_amb : npt.NDArray[np.floating]
|
|
227
233
|
Ratio of the ambient temperature to the temperature at mean sea-level.
|
|
228
|
-
delta_amb : npt.NDArray[np.
|
|
234
|
+
delta_amb : npt.NDArray[np.floating]
|
|
229
235
|
Ratio of the pressure altitude to the surface pressure.
|
|
230
|
-
mach_num : npt.NDArray[np.
|
|
236
|
+
mach_num : npt.NDArray[np.floating]
|
|
231
237
|
Mach number, [:math: `Ma`]
|
|
232
238
|
|
|
233
239
|
Returns
|
|
234
240
|
-------
|
|
235
|
-
npt.NDArray[np.
|
|
241
|
+
npt.NDArray[np.floating]
|
|
236
242
|
Estimate of fuel flow per engine at sea level, [:math:`kg \ s^{-1}`].
|
|
237
243
|
|
|
238
244
|
References
|
|
@@ -243,18 +249,18 @@ def equivalent_fuel_flow_rate_at_sea_level(
|
|
|
243
249
|
|
|
244
250
|
|
|
245
251
|
def equivalent_fuel_flow_rate_at_cruise(
|
|
246
|
-
fuel_flow_sls: npt.NDArray[np.
|
|
252
|
+
fuel_flow_sls: npt.NDArray[np.floating] | float,
|
|
247
253
|
theta_amb: ArrayOrFloat,
|
|
248
254
|
delta_amb: ArrayOrFloat,
|
|
249
255
|
mach_num: ArrayOrFloat,
|
|
250
|
-
) -> npt.NDArray[np.
|
|
256
|
+
) -> npt.NDArray[np.floating]:
|
|
251
257
|
r"""Convert fuel mass flow rate at sea level to equivalent fuel flow rate at cruise conditions.
|
|
252
258
|
|
|
253
259
|
Refer to Eq. (40) in :cite:`duboisFuelFlowMethod22006`.
|
|
254
260
|
|
|
255
261
|
Parameters
|
|
256
262
|
----------
|
|
257
|
-
fuel_flow_sls : npt.NDArray[np.
|
|
263
|
+
fuel_flow_sls : npt.NDArray[np.floating] | float
|
|
258
264
|
Fuel mass flow rate, [:math:`kg s^{-1}`]
|
|
259
265
|
theta_amb : ArrayOrFloat
|
|
260
266
|
Ratio of the ambient temperature to the temperature at mean sea-level.
|
|
@@ -265,7 +271,7 @@ def equivalent_fuel_flow_rate_at_cruise(
|
|
|
265
271
|
|
|
266
272
|
Returns
|
|
267
273
|
-------
|
|
268
|
-
npt.NDArray[np.
|
|
274
|
+
npt.NDArray[np.floating]
|
|
269
275
|
Estimate of fuel mass flow rate at sea level, [:math:`kg \ s^{-1}`]
|
|
270
276
|
|
|
271
277
|
References
|
|
@@ -279,28 +285,28 @@ def equivalent_fuel_flow_rate_at_cruise(
|
|
|
279
285
|
|
|
280
286
|
|
|
281
287
|
def reserve_fuel_requirements(
|
|
282
|
-
rocd: npt.NDArray[np.
|
|
283
|
-
altitude_ft: npt.NDArray[np.
|
|
284
|
-
fuel_flow: npt.NDArray[np.
|
|
285
|
-
fuel_burn: npt.NDArray[np.
|
|
288
|
+
rocd: npt.NDArray[np.floating],
|
|
289
|
+
altitude_ft: npt.NDArray[np.floating],
|
|
290
|
+
fuel_flow: npt.NDArray[np.floating],
|
|
291
|
+
fuel_burn: npt.NDArray[np.floating],
|
|
286
292
|
) -> float:
|
|
287
293
|
r"""
|
|
288
294
|
Estimate reserve fuel requirements.
|
|
289
295
|
|
|
290
296
|
Parameters
|
|
291
297
|
----------
|
|
292
|
-
rocd: npt.NDArray[np.
|
|
298
|
+
rocd: npt.NDArray[np.floating]
|
|
293
299
|
Rate of climb and descent, [:math:`ft \ min^{-1}`]
|
|
294
|
-
altitude_ft: npt.NDArray[np.
|
|
300
|
+
altitude_ft: npt.NDArray[np.floating]
|
|
295
301
|
Altitude, [:math:`ft`]
|
|
296
|
-
fuel_flow: npt.NDArray[np.
|
|
302
|
+
fuel_flow: npt.NDArray[np.floating]
|
|
297
303
|
Fuel mass flow rate, [:math:`kg \ s^{-1}`].
|
|
298
|
-
fuel_burn: npt.NDArray[np.
|
|
304
|
+
fuel_burn: npt.NDArray[np.floating]
|
|
299
305
|
Fuel consumption for each waypoint, [:math:`kg`]
|
|
300
306
|
|
|
301
307
|
Returns
|
|
302
308
|
-------
|
|
303
|
-
npt.NDArray[np.
|
|
309
|
+
npt.NDArray[np.floating]
|
|
304
310
|
Reserve fuel requirements, [:math:`kg`]
|
|
305
311
|
|
|
306
312
|
References
|
|
@@ -319,8 +325,8 @@ def reserve_fuel_requirements(
|
|
|
319
325
|
|
|
320
326
|
See Also
|
|
321
327
|
--------
|
|
322
|
-
|
|
323
|
-
|
|
328
|
+
pycontrails.Flight.segment_phase
|
|
329
|
+
fuel_burn
|
|
324
330
|
"""
|
|
325
331
|
segment_phase = flight.segment_phase(rocd, altitude_ft)
|
|
326
332
|
|
|
@@ -345,6 +351,128 @@ def reserve_fuel_requirements(
|
|
|
345
351
|
# -------------
|
|
346
352
|
|
|
347
353
|
|
|
354
|
+
@functools.cache
|
|
355
|
+
def _historical_regional_load_factor(path: pathlib.Path) -> pd.DataFrame:
|
|
356
|
+
"""Load the historical regional load factor database.
|
|
357
|
+
|
|
358
|
+
Daily load factors are estimated from linearly interpolating the monthly statistics.
|
|
359
|
+
|
|
360
|
+
Returns
|
|
361
|
+
-------
|
|
362
|
+
pd.DataFrame
|
|
363
|
+
Historical regional load factor for each day.
|
|
364
|
+
|
|
365
|
+
Notes
|
|
366
|
+
-----
|
|
367
|
+
The monthly **passenger load factor** for each region is compiled from IATA's monthly
|
|
368
|
+
publication of the Air Passenger Market Analysis, where the static file will be continuously
|
|
369
|
+
updated. The report estimates the regional passenger load factor by dividing the revenue
|
|
370
|
+
passenger-km (RPK) by the available seat-km (ASK).
|
|
371
|
+
|
|
372
|
+
The monthly **cargo load factor** for each region is compiled from IATA's monthly publication
|
|
373
|
+
of the Air Cargo Market Analysis, where the static file will be continuously updated.
|
|
374
|
+
The report estimates the regional cargo load factor by dividing the freight tonne-km (FTK)
|
|
375
|
+
by the available freight tonne-km (AFTK).
|
|
376
|
+
"""
|
|
377
|
+
df = pd.read_csv(path, index_col="Date", parse_dates=True, date_format="%d/%m/%Y")
|
|
378
|
+
return df.resample("D").interpolate()
|
|
379
|
+
|
|
380
|
+
|
|
381
|
+
AIRPORT_TO_REGION = {
|
|
382
|
+
"A": "Asia Pacific",
|
|
383
|
+
"B": "Europe",
|
|
384
|
+
"C": "North America",
|
|
385
|
+
"D": "Africa",
|
|
386
|
+
"E": "Europe",
|
|
387
|
+
"F": "Africa",
|
|
388
|
+
"G": "Africa",
|
|
389
|
+
"H": "Africa",
|
|
390
|
+
"K": "North America",
|
|
391
|
+
"L": "Europe",
|
|
392
|
+
"M": "Latin America",
|
|
393
|
+
"N": "Asia Pacific",
|
|
394
|
+
"O": "Middle East",
|
|
395
|
+
"P": "Asia Pacific",
|
|
396
|
+
"R": "Asia Pacific",
|
|
397
|
+
"S": "Latin America",
|
|
398
|
+
"T": "Latin America",
|
|
399
|
+
"U": "Asia Pacific",
|
|
400
|
+
"V": "Asia Pacific",
|
|
401
|
+
"W": "Asia Pacific",
|
|
402
|
+
"Y": "Asia Pacific",
|
|
403
|
+
"Z": "Asia Pacific",
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
|
|
407
|
+
def aircraft_load_factor(
|
|
408
|
+
origin_airport_icao: str | None = None,
|
|
409
|
+
first_waypoint_time: pd.Timestamp | None = None,
|
|
410
|
+
*,
|
|
411
|
+
freighter: bool = False,
|
|
412
|
+
) -> float:
|
|
413
|
+
"""
|
|
414
|
+
Estimate passenger/cargo load factor based on historical data.
|
|
415
|
+
|
|
416
|
+
Accounts for regional and seasonal differences.
|
|
417
|
+
|
|
418
|
+
Parameters
|
|
419
|
+
----------
|
|
420
|
+
origin_airport_icao : str | None
|
|
421
|
+
ICAO code of origin airport. If None is provided, then globally averaged values will be
|
|
422
|
+
assumed at `first_waypoint_time`.
|
|
423
|
+
first_waypoint_time : pd.Timestamp | None
|
|
424
|
+
First waypoint UTC time. If None is provided, then regionally or globally averaged values
|
|
425
|
+
from the trailing twelve months will be used.
|
|
426
|
+
freighter: bool
|
|
427
|
+
Historical cargo load factor will be used if true, otherwise use passenger load factor.
|
|
428
|
+
|
|
429
|
+
Returns
|
|
430
|
+
-------
|
|
431
|
+
float
|
|
432
|
+
Passenger/cargo load factor [0 - 1], unitless
|
|
433
|
+
"""
|
|
434
|
+
# If origin airport is provided, use regional load factor
|
|
435
|
+
if origin_airport_icao is not None:
|
|
436
|
+
first_letter = origin_airport_icao[0]
|
|
437
|
+
region = AIRPORT_TO_REGION.get(first_letter, "Global")
|
|
438
|
+
else:
|
|
439
|
+
region = "Global"
|
|
440
|
+
|
|
441
|
+
# Use passenger or cargo database
|
|
442
|
+
if freighter:
|
|
443
|
+
lf_database = _historical_regional_load_factor(CLF_PATH)
|
|
444
|
+
else:
|
|
445
|
+
lf_database = _historical_regional_load_factor(PLF_PATH)
|
|
446
|
+
|
|
447
|
+
# If `first_waypoint_time` is None, global/regional averages for the trailing twelve months
|
|
448
|
+
# will be assumed.
|
|
449
|
+
if first_waypoint_time is None:
|
|
450
|
+
t1 = lf_database.index[-1]
|
|
451
|
+
t0 = t1 - pd.DateOffset(months=12) + pd.DateOffset(days=1)
|
|
452
|
+
return lf_database.loc[t0:t1, region].mean().item()
|
|
453
|
+
|
|
454
|
+
date = first_waypoint_time.floor("D")
|
|
455
|
+
|
|
456
|
+
# If `date` is more recent than the historical data, then use most recent load factors
|
|
457
|
+
# from trailing twelve months as seasonal values are stable except in COVID years (2020-22).
|
|
458
|
+
if date > lf_database.index[-1]:
|
|
459
|
+
if date.month == 2 and date.day == 29: # remove any leap day
|
|
460
|
+
date = date.replace(day=28)
|
|
461
|
+
|
|
462
|
+
filt = (lf_database.index.month == date.month) & (lf_database.index.day == date.day)
|
|
463
|
+
date = lf_database.index[filt][-1]
|
|
464
|
+
|
|
465
|
+
# (2) If `date` is before the historical data, then use 2019 load factors.
|
|
466
|
+
elif date < lf_database.index[0]:
|
|
467
|
+
if date.month == 2 and date.day == 29: # remove any leap day
|
|
468
|
+
date = date.replace(day=28)
|
|
469
|
+
|
|
470
|
+
filt = (lf_database.index.month == date.month) & (lf_database.index.day == date.day)
|
|
471
|
+
date = lf_database.index[filt][0]
|
|
472
|
+
|
|
473
|
+
return lf_database.at[date, region].item()
|
|
474
|
+
|
|
475
|
+
|
|
348
476
|
def aircraft_weight(aircraft_mass: ArrayOrFloat) -> ArrayOrFloat:
|
|
349
477
|
"""Calculate the aircraft weight at each waypoint.
|
|
350
478
|
|
|
@@ -413,7 +541,8 @@ def initial_aircraft_mass(
|
|
|
413
541
|
|
|
414
542
|
See Also
|
|
415
543
|
--------
|
|
416
|
-
|
|
544
|
+
reserve_fuel_requirements
|
|
545
|
+
aircraft_load_factor
|
|
417
546
|
"""
|
|
418
547
|
tom = operating_empty_weight + load_factor * max_payload + total_fuel_burn + total_reserve_fuel
|
|
419
548
|
return min(tom, max_takeoff_weight)
|
|
@@ -424,11 +553,11 @@ def update_aircraft_mass(
|
|
|
424
553
|
operating_empty_weight: float,
|
|
425
554
|
max_takeoff_weight: float,
|
|
426
555
|
max_payload: float,
|
|
427
|
-
fuel_burn: npt.NDArray[np.
|
|
556
|
+
fuel_burn: npt.NDArray[np.floating],
|
|
428
557
|
total_reserve_fuel: float,
|
|
429
558
|
load_factor: float,
|
|
430
559
|
takeoff_mass: float | None,
|
|
431
|
-
) -> npt.NDArray[np.
|
|
560
|
+
) -> npt.NDArray[np.floating]:
|
|
432
561
|
"""Update aircraft mass based on the simulated total fuel consumption.
|
|
433
562
|
|
|
434
563
|
Used internally for finding aircraft mass iteratively.
|
|
@@ -444,7 +573,7 @@ def update_aircraft_mass(
|
|
|
444
573
|
Aircraft maximum take-off weight, [:math:`kg`].
|
|
445
574
|
max_payload: float
|
|
446
575
|
Aircraft maximum payload, [:math:`kg`]
|
|
447
|
-
fuel_burn: npt.NDArray[np.
|
|
576
|
+
fuel_burn: npt.NDArray[np.floating]
|
|
448
577
|
Fuel consumption for each waypoint, [:math:`kg`]
|
|
449
578
|
total_reserve_fuel: float
|
|
450
579
|
Total reserve fuel requirements, [:math:`kg`]
|
|
@@ -458,14 +587,15 @@ def update_aircraft_mass(
|
|
|
458
587
|
|
|
459
588
|
Returns
|
|
460
589
|
-------
|
|
461
|
-
npt.NDArray[np.
|
|
590
|
+
npt.NDArray[np.floating]
|
|
462
591
|
Updated aircraft mass, [:math:`kg`]
|
|
463
592
|
|
|
464
593
|
See Also
|
|
465
594
|
--------
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
595
|
+
fuel_burn
|
|
596
|
+
reserve_fuel_requirements
|
|
597
|
+
initial_aircraft_mass
|
|
598
|
+
aircraft_load_factor
|
|
469
599
|
"""
|
|
470
600
|
if takeoff_mass is None:
|
|
471
601
|
takeoff_mass = initial_aircraft_mass(
|
|
@@ -631,30 +761,30 @@ def turbine_inlet_temperature(
|
|
|
631
761
|
|
|
632
762
|
|
|
633
763
|
def thrust_force(
|
|
634
|
-
altitude: npt.NDArray[np.
|
|
635
|
-
true_airspeed: npt.NDArray[np.
|
|
636
|
-
segment_duration: npt.NDArray[np.
|
|
637
|
-
aircraft_mass: npt.NDArray[np.
|
|
638
|
-
F_drag: npt.NDArray[np.
|
|
639
|
-
) -> npt.NDArray[np.
|
|
764
|
+
altitude: npt.NDArray[np.floating],
|
|
765
|
+
true_airspeed: npt.NDArray[np.floating],
|
|
766
|
+
segment_duration: npt.NDArray[np.floating],
|
|
767
|
+
aircraft_mass: npt.NDArray[np.floating],
|
|
768
|
+
F_drag: npt.NDArray[np.floating],
|
|
769
|
+
) -> npt.NDArray[np.floating]:
|
|
640
770
|
r"""Calculate the thrust force at each waypoint.
|
|
641
771
|
|
|
642
772
|
Parameters
|
|
643
773
|
----------
|
|
644
|
-
altitude : npt.NDArray[np.
|
|
774
|
+
altitude : npt.NDArray[np.floating]
|
|
645
775
|
Waypoint altitude, [:math:`m`]
|
|
646
|
-
true_airspeed : npt.NDArray[np.
|
|
776
|
+
true_airspeed : npt.NDArray[np.floating]
|
|
647
777
|
True airspeed, [:math:`m \ s^{-1}`]
|
|
648
|
-
segment_duration : npt.NDArray[np.
|
|
778
|
+
segment_duration : npt.NDArray[np.floating]
|
|
649
779
|
Time difference between waypoints, [:math:`s`]
|
|
650
|
-
aircraft_mass : npt.NDArray[np.
|
|
780
|
+
aircraft_mass : npt.NDArray[np.floating]
|
|
651
781
|
Aircraft mass, [:math:`kg`]
|
|
652
|
-
F_drag : npt.NDArray[np.
|
|
782
|
+
F_drag : npt.NDArray[np.floating]
|
|
653
783
|
Draft force, [:math:`N`]
|
|
654
784
|
|
|
655
785
|
Returns
|
|
656
786
|
-------
|
|
657
|
-
npt.NDArray[np.
|
|
787
|
+
npt.NDArray[np.floating]
|
|
658
788
|
Thrust force, [:math:`N`]
|
|
659
789
|
|
|
660
790
|
References
|
|
@@ -789,49 +919,49 @@ def air_to_fuel_ratio(
|
|
|
789
919
|
# -------------------
|
|
790
920
|
|
|
791
921
|
|
|
792
|
-
def temperature_ratio(T: npt.NDArray[np.
|
|
922
|
+
def temperature_ratio(T: npt.NDArray[np.floating]) -> npt.NDArray[np.floating]:
|
|
793
923
|
"""Calculate the ratio of ambient temperature relative to the temperature at mean sea level.
|
|
794
924
|
|
|
795
925
|
Parameters
|
|
796
926
|
----------
|
|
797
|
-
T : npt.NDArray[np.
|
|
927
|
+
T : npt.NDArray[np.floating]
|
|
798
928
|
Air temperature, [:math:`K`]
|
|
799
929
|
|
|
800
930
|
Returns
|
|
801
931
|
-------
|
|
802
|
-
npt.NDArray[np.
|
|
932
|
+
npt.NDArray[np.floating]
|
|
803
933
|
Ratio of the temperature to the temperature at mean sea-level (MSL).
|
|
804
934
|
"""
|
|
805
935
|
return T / constants.T_msl
|
|
806
936
|
|
|
807
937
|
|
|
808
|
-
def pressure_ratio(p: npt.NDArray[np.
|
|
938
|
+
def pressure_ratio(p: npt.NDArray[np.floating]) -> npt.NDArray[np.floating]:
|
|
809
939
|
"""Calculate the ratio of ambient pressure relative to the surface pressure.
|
|
810
940
|
|
|
811
941
|
Parameters
|
|
812
942
|
----------
|
|
813
|
-
p : npt.NDArray[np.
|
|
943
|
+
p : npt.NDArray[np.floating]
|
|
814
944
|
Air pressure, [:math:`Pa`]
|
|
815
945
|
|
|
816
946
|
Returns
|
|
817
947
|
-------
|
|
818
|
-
npt.NDArray[np.
|
|
948
|
+
npt.NDArray[np.floating]
|
|
819
949
|
Ratio of the pressure altitude to the surface pressure.
|
|
820
950
|
"""
|
|
821
951
|
return p / constants.p_surface
|
|
822
952
|
|
|
823
953
|
|
|
824
|
-
def density_ratio(rho: npt.NDArray[np.
|
|
954
|
+
def density_ratio(rho: npt.NDArray[np.floating]) -> npt.NDArray[np.floating]:
|
|
825
955
|
r"""Calculate the ratio of air density relative to the air density at mean-sea-level.
|
|
826
956
|
|
|
827
957
|
Parameters
|
|
828
958
|
----------
|
|
829
|
-
rho : npt.NDArray[np.
|
|
959
|
+
rho : npt.NDArray[np.floating]
|
|
830
960
|
Air density, [:math:`kg \ m^{3}`]
|
|
831
961
|
|
|
832
962
|
Returns
|
|
833
963
|
-------
|
|
834
|
-
npt.NDArray[np.
|
|
964
|
+
npt.NDArray[np.floating]
|
|
835
965
|
Ratio of the density to the air density at mean sea-level (MSL).
|
|
836
966
|
"""
|
|
837
967
|
return rho / constants.rho_msl
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
Date,Global,Africa,Asia Pacific,Europe,Latin America,Middle East,North America
|
|
2
|
+
15/12/2018,0.488,0.381,0.54,0.567,0.291,0.488,0.414
|
|
3
|
+
15/1/2019,0.451,0.354,0.501,0.501,0.299,0.421,0.4
|
|
4
|
+
15/2/2019,0.447,0.363,0.473,0.53,0.297,0.466,0.379
|
|
5
|
+
15/3/2019,0.495,0.384,0.556,0.56,0.323,0.488,0.416
|
|
6
|
+
15/4/2019,0.463,0.374,0.518,0.496,0.325,0.458,0.405
|
|
7
|
+
15/5/2019,0.468,0.386,0.52,0.513,0.353,0.469,0.398
|
|
8
|
+
15/6/2019,0.454,0.324,0.522,0.498,0.337,0.44,0.382
|
|
9
|
+
15/7/2019,0.45,0.323,0.519,0.485,0.354,0.453,0.373
|
|
10
|
+
15/8/2019,0.446,0.302,0.516,0.477,0.372,0.435,0.377
|
|
11
|
+
15/9/2019,0.464,0.329,0.539,0.501,0.379,0.459,0.381
|
|
12
|
+
15/10/2019,0.477,0.361,0.539,0.533,0.364,0.477,0.394
|
|
13
|
+
15/11/2019,0.496,0.404,0.538,0.569,0.403,0.497,0.413
|
|
14
|
+
15/12/2019,0.467,0.368,0.519,0.53,0.3,0.47,0.395
|
|
15
|
+
15/1/2020,0.45,0.356,0.474,0.501,0.311,0.426,0.424
|
|
16
|
+
15/2/2020,0.464,0.368,0.543,0.531,0.342,0.461,0.372
|
|
17
|
+
15/3/2020,0.545,0.425,0.656,0.63,0.411,0.532,0.429
|
|
18
|
+
15/4/2020,0.58,0.486,0.691,0.648,0.554,0.525,0.487
|
|
19
|
+
15/5/2020,0.576,0.612,0.643,0.625,0.561,0.483,0.526
|
|
20
|
+
15/6/2020,0.573,0.547,0.645,0.62,0.512,0.494,0.521
|
|
21
|
+
15/7/2020,0.564,0.489,0.639,0.594,0.464,0.53,0.506
|
|
22
|
+
15/8/2020,0.548,0.502,0.616,0.568,0.478,0.535,0.489
|
|
23
|
+
15/9/2020,0.569,0.507,0.642,0.62,0.456,0.579,0.484
|
|
24
|
+
15/10/2020,0.576,0.502,0.617,0.651,0.443,0.606,0.496
|
|
25
|
+
15/11/2020,0.582,0.496,0.631,0.655,0.436,0.6,0.5
|
|
26
|
+
15/12/2020,0.573,0.51,0.639,0.653,0.367,0.597,0.482
|
|
27
|
+
15/1/2021,0.589,0.48,0.665,0.627,0.39,0.569,0.532
|
|
28
|
+
15/2/2021,0.575,0.476,0.692,0.641,0.429,0.598,0.453
|
|
29
|
+
15/3/2021,0.588,0.499,0.661,0.685,0.453,0.613,0.472
|
|
30
|
+
15/4/2021,0.578,0.504,0.633,0.681,0.457,0.598,0.473
|
|
31
|
+
15/5/2021,0.572,0.502,0.646,0.656,0.423,0.589,0.469
|
|
32
|
+
15/6/2021,0.565,0.48,0.676,0.626,0.381,0.581,0.458
|
|
33
|
+
15/7/2021,0.544,0.455,0.654,0.598,0.387,0.536,0.443
|
|
34
|
+
15/8/2021,0.542,0.43,0.698,0.575,0.404,0.529,0.437
|
|
35
|
+
15/9/2021,0.553,0.428,0.68,0.604,0.37,0.558,0.447
|
|
36
|
+
15/10/2021,0.561,0.45,0.661,0.626,0.421,0.572,0.449
|
|
37
|
+
15/11/2021,0.559,0.434,0.654,0.631,0.446,0.572,0.444
|
|
38
|
+
15/12/2021,0.542,0.502,0.634,0.623,0.413,0.556,0.43
|
|
39
|
+
15/1/2022,0.541,0.492,0.609,0.584,0.417,0.513,0.474
|
|
40
|
+
15/2/2022,0.532,0.502,0.592,0.636,0.476,0.529,0.429
|
|
41
|
+
15/3/2022,0.549,0.494,0.638,0.671,0.448,0.526,0.442
|
|
42
|
+
15/4/2022,0.516,0.49,0.631,0.578,0.419,0.504,0.419
|
|
43
|
+
15/5/2022,0.505,0.495,0.627,0.548,0.387,0.487,0.411
|
|
44
|
+
15/6/2022,0.492,0.447,0.608,0.507,0.383,0.488,0.404
|
|
45
|
+
15/7/2022,0.472,0.452,0.563,0.493,0.374,0.469,0.398
|
|
46
|
+
15/8/2022,0.467,0.418,0.547,0.502,0.374,0.466,0.393
|
|
47
|
+
15/9/2022,0.481,0.451,0.572,0.528,0.381,0.478,0.396
|
|
48
|
+
15/10/2022,0.487,0.437,0.561,0.558,0.384,0.48,0.401
|
|
49
|
+
15/11/2022,0.491,0.458,0.545,0.569,0.382,0.475,0.419
|
|
50
|
+
15/12/2022,0.472,0.432,0.528,0.559,0.322,0.454,0.406
|
|
51
|
+
15/1/2023,0.448,0.439,0.452,0.541,0.325,0.411,0.423
|
|
52
|
+
15/2/2023,0.456,0.468,0.464,0.574,0.361,0.445,0.4
|
|
53
|
+
15/3/2023,0.462,0.489,0.485,0.57,0.366,0.456,0.393
|
|
54
|
+
15/4/2023,0.427,0.482,0.442,0.497,0.364,0.431,0.373
|
|
55
|
+
15/5/2023,0.415,0.448,0.422,0.489,0.333,0.41,0.373
|
|
56
|
+
15/6/2023,0.432,0.446,0.468,0.476,0.337,0.446,0.374
|
|
57
|
+
15/7/2023,0.421,0.417,0.457,0.472,0.322,0.411,0.37
|
|
58
|
+
15/8/2023,0.42,0.388,0.443,0.484,0.326,0.407,0.377
|
|
59
|
+
15/9/2023,0.438,0.436,0.466,0.5,0.319,0.424,0.392
|
|
60
|
+
15/10/2023,0.452,0.416,0.472,0.53,0.354,0.46,0.392
|
|
61
|
+
15/11/2023,0.467,0.421,0.479,0.57,0.363,0.469,0.408
|
|
62
|
+
15/12/2023,0.459,0.41,0.479,0.562,0.316,0.455,0.403
|
|
63
|
+
15/1/2024,0.457,0.431,0.446,0.555,0.344,0.439,0.435
|
|
64
|
+
15/2/2024,0.451,0.451,0.432,0.584,0.376,0.463,0.396
|
|
65
|
+
15/3/2024,0.473,0.473,0.475,0.581,0.402,0.496,0.404
|
|
66
|
+
15/4/2024,0.439,0.429,0.445,0.515,0.387,0.447,0.387
|
|
67
|
+
15/5/2024,0.446,0.438,0.453,0.518,0.362,0.461,0.397
|
|
68
|
+
15/6/2024,0.458,0.385,0.496,0.507,0.336,0.473,0.388
|
|
69
|
+
15/7/2024,0.444,0.4,0.48,0.496,0.338,0.458,0.382
|
|
70
|
+
15/8/2024,0.44,0.378,0.466,0.501,0.359,0.445,0.387
|
|
71
|
+
15/9/2024,0.456,0.392,0.485,0.525,0.368,0.474,0.389
|