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.

Files changed (122) hide show
  1. pycontrails/__init__.py +70 -0
  2. pycontrails/_version.py +34 -0
  3. pycontrails/core/__init__.py +30 -0
  4. pycontrails/core/aircraft_performance.py +679 -0
  5. pycontrails/core/airports.py +228 -0
  6. pycontrails/core/cache.py +889 -0
  7. pycontrails/core/coordinates.py +174 -0
  8. pycontrails/core/fleet.py +483 -0
  9. pycontrails/core/flight.py +2185 -0
  10. pycontrails/core/flightplan.py +228 -0
  11. pycontrails/core/fuel.py +140 -0
  12. pycontrails/core/interpolation.py +702 -0
  13. pycontrails/core/met.py +2931 -0
  14. pycontrails/core/met_var.py +387 -0
  15. pycontrails/core/models.py +1321 -0
  16. pycontrails/core/polygon.py +549 -0
  17. pycontrails/core/rgi_cython.cp314-win_amd64.pyd +0 -0
  18. pycontrails/core/vector.py +2249 -0
  19. pycontrails/datalib/__init__.py +12 -0
  20. pycontrails/datalib/_met_utils/metsource.py +746 -0
  21. pycontrails/datalib/ecmwf/__init__.py +73 -0
  22. pycontrails/datalib/ecmwf/arco_era5.py +345 -0
  23. pycontrails/datalib/ecmwf/common.py +114 -0
  24. pycontrails/datalib/ecmwf/era5.py +554 -0
  25. pycontrails/datalib/ecmwf/era5_model_level.py +490 -0
  26. pycontrails/datalib/ecmwf/hres.py +804 -0
  27. pycontrails/datalib/ecmwf/hres_model_level.py +466 -0
  28. pycontrails/datalib/ecmwf/ifs.py +287 -0
  29. pycontrails/datalib/ecmwf/model_levels.py +435 -0
  30. pycontrails/datalib/ecmwf/static/model_level_dataframe_v20240418.csv +139 -0
  31. pycontrails/datalib/ecmwf/variables.py +268 -0
  32. pycontrails/datalib/geo_utils.py +261 -0
  33. pycontrails/datalib/gfs/__init__.py +28 -0
  34. pycontrails/datalib/gfs/gfs.py +656 -0
  35. pycontrails/datalib/gfs/variables.py +104 -0
  36. pycontrails/datalib/goes.py +757 -0
  37. pycontrails/datalib/himawari/__init__.py +27 -0
  38. pycontrails/datalib/himawari/header_struct.py +266 -0
  39. pycontrails/datalib/himawari/himawari.py +667 -0
  40. pycontrails/datalib/landsat.py +589 -0
  41. pycontrails/datalib/leo_utils/__init__.py +5 -0
  42. pycontrails/datalib/leo_utils/correction.py +266 -0
  43. pycontrails/datalib/leo_utils/landsat_metadata.py +300 -0
  44. pycontrails/datalib/leo_utils/search.py +250 -0
  45. pycontrails/datalib/leo_utils/sentinel_metadata.py +748 -0
  46. pycontrails/datalib/leo_utils/static/bq_roi_query.sql +6 -0
  47. pycontrails/datalib/leo_utils/vis.py +59 -0
  48. pycontrails/datalib/sentinel.py +650 -0
  49. pycontrails/datalib/spire/__init__.py +5 -0
  50. pycontrails/datalib/spire/exceptions.py +62 -0
  51. pycontrails/datalib/spire/spire.py +604 -0
  52. pycontrails/ext/bada.py +42 -0
  53. pycontrails/ext/cirium.py +14 -0
  54. pycontrails/ext/empirical_grid.py +140 -0
  55. pycontrails/ext/synthetic_flight.py +431 -0
  56. pycontrails/models/__init__.py +1 -0
  57. pycontrails/models/accf.py +425 -0
  58. pycontrails/models/apcemm/__init__.py +8 -0
  59. pycontrails/models/apcemm/apcemm.py +983 -0
  60. pycontrails/models/apcemm/inputs.py +226 -0
  61. pycontrails/models/apcemm/static/apcemm_yaml_template.yaml +183 -0
  62. pycontrails/models/apcemm/utils.py +437 -0
  63. pycontrails/models/cocip/__init__.py +29 -0
  64. pycontrails/models/cocip/cocip.py +2742 -0
  65. pycontrails/models/cocip/cocip_params.py +305 -0
  66. pycontrails/models/cocip/cocip_uncertainty.py +291 -0
  67. pycontrails/models/cocip/contrail_properties.py +1530 -0
  68. pycontrails/models/cocip/output_formats.py +2270 -0
  69. pycontrails/models/cocip/radiative_forcing.py +1260 -0
  70. pycontrails/models/cocip/radiative_heating.py +520 -0
  71. pycontrails/models/cocip/unterstrasser_wake_vortex.py +508 -0
  72. pycontrails/models/cocip/wake_vortex.py +396 -0
  73. pycontrails/models/cocip/wind_shear.py +120 -0
  74. pycontrails/models/cocipgrid/__init__.py +9 -0
  75. pycontrails/models/cocipgrid/cocip_grid.py +2552 -0
  76. pycontrails/models/cocipgrid/cocip_grid_params.py +138 -0
  77. pycontrails/models/dry_advection.py +602 -0
  78. pycontrails/models/emissions/__init__.py +21 -0
  79. pycontrails/models/emissions/black_carbon.py +599 -0
  80. pycontrails/models/emissions/emissions.py +1353 -0
  81. pycontrails/models/emissions/ffm2.py +336 -0
  82. pycontrails/models/emissions/static/default-engine-uids.csv +239 -0
  83. pycontrails/models/emissions/static/edb-gaseous-v29b-engines.csv +596 -0
  84. pycontrails/models/emissions/static/edb-nvpm-v29b-engines.csv +215 -0
  85. pycontrails/models/extended_k15.py +1327 -0
  86. pycontrails/models/humidity_scaling/__init__.py +37 -0
  87. pycontrails/models/humidity_scaling/humidity_scaling.py +1075 -0
  88. pycontrails/models/humidity_scaling/quantiles/era5-model-level-quantiles.pq +0 -0
  89. pycontrails/models/humidity_scaling/quantiles/era5-pressure-level-quantiles.pq +0 -0
  90. pycontrails/models/issr.py +210 -0
  91. pycontrails/models/pcc.py +326 -0
  92. pycontrails/models/pcr.py +154 -0
  93. pycontrails/models/ps_model/__init__.py +18 -0
  94. pycontrails/models/ps_model/ps_aircraft_params.py +381 -0
  95. pycontrails/models/ps_model/ps_grid.py +701 -0
  96. pycontrails/models/ps_model/ps_model.py +1000 -0
  97. pycontrails/models/ps_model/ps_operational_limits.py +525 -0
  98. pycontrails/models/ps_model/static/ps-aircraft-params-20250328.csv +69 -0
  99. pycontrails/models/ps_model/static/ps-synonym-list-20250328.csv +104 -0
  100. pycontrails/models/sac.py +442 -0
  101. pycontrails/models/tau_cirrus.py +183 -0
  102. pycontrails/physics/__init__.py +1 -0
  103. pycontrails/physics/constants.py +117 -0
  104. pycontrails/physics/geo.py +1138 -0
  105. pycontrails/physics/jet.py +968 -0
  106. pycontrails/physics/static/iata-cargo-load-factors-20250221.csv +74 -0
  107. pycontrails/physics/static/iata-passenger-load-factors-20250221.csv +74 -0
  108. pycontrails/physics/thermo.py +551 -0
  109. pycontrails/physics/units.py +472 -0
  110. pycontrails/py.typed +0 -0
  111. pycontrails/utils/__init__.py +1 -0
  112. pycontrails/utils/dependencies.py +66 -0
  113. pycontrails/utils/iteration.py +13 -0
  114. pycontrails/utils/json.py +187 -0
  115. pycontrails/utils/temp.py +50 -0
  116. pycontrails/utils/types.py +163 -0
  117. pycontrails-0.58.0.dist-info/METADATA +180 -0
  118. pycontrails-0.58.0.dist-info/RECORD +122 -0
  119. pycontrails-0.58.0.dist-info/WHEEL +5 -0
  120. pycontrails-0.58.0.dist-info/licenses/LICENSE +178 -0
  121. pycontrails-0.58.0.dist-info/licenses/NOTICE +43 -0
  122. pycontrails-0.58.0.dist-info/top_level.txt +3 -0
@@ -0,0 +1,18 @@
1
+ """Support for the Poll-Schumann (PS) aircraft performance model."""
2
+
3
+ from pycontrails.models.ps_model.ps_aircraft_params import (
4
+ PSAircraftEngineParams,
5
+ load_aircraft_engine_params,
6
+ )
7
+ from pycontrails.models.ps_model.ps_grid import PSGrid, ps_nominal_grid, ps_nominal_optimize_mach
8
+ from pycontrails.models.ps_model.ps_model import PSFlight, PSFlightParams
9
+
10
+ __all__ = [
11
+ "PSAircraftEngineParams",
12
+ "PSFlight",
13
+ "PSFlightParams",
14
+ "PSGrid",
15
+ "load_aircraft_engine_params",
16
+ "ps_nominal_grid",
17
+ "ps_nominal_optimize_mach",
18
+ ]
@@ -0,0 +1,381 @@
1
+ """Simple dataclasses for working with PS aircraft performance data."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import dataclasses
6
+ import functools
7
+ import pathlib
8
+ from collections.abc import Mapping
9
+ from typing import Any
10
+
11
+ import numpy as np
12
+ import pandas as pd
13
+
14
+ from pycontrails.core.aircraft_performance import AircraftPerformanceParams
15
+ from pycontrails.physics import constants as c
16
+ from pycontrails.utils.types import ArrayOrFloat
17
+
18
+ #: Path to the Poll-Schumann aircraft parameters CSV file.
19
+ PS_FILE_PATH = pathlib.Path(__file__).parent / "static" / "ps-aircraft-params-20250328.csv"
20
+
21
+
22
+ @dataclasses.dataclass(frozen=True)
23
+ class PSAircraftEngineParams:
24
+ """Store extracted aircraft and engine parameters for each aircraft type.
25
+
26
+ -------------------------------------
27
+ AIRCRAFT INFORMATION
28
+ -------------------------------------
29
+ - ``manufacturer`` Aircraft manufacturer name
30
+ - ``aircraft_type`` Specific aircraft type variant
31
+ - ``n_engine`` Number of engines
32
+ - ``winglets`` Does the aircraft type contain winglets? (True/False)
33
+
34
+ -------------------------------------
35
+ AIRCRAFT MASS PARAMETERS
36
+ -------------------------------------
37
+ - ``amass_mtow`` Aircraft maximum take-off weight, [:math:`kg`]
38
+ - ``amass_mlw`` Aircraft maximum landing weight, [:math:`kg`]
39
+ - ``amass_mzfw`` Aircraft maximum zero fuel weight, [:math:`kg`]
40
+ - ``amass_oew`` Aircraft operating empty weight, [:math:`kg`]
41
+ - ``amass_mpl`` Aircraft maximum payload, [:math:`kg`]
42
+
43
+ -------------------------------------
44
+ AIRCRAFT GEOMETRY
45
+ -------------------------------------
46
+ - ``wing_surface_area`` Reference wing surface area, [:math:`m^{2}`]
47
+ - ``wing_span`` Wing span, [:math:`m`]
48
+ - ``fuselage_width`` Aircraft fuselage width, [:math:`m`]
49
+ - ``delta_2`` Induced drag wing-fuselage interference factor
50
+ - ``cos_sweep`` Cosine of wing sweep angle measured at the 1/4 chord line
51
+ - ``wing_aspect_ratio`` Wing aspect ratio, ``wing_span**2 / wing_surface_area``
52
+
53
+ -------------------------------------
54
+ AERODYNAMIC PARAMETERS
55
+ -------------------------------------
56
+ - ``psi_0`` Aircraft geometry drag parameter
57
+ - ``x_ref`` Threshold condition where the terminating shockwave moves onto
58
+ the rear part of the wing
59
+ - ``wing_constant`` A constant used in the wave drag model, capturing the aerofoil
60
+ technology factor and wing geometry
61
+ - ``j_1`` Wave drag parameter 1
62
+ - ``j_2`` Wave drag parameter 2
63
+ - ``j_3`` Wave drag parameter 3
64
+ - ``c_l_do`` Design optimum lift coefficient
65
+
66
+ -------------------------------------
67
+ ENGINE PARAMETERS
68
+ -------------------------------------
69
+ - ``f_00`` Maximum thrust force that can be supplied by the engine at sea level static
70
+ conditions, summed over all engines, [:math:`N`].
71
+ - ``ff_max_sls`` Fuel mass flow rate at take-off and sea level static conditions, summed
72
+ over all engines, [:math:`kg s^{-1}`]
73
+ - ``ff_idle_sls`` Fuel mass flow rate under engine idle and sea level static conditions,
74
+ summed over all engines, [:math:`kg s^{-1}`]
75
+ - ``m_des`` Design optimum Mach number where the fuel mass flow rate is at a minimum.
76
+ - ``c_t_des`` Design optimum engine thrust coefficient where the fuel mass flow rate is
77
+ at a minimum.
78
+ - ``eta_1`` Multiplier for maximum overall propulsion efficiency model
79
+ - ``eta_2`` Exponent for maximum overall propulsion efficiency model
80
+ - ``tr_ec`` Engine characteristic ratio of total turbine-entry-temperature to
81
+ the total freestream temperature for maximum overall efficiency.
82
+ - ``m_ec`` Engine characteristic Mach number associated with `tr_ec`.
83
+ - ``tet_mto`` Turbine entry temperature at maximum take-off rating, [:math:`K`]
84
+ - ``tet_mcc`` Turbine entry temperature at maximum continuous climb rating, [:math:`K`]
85
+ - ``nominal_opr`` Nominal engine operating pressure ratio.
86
+ - ``nominal_bpr`` Nominal engine bypass ratio.
87
+ - ``nominal_fpr`` Nominal engine fan pressure ratio.
88
+
89
+ -------------------------------------
90
+ HEIGHT AND SPEED LIMITS
91
+ -------------------------------------
92
+ - ``fl_max`` Maximum flight level
93
+ - ``max_mach_num`` Maximum operational Mach number
94
+ - ``p_i_max`` Maximum operational impact pressure, [:math:`Pa`]
95
+ - ``p_inf_co`` Crossover pressure altitude, [:math:`Pa`]
96
+ """
97
+
98
+ manufacturer: str
99
+ aircraft_type: str
100
+ n_engine: int
101
+ winglets: bool
102
+
103
+ amass_mtow: float
104
+ amass_mlw: float
105
+ amass_mzfw: float
106
+ amass_oew: float
107
+ amass_mpl: float
108
+
109
+ wing_surface_area: float
110
+ wing_span: float
111
+ fuselage_width: float
112
+ delta_2: float
113
+ cos_sweep: float
114
+ wing_aspect_ratio: float
115
+
116
+ psi_0: float
117
+ x_ref: float
118
+ wing_constant: float
119
+ j_1: float
120
+ j_2: float
121
+ j_3: float
122
+ c_l_do: float
123
+
124
+ f_00: float
125
+ ff_max_sls: float
126
+ ff_idle_sls: float
127
+ m_des: float
128
+ c_t_des: float
129
+ eta_1: float
130
+ eta_2: float
131
+ tr_ec: float
132
+ m_ec: float
133
+ tet_mto: float
134
+ tet_mcc: float
135
+ nominal_opr: float
136
+ nominal_bpr: float
137
+ nominal_fpr: float
138
+
139
+ fl_max: float
140
+ max_mach_num: float
141
+ p_i_max: float
142
+ p_inf_co: float
143
+
144
+
145
+ def _row_to_aircraft_engine_params(tup: Any) -> tuple[str, PSAircraftEngineParams]:
146
+ icao = tup.ICAO
147
+ wing_aspect_ratio = tup.AR
148
+ amass_mtow = tup.MTOM_kg
149
+ tet_mto = turbine_entry_temperature_at_max_take_off(tup.Year_of_first_flight)
150
+ p_i_max = impact_pressure_max_operating_limits(tup.MMO)
151
+ params = PSAircraftEngineParams(
152
+ manufacturer=tup.Manufacturer,
153
+ aircraft_type=tup.Type,
154
+ n_engine=tup.n_engine,
155
+ winglets=tup.winglets == "yes",
156
+ amass_mtow=amass_mtow,
157
+ amass_mlw=tup.MLM_kg,
158
+ amass_mzfw=tup.MZFM_kg,
159
+ amass_oew=tup.OEM_i_kg,
160
+ amass_mpl=tup.MPM_i_kg,
161
+ wing_surface_area=tup.Sref_m2,
162
+ wing_span=tup.span_m,
163
+ fuselage_width=tup.bf_m,
164
+ delta_2=tup.delta_2,
165
+ cos_sweep=tup.cos_sweep,
166
+ wing_aspect_ratio=wing_aspect_ratio,
167
+ psi_0=tup.psi_0,
168
+ x_ref=tup.Xo,
169
+ wing_constant=tup.wing_constant,
170
+ j_1=tup.j_1,
171
+ j_2=tup.j_2,
172
+ j_3=70.0, # use constant value for now, may be updated in the future
173
+ c_l_do=tup.CL_do,
174
+ f_00=tup.nominal_F00_ISA_kn * 1000.0,
175
+ ff_max_sls=tup.mf_max_T_O_SLS_kg_s,
176
+ ff_idle_sls=tup.mf_idle_SLS_kg_s,
177
+ m_des=tup.M_des,
178
+ c_t_des=tup.CT_des,
179
+ eta_1=tup.eta_1,
180
+ eta_2=tup.eta_2,
181
+ tr_ec=tup.Tec,
182
+ m_ec=tup.Mec,
183
+ tet_mto=tet_mto,
184
+ tet_mcc=turbine_entry_temperature_at_max_continuous_climb(tet_mto),
185
+ nominal_opr=tup.nominal_opr,
186
+ nominal_bpr=tup.nominal_bpr,
187
+ nominal_fpr=tup.nominal_fpr,
188
+ fl_max=tup.FL_max,
189
+ max_mach_num=tup.MMO,
190
+ p_i_max=p_i_max,
191
+ p_inf_co=crossover_pressure_altitude(tup.MMO, p_i_max),
192
+ )
193
+ return icao, params
194
+
195
+
196
+ @functools.cache
197
+ def load_aircraft_engine_params(
198
+ engine_deterioration_factor: float = AircraftPerformanceParams.engine_deterioration_factor,
199
+ ) -> Mapping[str, PSAircraftEngineParams]:
200
+ """
201
+ Extract aircraft-engine parameters for each aircraft type supported by the PS model.
202
+
203
+ Parameters
204
+ ----------
205
+ engine_deterioration_factor: float
206
+ Account for "in-service" engine deterioration between maintenance cycles.
207
+ Default value reduces `eta_1` by 2.5%, which increases the fuel flow estimates by 2.5%.
208
+
209
+ Returns
210
+ -------
211
+ Mapping[str, PSAircraftEngineParams]
212
+ Aircraft-engine parameters for each aircraft type supported by the PS model.
213
+ """
214
+ dtypes = {
215
+ "ICAO": object,
216
+ "Manufacturer": object,
217
+ "Type": object,
218
+ "Year_of_first_flight": float,
219
+ "n_engine": int,
220
+ "winglets": object,
221
+ "WV": object,
222
+ "MTOM_kg": float,
223
+ "MLM_kg": float,
224
+ "MZFM_kg": float,
225
+ "OEM_i_kg": float,
226
+ "MPM_i_kg": float,
227
+ "MZFM_MTOM": float,
228
+ "OEM_i_MTOM": float,
229
+ "MPM_i_MTOM": float,
230
+ "Sref_m2": float,
231
+ "span_m": float,
232
+ "bf_m": float,
233
+ "delta_2": float,
234
+ "cos_sweep": float,
235
+ "AR": float,
236
+ "psi_0": float,
237
+ "Xo": float,
238
+ "wing_constant": float,
239
+ "j_2": float,
240
+ "j_1": float,
241
+ "CL_do": float,
242
+ "nominal_F00_ISA_kn": float,
243
+ "mf_max_T_O_SLS_kg_s": float,
244
+ "mf_idle_SLS_kg_s": float,
245
+ "M_des": float,
246
+ "CT_des": float,
247
+ "eta_1": float,
248
+ "eta_2": float,
249
+ "Mec": float,
250
+ "Tec": float,
251
+ "FL_max": float,
252
+ "MMO": float,
253
+ "nominal_opr": float,
254
+ "nominal_bpr": float,
255
+ "nominal_fpr": float,
256
+ }
257
+
258
+ df = pd.read_csv(PS_FILE_PATH, dtype=dtypes)
259
+ df["eta_1"] *= 1.0 - engine_deterioration_factor
260
+
261
+ return dict(_row_to_aircraft_engine_params(tup) for tup in df.itertuples(index=False))
262
+
263
+
264
+ def turbine_entry_temperature_at_max_take_off(first_flight: ArrayOrFloat) -> ArrayOrFloat:
265
+ """
266
+ Calculate turbine entry temperature at maximum take-off rating.
267
+
268
+ Parameters
269
+ ----------
270
+ first_flight: ArrayOrFloat
271
+ Year of first flight
272
+
273
+ Returns
274
+ -------
275
+ ArrayOrFloat
276
+ Turbine entry temperature at maximum take-off rating, ``tet_mto``, [:math:`K`]
277
+
278
+ Notes
279
+ -----
280
+ The turbine entry temperature at max take-off is approximated based on the year of first flight
281
+ for the specific aircraft type. This approximation captures the historical trends of
282
+ improvements in turbine cooling technology level. The uncertainty of this estimate is ±75 K.
283
+
284
+ References
285
+ ----------
286
+ - :cite:`cumpstyJetPropulsion2015`
287
+ """
288
+ out = 2000.0 * (1.0 - np.exp(62.8 - 0.0325 * first_flight))
289
+ if isinstance(first_flight, np.ndarray):
290
+ return out
291
+ return out.item()
292
+
293
+
294
+ def turbine_entry_temperature_at_max_continuous_climb(tet_mto: float) -> float:
295
+ """
296
+ Calculate turbine entry temperature at maximum continuous climb rating.
297
+
298
+ Parameters
299
+ ----------
300
+ tet_mto: float
301
+ Turbine entry temperature at maximum take-off rating, `tet_mto`, [:math:`K`]
302
+
303
+ Returns
304
+ -------
305
+ float
306
+ Turbine entry temperature at maximum continuous climb rating, `tet_mcc`, [:math:`K`]
307
+ """
308
+ return 0.92 * tet_mto
309
+
310
+
311
+ def impact_pressure_max_operating_limits(max_mach_num: float) -> float:
312
+ """
313
+ Calculate maximum permitted operational impact pressure.
314
+
315
+ Parameters
316
+ ----------
317
+ max_mach_num: float
318
+ Maximum permitted operational Mach number for aircraft type.
319
+
320
+ Returns
321
+ -------
322
+ float
323
+ Maximum permitted operational impact pressure for aircraft type, ``p_i_max``, [:math:`Pa`]
324
+
325
+ Notes
326
+ -----
327
+ The impact pressure is the difference between the free stream total pressure ``p_0`` and the
328
+ atmospheric static pressure ``p_inf``. By definition, the calibrated airspeed, ``v_cas``, is
329
+ the speed at sea level in the ISA that has the same impact pressure.
330
+ """
331
+ v_cas_mo_over_c_msl = max_calibrated_airspeed_over_speed_of_sound(max_mach_num)
332
+ return c.p_surface * (
333
+ (1.0 + 0.5 * (c.kappa - 1.0) * v_cas_mo_over_c_msl**2) ** (c.kappa / (c.kappa - 1.0)) - 1.0
334
+ )
335
+
336
+
337
+ def max_calibrated_airspeed_over_speed_of_sound(max_mach_num: float) -> float:
338
+ """
339
+ Calculate max calibrated airspeed over the speed of sound at ISA mean sea level.
340
+
341
+ Parameters
342
+ ----------
343
+ max_mach_num: float
344
+ Maximum permitted operational Mach number for aircraft type.
345
+
346
+ Returns
347
+ -------
348
+ float
349
+ Ratio of maximum operating limits of the calibrated airspeed (CAS) over the speed of sound
350
+ at mean sea level (MSL) in standard atmosphere (ISA), ``v_cas_mo_over_c_msl``
351
+ """
352
+ return 0.57 * (max_mach_num + 0.10)
353
+
354
+
355
+ def crossover_pressure_altitude(max_mach_num: float, p_i_max: float) -> float:
356
+ """
357
+ Calculate crossover pressure altitude.
358
+
359
+ Parameters
360
+ ----------
361
+ max_mach_num: float
362
+ Maximum permitted operational Mach number for aircraft type.
363
+ p_i_max : float
364
+ Maximum permitted operational impact pressure for aircraft type, [:math:`Pa`]
365
+
366
+ Returns
367
+ -------
368
+ float
369
+ Crossover pressure altitude, ``p_inf_co``, [:math:`Pa`]
370
+
371
+ Notes
372
+ -----
373
+ At low altitudes, the calibrated airspeed (CAS) is used to determine the maximum aircraft
374
+ operational speed. However, the ambient temperature drops as altitude increases, which causes
375
+ the Mach number to increase even when the CAS remains constant. This can cause the Mach number
376
+ to exceed the maximum permitted operational Mach number. Therefore, above the crossover
377
+ altitude, the maximum operational speed is determined by the Mach number instead of CAS.
378
+ """
379
+ return p_i_max / (
380
+ 0.5 * c.kappa * max_mach_num**2 * (1.0 + (max_mach_num**2 / 4.0) + (max_mach_num**4 / 40.0))
381
+ )