pycontrails 0.58.0__cp314-cp314-macosx_10_13_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.

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.cpython-314-darwin.so +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 +6 -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,472 @@
1
+ """Unit conversion support."""
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
9
+ from pycontrails.utils.types import ArrayScalarLike, support_arraylike
10
+
11
+
12
+ def pl_to_ft(pl: ArrayScalarLike) -> ArrayScalarLike:
13
+ r"""Convert from pressure level (hPa) to altitude (ft).
14
+
15
+ Assumes the ICAO standard atmosphere.
16
+
17
+ Parameters
18
+ ----------
19
+ pl : ArrayScalarLike
20
+ pressure level, [:math:`hPa`], [:math:`mbar`]
21
+
22
+ Returns
23
+ -------
24
+ ArrayScalarLike
25
+ altitude, [:math:`ft`]
26
+
27
+ See Also
28
+ --------
29
+ pl_to_m
30
+ ft_to_pl
31
+ m_to_T_isa
32
+ """
33
+ return m_to_ft(pl_to_m(pl))
34
+
35
+
36
+ def ft_to_pl(h: ArrayScalarLike) -> ArrayScalarLike:
37
+ r"""Convert from altitude (ft) to pressure level (hPa).
38
+
39
+ Assumes the ICAO standard atmosphere.
40
+
41
+ Parameters
42
+ ----------
43
+ h : ArrayScalarLike
44
+ altitude, [:math:`ft`]
45
+
46
+ Returns
47
+ -------
48
+ ArrayScalarLike
49
+ pressure level, [:math:`hPa`], [:math:`mbar`]
50
+
51
+ See Also
52
+ --------
53
+ m_to_pl
54
+ pl_to_ft
55
+ m_to_T_isa
56
+ """
57
+ return m_to_pl(ft_to_m(h))
58
+
59
+
60
+ def kelvin_to_celsius(kelvin: ArrayScalarLike) -> ArrayScalarLike:
61
+ """Convert temperature from Kelvin to Celsius.
62
+
63
+ Parameters
64
+ ----------
65
+ kelvin : ArrayScalarLike
66
+ temperature [:math:`K`]
67
+
68
+ Returns
69
+ -------
70
+ ArrayScalarLike
71
+ temperature [:math:`C`]
72
+ """
73
+ return kelvin + constants.absolute_zero
74
+
75
+
76
+ def m_to_T_isa(h: ArrayScalarLike) -> ArrayScalarLike:
77
+ """Calculate the ambient temperature (K) for a given altitude (m).
78
+
79
+ Assumes the ICAO standard atmosphere.
80
+
81
+ Parameters
82
+ ----------
83
+ h : ArrayScalarLike
84
+ altitude, [:math:`m`]
85
+
86
+ Returns
87
+ -------
88
+ ArrayScalarLike
89
+ ICAO standard atmosphere ambient temperature, [:math:`K`]
90
+
91
+
92
+ References
93
+ ----------
94
+ - :cite:`wikipediacontributorsInternationalStandardAtmosphere2023`
95
+
96
+ Notes
97
+ -----
98
+ See https://en.wikipedia.org/wiki/International_Standard_Atmosphere
99
+
100
+ See Also
101
+ --------
102
+ m_to_pl
103
+ ft_to_pl
104
+ """
105
+ h_min = np.minimum(h, constants.h_tropopause)
106
+ return constants.T_msl + h_min * constants.T_lapse_rate # type: ignore[return-value]
107
+
108
+
109
+ def _low_altitude_m_to_pl(h: npt.NDArray[np.floating]) -> npt.NDArray[np.floating]:
110
+ T_isa = m_to_T_isa(h)
111
+ power_term = -constants.g / (constants.T_lapse_rate * constants.R_d)
112
+ return (constants.p_surface * (T_isa / constants.T_msl) ** power_term) / 100.0
113
+
114
+
115
+ def _high_altitude_m_to_pl(h: npt.NDArray[np.floating]) -> npt.NDArray[np.floating]:
116
+ T_tropopause_isa = m_to_T_isa(np.asarray(constants.h_tropopause))
117
+ power_term = -constants.g / (constants.T_lapse_rate * constants.R_d)
118
+ p_tropopause_isa = constants.p_surface * (T_tropopause_isa / constants.T_msl) ** power_term
119
+ inside_exp = (-constants.g / (constants.R_d * T_tropopause_isa)) * (h - constants.h_tropopause)
120
+ return p_tropopause_isa * np.exp(inside_exp) / 100.0
121
+
122
+
123
+ @support_arraylike
124
+ def m_to_pl(h: npt.NDArray[np.floating]) -> npt.NDArray[np.floating]:
125
+ r"""Convert from altitude (m) to pressure level (hPa).
126
+
127
+ Parameters
128
+ ----------
129
+ h : npt.NDArray[np.floating]
130
+ altitude, [:math:`m`]
131
+
132
+ Returns
133
+ -------
134
+ npt.NDArray[np.floating]
135
+ pressure level, [:math:`hPa`], [:math:`mbar`]
136
+
137
+ References
138
+ ----------
139
+ - :cite:`wikipediacontributorsBarometricFormula2023`
140
+
141
+ Notes
142
+ -----
143
+ See https://en.wikipedia.org/wiki/Barometric_formula
144
+
145
+ See Also
146
+ --------
147
+ m_to_T_isa
148
+ ft_to_pl
149
+ """
150
+ condlist = [h < constants.h_tropopause, h >= constants.h_tropopause]
151
+ funclist = [_low_altitude_m_to_pl, _high_altitude_m_to_pl, np.nan] # nan passed through
152
+ return np.piecewise(h, condlist, funclist)
153
+
154
+
155
+ def _low_altitude_pl_to_m(pl: npt.NDArray[np.floating]) -> npt.NDArray[np.floating]:
156
+ base = 100.0 * pl / constants.p_surface
157
+ exponent = -constants.T_lapse_rate * constants.R_d / constants.g
158
+ T_isa = constants.T_msl * base**exponent
159
+ return (T_isa - constants.T_msl) / constants.T_lapse_rate
160
+
161
+
162
+ def _high_altitude_pl_to_m(pl: npt.NDArray[np.floating]) -> npt.NDArray[np.floating]:
163
+ T_tropopause_isa = m_to_T_isa(np.asarray(constants.h_tropopause))
164
+ power_term = -constants.g / (constants.T_lapse_rate * constants.R_d)
165
+ p_tropopause_isa = constants.p_surface * (T_tropopause_isa / constants.T_msl) ** power_term
166
+ inside_exp = np.log(pl * 100.0 / p_tropopause_isa)
167
+ return inside_exp / (-constants.g / (constants.R_d * T_tropopause_isa)) + constants.h_tropopause
168
+
169
+
170
+ @support_arraylike
171
+ def pl_to_m(pl: npt.NDArray[np.floating]) -> npt.NDArray[np.floating]:
172
+ r"""Convert from pressure level (hPa) to altitude (m).
173
+
174
+ Function is slightly different from the classical formula:
175
+ ``constants.T_msl / 0.0065) * (1 - (pl_pa / constants.p_surface) ** (1 / 5.255)``
176
+ in order to provide a mathematical inverse to :func:`m_to_pl`.
177
+
178
+ For low altitudes (below the tropopause), this implementation closely agrees to classical
179
+ formula.
180
+
181
+ Parameters
182
+ ----------
183
+ pl : ArrayLike
184
+ pressure level, [:math:`hPa`], [:math:`mbar`]
185
+
186
+ Returns
187
+ -------
188
+ ArrayLike
189
+ altitude, [:math:`m`]
190
+
191
+ References
192
+ ----------
193
+ - :cite:`wikipediacontributorsBarometricFormula2023`
194
+
195
+ Notes
196
+ -----
197
+ See https://en.wikipedia.org/wiki/Barometric_formula
198
+
199
+ See Also
200
+ --------
201
+ pl_to_ft
202
+ m_to_pl
203
+ m_to_T_isa
204
+ """
205
+ pl_tropopause = m_to_pl(constants.h_tropopause)
206
+ condlist = [pl < pl_tropopause, pl >= pl_tropopause]
207
+ funclist = [_high_altitude_pl_to_m, _low_altitude_pl_to_m, np.nan] # nan passed through
208
+ return np.piecewise(pl, condlist, funclist)
209
+
210
+
211
+ def degrees_to_radians(degrees: ArrayScalarLike) -> ArrayScalarLike:
212
+ r"""Convert from degrees to radians.
213
+
214
+ Parameters
215
+ ----------
216
+ degrees : ArrayScalarLike
217
+ Degrees values, [:math:`\deg`]
218
+
219
+ Returns
220
+ -------
221
+ ArrayScalarLike
222
+ Radians values
223
+ """
224
+ return degrees * (np.pi / 180.0)
225
+
226
+
227
+ def radians_to_degrees(radians: ArrayScalarLike) -> ArrayScalarLike:
228
+ r"""Convert from radians to degrees.
229
+
230
+ Parameters
231
+ ----------
232
+ radians : ArrayScalarLike
233
+ degrees values, [:math:`\rad`]
234
+
235
+ Returns
236
+ -------
237
+ ArrayScalarLike
238
+ Radian values
239
+ """
240
+ return radians * (180.0 / np.pi)
241
+
242
+
243
+ def ft_to_m(ft: ArrayScalarLike) -> ArrayScalarLike:
244
+ """Convert length from feet to meter.
245
+
246
+ Parameters
247
+ ----------
248
+ ft : ArrayScalarLike
249
+ length, [:math:`ft`]
250
+
251
+ Returns
252
+ -------
253
+ ArrayScalarLike
254
+ length, [:math:`m`]
255
+ """
256
+ return ft * 0.3048
257
+
258
+
259
+ def m_to_ft(m: ArrayScalarLike) -> ArrayScalarLike:
260
+ """Convert length from meters to feet.
261
+
262
+ Parameters
263
+ ----------
264
+ m : ArrayScalarLike
265
+ length, [:math:`m`]
266
+
267
+ Returns
268
+ -------
269
+ ArrayScalarLike
270
+ length, [:math:`ft`]
271
+ """
272
+ return m / 0.3048
273
+
274
+
275
+ def m_per_s_to_knots(m_per_s: ArrayScalarLike) -> ArrayScalarLike:
276
+ r"""Convert speed from meters per second (m/s) to knots.
277
+
278
+ Parameters
279
+ ----------
280
+ m_per_s : ArrayScalarLike
281
+ Speed, [:math:`m \ s^{-1}`]
282
+
283
+ Returns
284
+ -------
285
+ ArrayScalarLike
286
+ Speed, [:math:`knots`]
287
+ """
288
+ return m_per_s / 0.514444
289
+
290
+
291
+ def knots_to_m_per_s(knots: ArrayScalarLike) -> ArrayScalarLike:
292
+ r"""Convert speed from knots to meters per second (m/s).
293
+
294
+ Parameters
295
+ ----------
296
+ knots : ArrayScalarLike
297
+ Speed, [:math:`knots`]
298
+
299
+ Returns
300
+ -------
301
+ ArrayScalarLike
302
+ Speed, [:math:`m \ s^{-1}`]
303
+ """
304
+ return knots * 0.514444
305
+
306
+
307
+ def longitude_distance_to_m(
308
+ distance_degrees: ArrayScalarLike, latitude_mean: ArrayScalarLike
309
+ ) -> ArrayScalarLike:
310
+ r"""
311
+ Convert longitude degrees distance between two points to cartesian distances in meters.
312
+
313
+ Parameters
314
+ ----------
315
+ distance_degrees : ArrayScalarLike
316
+ longitude distance, [:math:`\deg`]
317
+ latitude_mean : ArrayScalarLike, optional
318
+ mean latitude between ``longitude_1`` and ``longitude_2``, [:math:`\deg`]
319
+
320
+ Returns
321
+ -------
322
+ ArrayScalarLike
323
+ cartesian distance along the longitude axis, [:math:`m`]
324
+ """
325
+ latitude_mean_rad = degrees_to_radians(latitude_mean)
326
+ return (distance_degrees / 180.0) * np.pi * constants.radius_earth * np.cos(latitude_mean_rad)
327
+
328
+
329
+ def latitude_distance_to_m(distance_degrees: ArrayScalarLike) -> ArrayScalarLike:
330
+ r"""
331
+ Convert latitude degrees distance between two points to cartesian distances in meters.
332
+
333
+ Parameters
334
+ ----------
335
+ distance_degrees : ArrayScalarLike
336
+ latitude distance, [:math:`\deg`]
337
+
338
+ Returns
339
+ -------
340
+ ArrayScalarLike
341
+ Cartesian distance along the latitude axis, [:math:`m`]
342
+ """
343
+ return (distance_degrees / 180.0) * np.pi * constants.radius_earth
344
+
345
+
346
+ def m_to_longitude_distance(
347
+ distance_m: ArrayScalarLike, latitude_mean: ArrayScalarLike
348
+ ) -> ArrayScalarLike:
349
+ r"""
350
+ Convert cartesian distance (meters) to differences in longitude degrees.
351
+
352
+ Small angle approximation for ``distance_m`` << :attr:`constants.radius_earth`
353
+
354
+ Parameters
355
+ ----------
356
+ distance_m : ArrayScalarLike
357
+ cartesian distance along longitude axis, [:math:`m`]
358
+ latitude_mean : ArrayScalarLike
359
+ mean latitude between ``longitude_1`` and ``longitude_2``, [:math:`\deg`]
360
+
361
+ Returns
362
+ -------
363
+ ArrayScalarLike
364
+ longitude distance, [:math:`\deg`]
365
+ """
366
+ return radians_to_degrees(
367
+ distance_m / (constants.radius_earth * np.cos(degrees_to_radians(latitude_mean)))
368
+ )
369
+
370
+
371
+ def m_to_latitude_distance(distance_m: ArrayScalarLike) -> ArrayScalarLike:
372
+ r"""
373
+ Convert cartesian distance (meters) to differences in latitude degrees.
374
+
375
+ Small angle approximation for ``distance_m`` << :attr:`constants.radius_earth`
376
+
377
+ Parameters
378
+ ----------
379
+ distance_m : ArrayScalarLike
380
+ cartesian distance along latitude axis, [:math:`m`]
381
+
382
+ Returns
383
+ -------
384
+ ArrayScalarLike
385
+ latitude distance, [:math:`\deg`]
386
+ """
387
+ return radians_to_degrees(distance_m / constants.radius_earth)
388
+
389
+
390
+ def tas_to_mach_number(true_airspeed: ArrayScalarLike, T: ArrayScalarLike) -> ArrayScalarLike:
391
+ r"""Calculate Mach number from true airspeed at a specified ambient temperature.
392
+
393
+ Parameters
394
+ ----------
395
+ true_airspeed : ArrayScalarLike
396
+ True airspeed, [:math:`m \ s^{-1}`]
397
+ T : ArrayScalarLike
398
+ Ambient temperature, [:math:`K`]
399
+
400
+ Returns
401
+ -------
402
+ ArrayScalarLike
403
+ Mach number, [:math: `Ma`]
404
+
405
+ References
406
+ ----------
407
+ - :cite:`cumpstyJetPropulsion2015`
408
+ """
409
+ return true_airspeed / np.sqrt((constants.kappa * constants.R_d) * T)
410
+
411
+
412
+ def mach_number_to_tas(
413
+ mach_number: float | npt.NDArray[np.floating], T: float | npt.NDArray[np.floating]
414
+ ) -> float | npt.NDArray[np.floating]:
415
+ r"""Calculate true airspeed from the Mach number at a specified ambient temperature.
416
+
417
+ Parameters
418
+ ----------
419
+ mach_number : float | npt.NDArray[np.floating]
420
+ Mach number, [:math: `Ma`]
421
+ T : npt.NDArray[np.floating]
422
+ Ambient temperature, [:math:`K`]
423
+
424
+ Returns
425
+ -------
426
+ npt.NDArray[np.floating]
427
+ True airspeed, [:math:`m \ s^{-1}`]
428
+
429
+ References
430
+ ----------
431
+ - :cite:`cumpstyJetPropulsion2015`
432
+ """
433
+ return mach_number * np.sqrt((constants.kappa * constants.R_d) * T)
434
+
435
+
436
+ def lbs_to_kg(lbs: ArrayScalarLike) -> ArrayScalarLike:
437
+ r"""Convert mass from pounds (lbs) to kilograms (kg).
438
+
439
+ Parameters
440
+ ----------
441
+ lbs : ArrayScalarLike
442
+ mass, pounds [:math:`lbs`]
443
+
444
+ Returns
445
+ -------
446
+ ArrayScalarLike
447
+ mass, kilograms [:math:`kg`]
448
+ """
449
+ return lbs * 0.45359
450
+
451
+
452
+ def dt_to_seconds(
453
+ dt: npt.NDArray[np.timedelta64] | np.timedelta64,
454
+ dtype: npt.DTypeLike = np.float64,
455
+ ) -> npt.NDArray[np.floating]:
456
+ """Convert a time delta to seconds as a float with specified ``dtype`` precision.
457
+
458
+ Parameters
459
+ ----------
460
+ dt : np.ndarray
461
+ Time delta for each waypoint
462
+ dtype : np.dtype
463
+ Data type of the output array
464
+
465
+ Returns
466
+ -------
467
+ np.ndarray
468
+ Time delta in seconds as a float
469
+ """
470
+ out = np.empty(dt.shape, dtype=dtype)
471
+ np.divide(dt, np.timedelta64(1, "s"), out=out)
472
+ return out
pycontrails/py.typed ADDED
File without changes
@@ -0,0 +1 @@
1
+ """Misc re-usable utilities."""
@@ -0,0 +1,66 @@
1
+ """Raise ``ImportError`` when dependencies are not met."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import NoReturn
6
+
7
+
8
+ def raise_module_not_found_error(
9
+ name: str,
10
+ package_name: str,
11
+ module_not_found_error: ImportError,
12
+ pycontrails_optional_package: str | None = None,
13
+ extra: str | None = None,
14
+ ) -> NoReturn:
15
+ """Raise ``ImportError`` with a helpful message.
16
+
17
+ Parameters
18
+ ----------
19
+ name : str
20
+ The name describing the context of the ``ImportError``. For example,
21
+ if the module is required for a specific function, the name could be
22
+ "my_function function". If the module is required for a specific method,
23
+ the name could be "MyClass.my_method method". If the module is required
24
+ for an entire ``pycontrails`` module, the name could be "my_module module".
25
+ package_name : str
26
+ The name of the package that is required. This should be the full name of
27
+ the python package, which may be different from the name of the module
28
+ that is actually imported. For example, if ``import sklearn`` triggers
29
+ the ``ImportError``, the ``package_name`` should be "scikit-learn".
30
+ module_not_found_error : ImportError
31
+ The ``ImportError`` that was raised. This is passed to the
32
+ ``from`` clause of the ``raise`` statement below. The subclass of the
33
+ ``ImportError`` is preserved (e.g., ``ModuleNotFoundError`` or
34
+ ``ImportError``).
35
+ pycontrails_optional_package : str, optional
36
+ The name of the optional ``pycontrails`` package that can be used to
37
+ install the required package. See the ``pyproject.toml`` file.
38
+ extra : str, optional
39
+ Any extra information that should be included in the error message.
40
+ This is appended to the end of the error message.
41
+ """
42
+ # Put the function or method or module name in quotes if the full name
43
+ # contains a space.
44
+ try:
45
+ n1, n2 = name.split(" ")
46
+ except ValueError:
47
+ if "'" not in name:
48
+ name = f"'{name}'"
49
+ else:
50
+ if "'" not in n1:
51
+ n1 = f"'{n1}'"
52
+ name = f"{n1} {n2}"
53
+
54
+ msg = (
55
+ f"The {name} requires the '{package_name}' package. "
56
+ f"This can be installed with 'pip install {package_name}'"
57
+ )
58
+ if pycontrails_optional_package:
59
+ msg = f"{msg} or 'pip install pycontrails[{pycontrails_optional_package}]'."
60
+ else:
61
+ msg = f"{msg}."
62
+
63
+ if extra:
64
+ msg = f"{msg} {extra}"
65
+
66
+ raise type(module_not_found_error)(msg) from module_not_found_error
@@ -0,0 +1,13 @@
1
+ """Utilites for iterating of sequences."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from collections.abc import Iterator
6
+ from typing import Any
7
+
8
+
9
+ def chunk_list(lst: list[Any], n: int) -> Iterator[list[Any]]:
10
+ """Yield successive n-sized chunks from list."""
11
+
12
+ for i in range(0, len(lst), n):
13
+ yield lst[i : i + n]