pycontrails 0.54.3__cp310-cp310-win_amd64.whl → 0.54.4__cp310-cp310-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 +2 -2
- pycontrails/_version.py +2 -2
- pycontrails/core/__init__.py +1 -1
- pycontrails/core/aircraft_performance.py +58 -58
- pycontrails/core/cache.py +7 -7
- pycontrails/core/fleet.py +25 -21
- pycontrails/core/flight.py +213 -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.cp310-win_amd64.pyd +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/gfs/__init__.py +6 -6
- pycontrails/datalib/gfs/gfs.py +2 -2
- pycontrails/datalib/goes.py +5 -5
- pycontrails/ext/empirical_grid.py +1 -1
- pycontrails/models/apcemm/apcemm.py +3 -3
- pycontrails/models/cocip/__init__.py +2 -2
- pycontrails/models/cocip/cocip.py +15 -15
- 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 +93 -87
- pycontrails/models/dry_advection.py +10 -5
- 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 +1 -1
- pycontrails/models/ps_model/ps_aircraft_params.py +8 -4
- pycontrails/models/ps_model/ps_grid.py +74 -64
- pycontrails/models/ps_model/ps_model.py +14 -14
- pycontrails/models/ps_model/ps_operational_limits.py +20 -18
- pycontrails/models/tau_cirrus.py +8 -1
- pycontrails/physics/geo.py +67 -67
- pycontrails/physics/jet.py +79 -79
- pycontrails/physics/units.py +14 -14
- pycontrails/utils/json.py +1 -2
- pycontrails/utils/types.py +12 -7
- {pycontrails-0.54.3.dist-info → pycontrails-0.54.4.dist-info}/METADATA +2 -2
- {pycontrails-0.54.3.dist-info → pycontrails-0.54.4.dist-info}/NOTICE +1 -1
- pycontrails-0.54.4.dist-info/RECORD +111 -0
- pycontrails-0.54.3.dist-info/RECORD +0 -111
- {pycontrails-0.54.3.dist-info → pycontrails-0.54.4.dist-info}/LICENSE +0 -0
- {pycontrails-0.54.3.dist-info → pycontrails-0.54.4.dist-info}/WHEEL +0 -0
- {pycontrails-0.54.3.dist-info → pycontrails-0.54.4.dist-info}/top_level.txt +0 -0
|
@@ -13,6 +13,7 @@ import pandas as pd
|
|
|
13
13
|
|
|
14
14
|
from pycontrails.core.aircraft_performance import AircraftPerformanceParams
|
|
15
15
|
from pycontrails.physics import constants as c
|
|
16
|
+
from pycontrails.utils.types import ArrayOrFloat
|
|
16
17
|
|
|
17
18
|
#: Path to the Poll-Schumann aircraft parameters CSV file.
|
|
18
19
|
PS_FILE_PATH = pathlib.Path(__file__).parent / "static" / "ps-aircraft-params-20240524.csv"
|
|
@@ -260,18 +261,18 @@ def load_aircraft_engine_params(
|
|
|
260
261
|
return dict(_row_to_aircraft_engine_params(tup) for tup in df.itertuples(index=False))
|
|
261
262
|
|
|
262
263
|
|
|
263
|
-
def turbine_entry_temperature_at_max_take_off(first_flight:
|
|
264
|
+
def turbine_entry_temperature_at_max_take_off(first_flight: ArrayOrFloat) -> ArrayOrFloat:
|
|
264
265
|
"""
|
|
265
266
|
Calculate turbine entry temperature at maximum take-off rating.
|
|
266
267
|
|
|
267
268
|
Parameters
|
|
268
269
|
----------
|
|
269
|
-
first_flight:
|
|
270
|
+
first_flight: ArrayOrFloat
|
|
270
271
|
Year of first flight
|
|
271
272
|
|
|
272
273
|
Returns
|
|
273
274
|
-------
|
|
274
|
-
|
|
275
|
+
ArrayOrFloat
|
|
275
276
|
Turbine entry temperature at maximum take-off rating, ``tet_mto``, [:math:`K`]
|
|
276
277
|
|
|
277
278
|
Notes
|
|
@@ -284,7 +285,10 @@ def turbine_entry_temperature_at_max_take_off(first_flight: float) -> float:
|
|
|
284
285
|
----------
|
|
285
286
|
- :cite:`cumpstyJetPropulsion2015`
|
|
286
287
|
"""
|
|
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()
|
|
288
292
|
|
|
289
293
|
|
|
290
294
|
def turbine_entry_temperature_at_max_continuous_climb(tet_mto: float) -> float:
|
|
@@ -10,7 +10,6 @@ import numpy as np
|
|
|
10
10
|
import numpy.typing as npt
|
|
11
11
|
import scipy.optimize
|
|
12
12
|
import xarray as xr
|
|
13
|
-
import xarray.core.coordinates as xrcc
|
|
14
13
|
|
|
15
14
|
from pycontrails.core.aircraft_performance import (
|
|
16
15
|
AircraftPerformanceGrid,
|
|
@@ -178,9 +177,9 @@ class PSGrid(AircraftPerformanceGrid):
|
|
|
178
177
|
@dataclasses.dataclass
|
|
179
178
|
class _PerfVariables:
|
|
180
179
|
atyp_param: PSAircraftEngineParams
|
|
181
|
-
air_pressure: npt.NDArray[np.
|
|
182
|
-
air_temperature: npt.NDArray[np.
|
|
183
|
-
mach_number: npt.NDArray[np.
|
|
180
|
+
air_pressure: npt.NDArray[np.floating] | float
|
|
181
|
+
air_temperature: npt.NDArray[np.floating] | float
|
|
182
|
+
mach_number: npt.NDArray[np.floating] | float
|
|
184
183
|
q_fuel: float
|
|
185
184
|
|
|
186
185
|
|
|
@@ -193,8 +192,10 @@ def _nominal_perf(aircraft_mass: ArrayOrFloat, perf: _PerfVariables) -> Aircraft
|
|
|
193
192
|
mach_number = perf.mach_number
|
|
194
193
|
q_fuel = perf.q_fuel
|
|
195
194
|
|
|
196
|
-
|
|
197
|
-
|
|
195
|
+
# Using np.float32 here avoids scalar promotion to float64 via numpy 2.0 and NEP50
|
|
196
|
+
# In other words, the dtype of the perf variables is maintained
|
|
197
|
+
theta = np.float32(0.0)
|
|
198
|
+
dv_dt = np.float32(0.0)
|
|
198
199
|
|
|
199
200
|
rn = ps_model.reynolds_number(
|
|
200
201
|
atyp_param.wing_surface_area, mach_number, air_temperature, air_pressure
|
|
@@ -271,7 +272,7 @@ def _estimate_mass_extremes(
|
|
|
271
272
|
atyp_param: PSAircraftEngineParams,
|
|
272
273
|
perf: _PerfVariables,
|
|
273
274
|
n_iter: int = 3,
|
|
274
|
-
) -> tuple[npt.NDArray[np.
|
|
275
|
+
) -> tuple[npt.NDArray[np.floating], npt.NDArray[np.floating]]:
|
|
275
276
|
"""Calculate the minimum and maximum mass for a given aircraft type."""
|
|
276
277
|
|
|
277
278
|
oem = atyp_param.amass_oew # operating empty mass
|
|
@@ -296,40 +297,53 @@ def _estimate_mass_extremes(
|
|
|
296
297
|
|
|
297
298
|
|
|
298
299
|
def _parse_variables(
|
|
299
|
-
level: npt.NDArray[np.
|
|
300
|
-
air_temperature: xr.DataArray | npt.NDArray[np.
|
|
301
|
-
) -> tuple[
|
|
302
|
-
|
|
303
|
-
|
|
300
|
+
level: npt.NDArray[np.floating] | None,
|
|
301
|
+
air_temperature: xr.DataArray | npt.NDArray[np.floating] | None,
|
|
302
|
+
) -> tuple[
|
|
303
|
+
tuple[str],
|
|
304
|
+
dict[str, npt.NDArray[np.floating]],
|
|
305
|
+
npt.NDArray[np.floating],
|
|
306
|
+
npt.NDArray[np.floating],
|
|
307
|
+
]:
|
|
308
|
+
"""Parse the level and air temperature arguments.
|
|
309
|
+
|
|
310
|
+
Returns a tuple of ``(dims, coords, air_pressure, air_temperature)``.
|
|
311
|
+
"""
|
|
304
312
|
if isinstance(air_temperature, xr.DataArray):
|
|
305
313
|
if level is not None:
|
|
306
314
|
msg = "If 'air_temperature' is a DataArray, 'level' must be None"
|
|
307
315
|
raise ValueError(msg)
|
|
308
316
|
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
317
|
+
try:
|
|
318
|
+
pressure_da = air_temperature["air_pressure"]
|
|
319
|
+
except KeyError as exc:
|
|
320
|
+
msg = "An 'air_pressure' coordinate must be present in 'air_temperature'"
|
|
321
|
+
raise KeyError(msg) from exc
|
|
322
|
+
|
|
323
|
+
air_temperature, pressure_da = xr.broadcast(air_temperature, pressure_da)
|
|
324
|
+
return ( # type: ignore[return-value]
|
|
325
|
+
air_temperature.dims,
|
|
326
|
+
air_temperature.coords,
|
|
327
|
+
np.asarray(pressure_da),
|
|
328
|
+
np.asarray(air_temperature),
|
|
329
|
+
)
|
|
320
330
|
|
|
321
331
|
if level is None:
|
|
322
|
-
msg = "The 'level' argument must be
|
|
332
|
+
msg = "The 'level' argument must be provided"
|
|
323
333
|
raise ValueError(msg)
|
|
324
334
|
|
|
325
|
-
|
|
335
|
+
air_pressure = level * 100.0
|
|
336
|
+
if air_temperature is None:
|
|
337
|
+
altitude_m = units.pl_to_m(level)
|
|
338
|
+
air_temperature = units.m_to_T_isa(altitude_m)
|
|
339
|
+
return ("level",), {"level": level}, air_pressure, air_temperature
|
|
326
340
|
|
|
327
341
|
|
|
328
342
|
def ps_nominal_grid(
|
|
329
343
|
aircraft_type: str,
|
|
330
344
|
*,
|
|
331
|
-
level: npt.NDArray[np.
|
|
332
|
-
air_temperature: xr.DataArray | npt.NDArray[np.
|
|
345
|
+
level: npt.NDArray[np.floating] | None = None,
|
|
346
|
+
air_temperature: xr.DataArray | npt.NDArray[np.floating] | None = None,
|
|
333
347
|
q_fuel: float = JetA.q_fuel,
|
|
334
348
|
mach_number: float | None = None,
|
|
335
349
|
maxiter: int = PSGridParams.maxiter,
|
|
@@ -345,13 +359,13 @@ def ps_nominal_grid(
|
|
|
345
359
|
----------
|
|
346
360
|
aircraft_type : str
|
|
347
361
|
The aircraft type.
|
|
348
|
-
level : npt.NDArray[np.
|
|
362
|
+
level : npt.NDArray[np.floating] | None, optional
|
|
349
363
|
The pressure level, [:math:`hPa`]. If None, the ``air_temperature``
|
|
350
|
-
argument must be a :class:`xarray.DataArray` with
|
|
351
|
-
air_temperature : xr.DataArray | npt.NDArray[np.
|
|
364
|
+
argument must be a :class:`xarray.DataArray` with an ``air_pressure`` coordinate.
|
|
365
|
+
air_temperature : xr.DataArray | npt.NDArray[np.floating] | None, optional
|
|
352
366
|
The ambient air temperature, [:math:`K`]. If None (default), the ISA
|
|
353
367
|
temperature is computed from the ``level`` argument. If a :class:`xarray.DataArray`,
|
|
354
|
-
|
|
368
|
+
an ``air_pressure`` coordinate must be present and the ``level`` argument must be None
|
|
355
369
|
to avoid ambiguity. If a :class:`numpy.ndarray` is passed, it is assumed to be 1
|
|
356
370
|
dimensional with the same shape as the ``level`` argument.
|
|
357
371
|
q_fuel : float, optional
|
|
@@ -380,6 +394,10 @@ def ps_nominal_grid(
|
|
|
380
394
|
KeyError
|
|
381
395
|
If "aircraft_type" is not supported by the PS model.
|
|
382
396
|
|
|
397
|
+
See Also
|
|
398
|
+
--------
|
|
399
|
+
ps_nominal_optimize_mach
|
|
400
|
+
|
|
383
401
|
Examples
|
|
384
402
|
--------
|
|
385
403
|
>>> level = np.arange(200, 300, 10, dtype=float)
|
|
@@ -393,16 +411,16 @@ def ps_nominal_grid(
|
|
|
393
411
|
>>> perf.to_dataframe()
|
|
394
412
|
aircraft_mass engine_efficiency fuel_flow
|
|
395
413
|
level
|
|
396
|
-
200.0 58416.
|
|
414
|
+
200.0 58416.230844 0.300958 0.575635
|
|
397
415
|
210.0 61617.676624 0.300958 0.604417
|
|
398
|
-
220.0 64829.
|
|
399
|
-
230.0 68026.
|
|
416
|
+
220.0 64829.702584 0.300958 0.633199
|
|
417
|
+
230.0 68026.415694 0.300958 0.662998
|
|
400
418
|
240.0 71187.897060 0.300958 0.694631
|
|
401
|
-
250.0 71775.
|
|
402
|
-
260.0 71765.
|
|
403
|
-
270.0 71752.
|
|
404
|
-
280.0 71736.
|
|
405
|
-
290.0 71717.
|
|
419
|
+
250.0 71775.399880 0.300824 0.703349
|
|
420
|
+
260.0 71765.716789 0.300363 0.708259
|
|
421
|
+
270.0 71752.405449 0.299671 0.714514
|
|
422
|
+
280.0 71736.129125 0.298823 0.721878
|
|
423
|
+
290.0 71717.392213 0.297875 0.730169
|
|
406
424
|
|
|
407
425
|
>>> # Now compute it for a higher Mach number
|
|
408
426
|
>>> perf = ps_nominal_grid("A320", level=level, mach_number=0.78)
|
|
@@ -411,26 +429,16 @@ def ps_nominal_grid(
|
|
|
411
429
|
level
|
|
412
430
|
200.0 57941.825236 0.306598 0.596100
|
|
413
431
|
210.0 60626.062062 0.306605 0.621331
|
|
414
|
-
220.0 63818.
|
|
415
|
-
230.0 66993.
|
|
416
|
-
240.0 70129.
|
|
417
|
-
250.0 71703.
|
|
418
|
-
260.0 71690.
|
|
419
|
-
270.0 71673.
|
|
420
|
-
280.0 71653.
|
|
421
|
-
290.0 71630.
|
|
432
|
+
220.0 63818.498305 0.306605 0.650918
|
|
433
|
+
230.0 66993.691515 0.306605 0.681551
|
|
434
|
+
240.0 70129.930502 0.306605 0.714069
|
|
435
|
+
250.0 71703.009114 0.306560 0.732944
|
|
436
|
+
260.0 71690.188703 0.306239 0.739276
|
|
437
|
+
270.0 71673.392137 0.305694 0.747052
|
|
438
|
+
280.0 71653.431366 0.304997 0.755990
|
|
439
|
+
290.0 71630.901358 0.304201 0.765883
|
|
422
440
|
"""
|
|
423
|
-
coords
|
|
424
|
-
if isinstance(air_temperature, xr.DataArray):
|
|
425
|
-
dims = air_temperature.dims
|
|
426
|
-
coords = air_temperature.coords
|
|
427
|
-
else:
|
|
428
|
-
dims = ("level",)
|
|
429
|
-
coords = {"level": level}
|
|
430
|
-
|
|
431
|
-
level, air_temperature = _parse_variables(level, air_temperature)
|
|
432
|
-
|
|
433
|
-
air_pressure = level * 100.0
|
|
441
|
+
dims, coords, air_pressure, air_temperature = _parse_variables(level, air_temperature)
|
|
434
442
|
|
|
435
443
|
aircraft_engine_params = ps_model.load_aircraft_engine_params(engine_deterioration_factor)
|
|
436
444
|
|
|
@@ -475,7 +483,7 @@ def ps_nominal_grid(
|
|
|
475
483
|
func=_newton_func,
|
|
476
484
|
args=(perf,),
|
|
477
485
|
x0=x0,
|
|
478
|
-
tol=
|
|
486
|
+
tol=80.0, # use roughly the weight of a passenger as a tolerance
|
|
479
487
|
disp=False,
|
|
480
488
|
maxiter=maxiter,
|
|
481
489
|
)
|
|
@@ -551,7 +559,7 @@ def ps_nominal_optimize_mach(
|
|
|
551
559
|
"""Calculate the nominal optimal mach number for a given aircraft type.
|
|
552
560
|
|
|
553
561
|
This function is similar to the :class:`ps_nominal_grid` method, but rather than
|
|
554
|
-
maximizing engine
|
|
562
|
+
maximizing engine efficiency by adjusting aircraft, we are minimizing cost by adjusting
|
|
555
563
|
mach number.
|
|
556
564
|
|
|
557
565
|
Parameters
|
|
@@ -598,8 +606,7 @@ def ps_nominal_optimize_mach(
|
|
|
598
606
|
- ``"mach_number"``: The mach number that minimizes segment cost
|
|
599
607
|
- ``"fuel_flow"`` : Fuel flow rate, [:math:`kg/s`]
|
|
600
608
|
- ``"engine_efficiency"`` : Engine efficiency
|
|
601
|
-
- ``"aircraft_mass"`` : Aircraft mass,
|
|
602
|
-
[:math:`kg`]
|
|
609
|
+
- ``"aircraft_mass"`` : Aircraft mass, [:math:`kg`]
|
|
603
610
|
|
|
604
611
|
Raises
|
|
605
612
|
------
|
|
@@ -607,6 +614,10 @@ def ps_nominal_optimize_mach(
|
|
|
607
614
|
If "aircraft_type" is not supported by the PS model.
|
|
608
615
|
ValueError
|
|
609
616
|
If wind data is provided without segment angles.
|
|
617
|
+
|
|
618
|
+
See Also
|
|
619
|
+
--------
|
|
620
|
+
ps_nominal_grid
|
|
610
621
|
"""
|
|
611
622
|
dims = ("level",)
|
|
612
623
|
coords = {"level": level}
|
|
@@ -624,12 +635,11 @@ def ps_nominal_optimize_mach(
|
|
|
624
635
|
altitude_m = units.pl_to_m(level)
|
|
625
636
|
air_temperature = units.m_to_T_isa(altitude_m)
|
|
626
637
|
|
|
627
|
-
headwind: ArrayOrFloat
|
|
628
638
|
if northward_wind is not None and eastward_wind is not None:
|
|
629
639
|
if sin_a is None or cos_a is None:
|
|
630
640
|
msg = "Segment angles must be provide if wind data is specified"
|
|
631
641
|
raise ValueError(msg)
|
|
632
|
-
headwind = -(northward_wind * cos_a + eastward_wind * sin_a)
|
|
642
|
+
headwind = -(northward_wind * cos_a + eastward_wind * sin_a) # type: ignore[misc]
|
|
633
643
|
else:
|
|
634
644
|
headwind = 0.0 # type: ignore
|
|
635
645
|
|
|
@@ -224,14 +224,14 @@ class PSFlight(AircraftPerformance):
|
|
|
224
224
|
self,
|
|
225
225
|
*,
|
|
226
226
|
aircraft_type: str,
|
|
227
|
-
altitude_ft: npt.NDArray[np.
|
|
228
|
-
air_temperature: npt.NDArray[np.
|
|
227
|
+
altitude_ft: npt.NDArray[np.floating],
|
|
228
|
+
air_temperature: npt.NDArray[np.floating],
|
|
229
229
|
time: npt.NDArray[np.datetime64] | None,
|
|
230
|
-
true_airspeed: npt.NDArray[np.
|
|
231
|
-
aircraft_mass: npt.NDArray[np.
|
|
232
|
-
engine_efficiency: npt.NDArray[np.
|
|
233
|
-
fuel_flow: npt.NDArray[np.
|
|
234
|
-
thrust: npt.NDArray[np.
|
|
230
|
+
true_airspeed: npt.NDArray[np.floating] | float | None,
|
|
231
|
+
aircraft_mass: npt.NDArray[np.floating] | float,
|
|
232
|
+
engine_efficiency: npt.NDArray[np.floating] | float | None,
|
|
233
|
+
fuel_flow: npt.NDArray[np.floating] | float | None,
|
|
234
|
+
thrust: npt.NDArray[np.floating] | float | None,
|
|
235
235
|
q_fuel: float,
|
|
236
236
|
**kwargs: Any,
|
|
237
237
|
) -> AircraftPerformanceData:
|
|
@@ -266,8 +266,8 @@ class PSFlight(AircraftPerformance):
|
|
|
266
266
|
rn = reynolds_number(atyp_param.wing_surface_area, mach_num, air_temperature, air_pressure)
|
|
267
267
|
|
|
268
268
|
# Allow array or None time
|
|
269
|
-
dv_dt: npt.NDArray[np.
|
|
270
|
-
theta: npt.NDArray[np.
|
|
269
|
+
dv_dt: npt.NDArray[np.floating] | float
|
|
270
|
+
theta: npt.NDArray[np.floating] | float
|
|
271
271
|
if time is None:
|
|
272
272
|
# Assume a nominal cruising state
|
|
273
273
|
dt_sec = None
|
|
@@ -780,7 +780,7 @@ def overall_propulsion_efficiency(
|
|
|
780
780
|
c_t_eta_b: ArrayOrFloat,
|
|
781
781
|
atyp_param: PSAircraftEngineParams,
|
|
782
782
|
eta_over_eta_b_min: float | None = None,
|
|
783
|
-
) -> npt.NDArray[np.
|
|
783
|
+
) -> npt.NDArray[np.floating]:
|
|
784
784
|
"""Calculate overall propulsion efficiency.
|
|
785
785
|
|
|
786
786
|
Parameters
|
|
@@ -800,7 +800,7 @@ def overall_propulsion_efficiency(
|
|
|
800
800
|
|
|
801
801
|
Returns
|
|
802
802
|
-------
|
|
803
|
-
npt.NDArray[np.
|
|
803
|
+
npt.NDArray[np.floating]
|
|
804
804
|
Overall propulsion efficiency
|
|
805
805
|
"""
|
|
806
806
|
eta_over_eta_b = propulsion_efficiency_over_max_propulsion_efficiency(mach_num, c_t, c_t_eta_b)
|
|
@@ -816,7 +816,7 @@ def propulsion_efficiency_over_max_propulsion_efficiency(
|
|
|
816
816
|
mach_num: ArrayOrFloat,
|
|
817
817
|
c_t: ArrayOrFloat,
|
|
818
818
|
c_t_eta_b: ArrayOrFloat,
|
|
819
|
-
) -> npt.NDArray[np.
|
|
819
|
+
) -> npt.NDArray[np.floating]:
|
|
820
820
|
"""Calculate ratio of OPE to maximum OPE that can be attained for a given Mach number.
|
|
821
821
|
|
|
822
822
|
Parameters
|
|
@@ -830,7 +830,7 @@ def propulsion_efficiency_over_max_propulsion_efficiency(
|
|
|
830
830
|
|
|
831
831
|
Returns
|
|
832
832
|
-------
|
|
833
|
-
npt.NDArray[np.
|
|
833
|
+
npt.NDArray[np.floating]
|
|
834
834
|
Ratio of OPE to maximum OPE, ``eta / eta_b``
|
|
835
835
|
|
|
836
836
|
Notes
|
|
@@ -840,7 +840,7 @@ def propulsion_efficiency_over_max_propulsion_efficiency(
|
|
|
840
840
|
"""
|
|
841
841
|
c_t_over_c_t_eta_b = c_t / c_t_eta_b
|
|
842
842
|
|
|
843
|
-
sigma = np.where(mach_num < 0.4, 1.3 * (0.4 - mach_num), 0.0)
|
|
843
|
+
sigma = np.where(mach_num < 0.4, 1.3 * (0.4 - mach_num), np.float32(0.0)) # avoid promotion
|
|
844
844
|
|
|
845
845
|
eta_over_eta_b_low = (
|
|
846
846
|
10.0 * (1.0 + 0.8 * (sigma - 0.43) - 0.6027 * sigma * 0.43) * c_t_over_c_t_eta_b
|
|
@@ -155,34 +155,34 @@ def max_available_thrust_coefficient(
|
|
|
155
155
|
|
|
156
156
|
|
|
157
157
|
def get_excess_thrust_available(
|
|
158
|
-
mach_number:
|
|
159
|
-
air_temperature:
|
|
160
|
-
air_pressure:
|
|
161
|
-
aircraft_mass:
|
|
162
|
-
theta:
|
|
158
|
+
mach_number: ArrayOrFloat,
|
|
159
|
+
air_temperature: ArrayOrFloat,
|
|
160
|
+
air_pressure: ArrayOrFloat,
|
|
161
|
+
aircraft_mass: ArrayOrFloat,
|
|
162
|
+
theta: ArrayOrFloat,
|
|
163
163
|
atyp_param: PSAircraftEngineParams,
|
|
164
|
-
) ->
|
|
164
|
+
) -> ArrayOrFloat:
|
|
165
165
|
r"""
|
|
166
166
|
Calculate the excess thrust coefficient available at specified operation condition.
|
|
167
167
|
|
|
168
168
|
Parameters
|
|
169
169
|
----------
|
|
170
|
-
mach_number :
|
|
170
|
+
mach_number : ArrayOrFloat
|
|
171
171
|
Mach number at each waypoint
|
|
172
|
-
air_temperature :
|
|
172
|
+
air_temperature : ArrayOrFloat
|
|
173
173
|
Ambient temperature at each waypoint, [:math:`K`]
|
|
174
|
-
air_pressure :
|
|
174
|
+
air_pressure : ArrayOrFloat
|
|
175
175
|
Ambient pressure, [:math:`Pa`]
|
|
176
|
-
aircraft_mass :
|
|
176
|
+
aircraft_mass : ArrayOrFloat
|
|
177
177
|
Aircraft mass at each waypoint, [:math:`kg`]
|
|
178
|
-
theta :
|
|
178
|
+
theta : ArrayOrFloat
|
|
179
179
|
Climb (positive value) or descent (negative value) angle, [:math:`\deg`]
|
|
180
180
|
atyp_param : PSAircraftEngineParams
|
|
181
181
|
Extracted aircraft and engine parameters.
|
|
182
182
|
|
|
183
183
|
Returns
|
|
184
184
|
-------
|
|
185
|
-
|
|
185
|
+
ArrayOrFloat
|
|
186
186
|
The difference between the maximum rated thrust coefficient and the thrust coefficient
|
|
187
187
|
required to maintain the current mach_number.
|
|
188
188
|
"""
|
|
@@ -217,7 +217,7 @@ def get_excess_thrust_available(
|
|
|
217
217
|
)
|
|
218
218
|
|
|
219
219
|
tas = units.mach_number_to_tas(mach_number, air_temperature)
|
|
220
|
-
req_thrust_coeff = required_thrust_coefficient(c_lift, c_drag, tas)
|
|
220
|
+
req_thrust_coeff = required_thrust_coefficient(c_lift, c_drag, tas) # type: ignore[type-var]
|
|
221
221
|
|
|
222
222
|
c_t_eta_b = thrust_coefficient_at_max_efficiency(
|
|
223
223
|
mach_number, atyp_param.m_des, atyp_param.c_t_des
|
|
@@ -226,7 +226,7 @@ def get_excess_thrust_available(
|
|
|
226
226
|
air_temperature, mach_number, c_t_eta_b, atyp_param
|
|
227
227
|
)
|
|
228
228
|
|
|
229
|
-
return max_thrust_coeff - req_thrust_coeff
|
|
229
|
+
return max_thrust_coeff - req_thrust_coeff # type: ignore[return-value]
|
|
230
230
|
|
|
231
231
|
|
|
232
232
|
def _normalised_max_throttle_parameter(
|
|
@@ -466,7 +466,9 @@ def maximum_mach_num(
|
|
|
466
466
|
# ----------------
|
|
467
467
|
|
|
468
468
|
|
|
469
|
-
def fuel_flow_idle(
|
|
469
|
+
def fuel_flow_idle(
|
|
470
|
+
fuel_flow_idle_sls: float, altitude_ft: ArrayOrFloat
|
|
471
|
+
) -> npt.NDArray[np.floating]:
|
|
470
472
|
r"""Calculate minimum fuel mass flow rate at flight idle conditions.
|
|
471
473
|
|
|
472
474
|
Parameters
|
|
@@ -478,7 +480,7 @@ def fuel_flow_idle(fuel_flow_idle_sls: float, altitude_ft: ArrayOrFloat) -> npt.
|
|
|
478
480
|
|
|
479
481
|
Returns
|
|
480
482
|
-------
|
|
481
|
-
npt.NDArray[np.
|
|
483
|
+
npt.NDArray[np.floating]
|
|
482
484
|
Fuel mass flow rate at flight idle conditions, [:math:`kg \ s^{-1}`]
|
|
483
485
|
"""
|
|
484
486
|
x = altitude_ft / 10000.0
|
|
@@ -491,7 +493,7 @@ def max_fuel_flow(
|
|
|
491
493
|
mach_number: ArrayOrFloat,
|
|
492
494
|
fuel_flow_max_sls: float,
|
|
493
495
|
flight_phase: npt.NDArray[np.uint8] | flight.FlightPhase,
|
|
494
|
-
) -> npt.NDArray[np.
|
|
496
|
+
) -> npt.NDArray[np.floating]:
|
|
495
497
|
r"""Correct maximum fuel mass flow rate that can be supplied by the engine.
|
|
496
498
|
|
|
497
499
|
Parameters
|
|
@@ -509,7 +511,7 @@ def max_fuel_flow(
|
|
|
509
511
|
|
|
510
512
|
Returns
|
|
511
513
|
-------
|
|
512
|
-
npt.NDArray[np.
|
|
514
|
+
npt.NDArray[np.floating]
|
|
513
515
|
Maximum allowable fuel mass flow rate, [:math:`kg \ s^{-1}`]
|
|
514
516
|
"""
|
|
515
517
|
ff_max = jet.equivalent_fuel_flow_rate_at_cruise(
|
pycontrails/models/tau_cirrus.py
CHANGED
|
@@ -80,7 +80,14 @@ def tau_cirrus(met: MetDataset) -> xr.DataArray:
|
|
|
80
80
|
met.data["air_pressure"],
|
|
81
81
|
)
|
|
82
82
|
|
|
83
|
-
|
|
83
|
+
# dask.array.gradient expects at least 2 elements in each chunk
|
|
84
|
+
level_axis = geopotential_height.get_axis_num("level")
|
|
85
|
+
if geopotential_height.chunks:
|
|
86
|
+
level_chunks = geopotential_height.chunks[level_axis] # type: ignore[call-overload, index]
|
|
87
|
+
if any(chunk < 2 for chunk in level_chunks):
|
|
88
|
+
geopotential_height = geopotential_height.chunk(level=-1)
|
|
89
|
+
|
|
90
|
+
dz = -dask.array.gradient(geopotential_height, axis=level_axis)
|
|
84
91
|
dz = xr.DataArray(dz, dims=geopotential_height.dims)
|
|
85
92
|
|
|
86
93
|
da = beta_e * dz
|