pycontrails 0.54.6__cp310-cp310-macosx_11_0_arm64.whl → 0.54.7__cp310-cp310-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.

@@ -103,6 +103,8 @@ class CocipParams(AdvectionBuffers):
103
103
  #: Cocip output with ``preprocess_lowmem=True`` is only guaranteed to match output
104
104
  #: with ``preprocess_lowmem=False`` when run with ``interpolation_bounds_error=True``
105
105
  #: to ensure no out-of-bounds interpolation occurs.
106
+ #:
107
+ #: .. versionadded:: 0.52.3
106
108
  preprocess_lowmem: bool = False
107
109
 
108
110
  # --------------
@@ -114,6 +116,8 @@ class CocipParams(AdvectionBuffers):
114
116
  #: ``"auto"``, ``"tau_cirrus"`` will be computed during model initialization
115
117
  #: iff the met data is dask-backed. Otherwise, it will be computed during model
116
118
  #: evaluation after the met data is downselected.
119
+ #:
120
+ #: .. versionadded:: 0.47.1
117
121
  compute_tau_cirrus_in_model_init: bool | str = "auto"
118
122
 
119
123
  # ---------
@@ -152,10 +156,14 @@ class CocipParams(AdvectionBuffers):
152
156
  #: These are not standard CoCiP outputs but based on the derivation used
153
157
  #: in the first supplement to :cite:`yinPredictingClimateImpact2023`. ATR20 is defined
154
158
  #: as the average temperature response over a 20 year horizon.
159
+ #:
160
+ #: .. versionadded:: 0.50.0
155
161
  compute_atr20: bool = False
156
162
 
157
163
  #: Constant factor used to convert global- and year-mean RF, [:math:`W m^{-2}`],
158
164
  #: to ATR20, [:math:`K`], given by :cite:`yinPredictingClimateImpact2023`.
165
+ #:
166
+ #: .. versionadded:: 0.50.0
159
167
  global_rf_to_atr20_factor: float = 0.0151
160
168
 
161
169
  # ----------------
@@ -197,12 +205,13 @@ class CocipParams(AdvectionBuffers):
197
205
  max_depth: float = 1500.0
198
206
 
199
207
  #: Experimental. Improved ice crystal number survival fraction in the wake vortex phase.
200
- #: Implement :cite:`unterstrasserPropertiesYoungContrails2016`, who developed a
208
+ #: Implement :cite:`lottermoserHighResolutionEarlyContrails2025`, who developed a
201
209
  #: parametric model that estimates the survival fraction of the contrail ice crystal
202
210
  #: number after the wake vortex phase based on the results from large eddy simulations.
203
211
  #: This replicates Fig. 4 of :cite:`karcherFormationRadiativeForcing2018`.
204
212
  #:
205
213
  #: .. versionadded:: 0.50.1
214
+ #: .. versionchanged:: 0.54.7
206
215
  unterstrasser_ice_survival_fraction: bool = False
207
216
 
208
217
  #: Experimental. Radiative heating effects on contrail cirrus properties.
@@ -1,11 +1,16 @@
1
- """Wave-vortex downwash functions from Unterstrasser (2016).
1
+ """Wave-vortex downwash functions from Lottermoser & Unterstrasser (2025).
2
2
 
3
3
  Notes
4
4
  -----
5
5
  :cite:`unterstrasserPropertiesYoungContrails2016` provides a parameterized model of the
6
6
  survival fraction of the contrail ice crystal number ``f_surv`` during the wake-vortex phase.
7
- The model was developed based on output from large eddy simulations, and improves agreement with
8
- LES outputs relative to the default survival fraction parameterization used in CoCiP.
7
+ The model has since been updated in :cite:`lottermoserHighResolutionEarlyContrails2025`. This update
8
+ improves the goodness-of-fit between the parameterised model and LES, and expands the parameter
9
+ space and can now be used for very low and very high soot inputs, different fuel types (where the
10
+ EI H2Os are different), and higher ambient temperatures (up to 235 K) to accomodate for contrails
11
+ formed by liquid hydrogen aircraft. The model was developed based on output from large eddy
12
+ simulations, and improves agreement with LES outputs relative to the default survival fraction
13
+ parameterization used in CoCiP.
9
14
 
10
15
  For comparison, CoCiP assumes that ``f_surv`` is equal to the change in the contrail ice water
11
16
  content (by mass) before and after the wake vortex phase. However, for larger (smaller) ice
@@ -14,6 +19,9 @@ by mass. This is particularly important in the "soot-poor" scenario, for example
14
19
  lean-burn engines where their soot emissions can be 3-4 orders of magnitude lower than conventional
15
20
  RQL engines.
16
21
 
22
+ ADD CITATION TO BIBTEX: :cite:`lottermoserHighResolutionEarlyContrails2025`
23
+ Lottermoser, A. and Unterstraßer, S.: High-resolution modelling of early contrail evolution from
24
+ hydrogen-powered aircraft, EGUsphere [preprint], https://doi.org/10.5194/egusphere-2024-3859, 2025.
17
25
  """
18
26
 
19
27
  from __future__ import annotations
@@ -34,6 +42,8 @@ def ice_particle_number_survival_fraction(
34
42
  fuel_flow: npt.NDArray[np.floating],
35
43
  aei_n: npt.NDArray[np.floating],
36
44
  z_desc: npt.NDArray[np.floating],
45
+ *,
46
+ analytical_solution: bool = True,
37
47
  ) -> npt.NDArray[np.floating]:
38
48
  r"""
39
49
  Calculate fraction of ice particle number surviving the wake vortex phase and required inputs.
@@ -61,6 +71,8 @@ def ice_particle_number_survival_fraction(
61
71
  z_desc : npt.NDArray[np.floating]
62
72
  Final vertical displacement of the wake vortex, ``dz_max`` in :mod:`wake_vortex.py`,
63
73
  [:math:`m`].
74
+ analytical_solution : bool
75
+ Use analytical solution to calculate ``z_atm`` and ``z_emit`` instead of numerical solution.
64
76
 
65
77
  Returns
66
78
  -------
@@ -70,57 +82,114 @@ def ice_particle_number_survival_fraction(
70
82
  References
71
83
  ----------
72
84
  - :cite:`unterstrasserPropertiesYoungContrails2016`
85
+ - :cite:`lottermoserHighResolutionEarlyContrails2025`
73
86
 
74
87
  Notes
75
88
  -----
76
- - See eq. (3), (9), and (10) in :cite:`unterstrasserPropertiesYoungContrails2016`.
77
89
  - For consistency in CoCiP, ``z_desc`` should be calculated using :func:`dz_max` instead of
78
90
  using :func:`z_desc_length_scale`.
79
91
  """
80
- # Length scales
81
- z_atm = z_atm_length_scale(air_temperature, rhi_0)
82
92
  rho_emit = emitted_water_vapour_concentration(ei_h2o, wingspan, true_airspeed, fuel_flow)
83
- z_emit = z_emit_length_scale(rho_emit, air_temperature)
84
- z_total = z_total_length_scale(aei_n, z_atm, z_emit, z_desc)
93
+
94
+ # Length scales
95
+ if analytical_solution:
96
+ z_atm = z_atm_length_scale_analytical(air_temperature, rhi_0)
97
+ z_emit = z_emit_length_scale_analytical(rho_emit, air_temperature)
98
+
99
+ else:
100
+ z_atm = z_atm_length_scale_numerical(air_temperature, rhi_0)
101
+ z_emit = z_emit_length_scale_numerical(rho_emit, air_temperature)
102
+
103
+ z_total = z_total_length_scale(z_atm, z_emit, z_desc, true_airspeed, fuel_flow, aei_n, wingspan)
85
104
  return _survival_fraction_from_length_scale(z_total)
86
105
 
87
106
 
88
107
  def z_total_length_scale(
89
- aei_n: npt.NDArray[np.floating],
90
108
  z_atm: npt.NDArray[np.floating],
91
109
  z_emit: npt.NDArray[np.floating],
92
110
  z_desc: npt.NDArray[np.floating],
111
+ true_airspeed: npt.NDArray[np.floating],
112
+ fuel_flow: npt.NDArray[np.floating],
113
+ aei_n: npt.NDArray[np.floating],
114
+ wingspan: npt.NDArray[np.floating] | float,
93
115
  ) -> npt.NDArray[np.floating]:
94
116
  """
95
117
  Calculate the total length-scale effect of the wake vortex downwash.
96
118
 
97
119
  Parameters
98
120
  ----------
99
- aei_n : npt.NDArray[np.floating]
100
- Apparent ice crystal number emissions index at contrail formation, [:math:`kg^{-1}`]
101
121
  z_atm : npt.NDArray[np.floating]
102
122
  Length-scale effect of ambient supersaturation on the ice crystal mass budget, [:math:`m`]
103
123
  z_emit : npt.NDArray[np.floating]
104
124
  Length-scale effect of water vapour emissions on the ice crystal mass budget, [:math:`m`]
105
125
  z_desc : npt.NDArray[np.floating]
106
126
  Final vertical displacement of the wake vortex, `dz_max` in `wake_vortex.py`, [:math:`m`]
127
+ true_airspeed : npt.NDArray[np.floating]
128
+ true airspeed for each waypoint, [:math:`m s^{-1}`]
129
+ fuel_flow : npt.NDArray[np.floating]
130
+ Fuel mass flow rate, [:math:`kg s^{-1}`]
131
+ aei_n : npt.NDArray[np.floating]
132
+ Apparent ice crystal number emissions index at contrail formation, [:math:`kg^{-1}`]
133
+ wingspan : npt.NDArray[np.floating] | float
134
+ aircraft wingspan, [:math:`m`]
107
135
 
108
136
  Returns
109
137
  -------
110
138
  npt.NDArray[np.floating]
111
139
  Total length-scale effect of the wake vortex downwash, [:math:`m`]
140
+
141
+ Notes
142
+ -----
143
+ - For `psi`, see Appendix A1 in :cite:`lottermoserHighResolutionEarlyContrails2025`.
144
+ - For `z_total`, see Eq. (9) and (10) in :cite:`lottermoserHighResolutionEarlyContrails2025`.
145
+ """
146
+ # Calculate psi term
147
+ fuel_dist = fuel_flow / true_airspeed # Units: [:math:`kg m^{-1}`]
148
+ n_ice_dist = fuel_dist * aei_n # Units: [:math:`m^{-1}`]
149
+
150
+ n_ice_per_vol = n_ice_dist / plume_area(wingspan) # Units: [:math:`m^{-3}`]
151
+ n_ice_per_vol_ref = 3.38e12 / plume_area(60.3)
152
+
153
+ psi = (n_ice_per_vol_ref / n_ice_per_vol) ** 0.16
154
+
155
+ # Calculate total length-scale effect
156
+ return psi * (1.27 * z_atm + 0.42 * z_emit) - 0.49 * z_desc
157
+
158
+
159
+ def z_atm_length_scale_analytical(
160
+ air_temperature: npt.NDArray[np.floating],
161
+ rhi_0: npt.NDArray[np.floating],
162
+ ) -> npt.NDArray[np.floating]:
163
+ """Calculate the length-scale effect of ambient supersaturation on the ice crystal mass budget.
164
+
165
+ Parameters
166
+ ----------
167
+ air_temperature : npt.NDArray[np.floating]
168
+ Ambient temperature for each waypoint, [:math:`K`].
169
+ rhi_0 : npt.NDArray[np.floating]
170
+ Relative humidity with respect to ice at the flight waypoint.
171
+
172
+ Returns
173
+ -------
174
+ npt.NDArray[np.floating]
175
+ The effect of the ambient supersaturation on the ice crystal mass budget,
176
+ provided as a length scale equivalent, estimated with analytical fit [:math:`m`].
177
+
178
+ Notes
179
+ -----
180
+ - See Eq. (A2) in :cite:`lottermoserHighResolutionEarlyContrails2025`.
112
181
  """
113
- alpha_base = (aei_n / 2.8e14) ** (-0.18)
114
- alpha_atm = 1.7 * alpha_base
115
- alpha_emit = 1.15 * alpha_base
182
+ z_atm = np.zeros_like(rhi_0)
116
183
 
117
- z_total = alpha_atm * z_atm + alpha_emit * z_emit - 0.6 * z_desc
184
+ # Only perform operation when the ambient condition is supersaturated w.r.t. ice
185
+ issr = rhi_0 > 1.0
118
186
 
119
- z_total.clip(min=0.0, out=z_total)
120
- return z_total
187
+ s_i = rhi_0 - 1.0
188
+ z_atm[issr] = 607.46 * s_i[issr] ** 0.897 * (air_temperature[issr] / 205.0) ** 2.225
189
+ return z_atm
121
190
 
122
191
 
123
- def z_atm_length_scale(
192
+ def z_atm_length_scale_numerical(
124
193
  air_temperature: npt.NDArray[np.floating],
125
194
  rhi_0: npt.NDArray[np.floating],
126
195
  *,
@@ -141,11 +210,11 @@ def z_atm_length_scale(
141
210
  -------
142
211
  npt.NDArray[np.floating]
143
212
  The effect of the ambient supersaturation on the ice crystal mass budget,
144
- provided as a length scale equivalent, [:math:`m`].
213
+ provided as a length scale equivalent, estimated with numerical methods [:math:`m`].
145
214
 
146
215
  Notes
147
216
  -----
148
- - See eq. (5) in :cite:`unterstrasserPropertiesYoungContrails2016`.
217
+ - See Eq. (6) in :cite:`lottermoserHighResolutionEarlyContrails2025`.
149
218
  """
150
219
  # Only perform operation when the ambient condition is supersaturated w.r.t. ice
151
220
  issr = rhi_0 > 1.0
@@ -157,14 +226,14 @@ def z_atm_length_scale(
157
226
  # Did not use scipy functions because it is unstable when dealing with np.arrays
158
227
  z_1 = np.zeros_like(rhi_issr)
159
228
  z_2 = np.full_like(rhi_issr, 1000.0)
160
- lhs = rhi_issr * thermo.e_sat_ice(air_temperature_issr) / air_temperature_issr
229
+ lhs = rhi_issr * thermo.e_sat_ice(air_temperature_issr) / (air_temperature_issr**3.5)
161
230
 
162
231
  dry_adiabatic_lapse_rate = constants.g / constants.c_pd
163
232
  for _ in range(n_iter):
164
233
  z_est = 0.5 * (z_1 + z_2)
165
234
  rhs = (thermo.e_sat_ice(air_temperature_issr + dry_adiabatic_lapse_rate * z_est)) / (
166
235
  air_temperature_issr + dry_adiabatic_lapse_rate * z_est
167
- )
236
+ ) ** 3.5
168
237
  z_1[lhs > rhs] = z_est[lhs > rhs]
169
238
  z_2[lhs < rhs] = z_est[lhs < rhs]
170
239
 
@@ -207,7 +276,38 @@ def emitted_water_vapour_concentration(
207
276
  return h2o_per_dist / area_p
208
277
 
209
278
 
210
- def z_emit_length_scale(
279
+ def z_emit_length_scale_analytical(
280
+ rho_emit: npt.NDArray[np.floating],
281
+ air_temperature: npt.NDArray[np.floating],
282
+ ) -> npt.NDArray[np.floating]:
283
+ """Calculate the length-scale effect of water vapour emissions on the ice crystal mass budget.
284
+
285
+ Parameters
286
+ ----------
287
+ rho_emit : npt.NDArray[np.floating] | float
288
+ Aircraft-emitted water vapour concentration in the plume, [:math:`kg m^{-3}`]
289
+ air_temperature : npt.NDArray[np.floating]
290
+ ambient temperature for each waypoint, [:math:`K`]
291
+
292
+ Returns
293
+ -------
294
+ npt.NDArray[np.floating]
295
+ The effect of the aircraft water vapour emission on the ice crystal mass budget,
296
+ provided as a length scale equivalent, estimated with analytical fit [:math:`m`]
297
+
298
+ Notes
299
+ -----
300
+ - See Eq. (A3) in :cite:`lottermoserHighResolutionEarlyContrails2025`.
301
+ """
302
+ t_205 = air_temperature - 205.0
303
+ return (
304
+ 1106.6
305
+ * ((rho_emit * 1e5) ** (0.678 + 0.0116 * t_205))
306
+ * np.exp(-(0.0807 + 0.000428 * t_205) * t_205)
307
+ )
308
+
309
+
310
+ def z_emit_length_scale_numerical(
211
311
  rho_emit: npt.NDArray[np.floating],
212
312
  air_temperature: npt.NDArray[np.floating],
213
313
  *,
@@ -228,24 +328,26 @@ def z_emit_length_scale(
228
328
  -------
229
329
  npt.NDArray[np.floating]
230
330
  The effect of the aircraft water vapour emission on the ice crystal mass budget,
231
- provided as a length scale equivalent, [:math:`m`]
331
+ provided as a length scale equivalent, estimated with numerical methods [:math:`m`]
232
332
 
233
333
  Notes
234
334
  -----
235
- - See eq. (7) in :cite:`unterstrasserPropertiesYoungContrails2016`.
335
+ - See Eq. (7) in :cite:`lottermoserHighResolutionEarlyContrails2025`.
236
336
  """
237
337
  # Solve non-linear equation numerically using the bisection method
238
338
  # Did not use scipy functions because it is unstable when dealing with np.arrays
239
339
  z_1 = np.zeros_like(rho_emit)
240
340
  z_2 = np.full_like(rho_emit, 1000.0)
241
341
 
242
- lhs = (thermo.e_sat_ice(air_temperature) / (constants.R_v * air_temperature)) + rho_emit
342
+ lhs = (thermo.e_sat_ice(air_temperature) / (constants.R_v * air_temperature**3.5)) + (
343
+ rho_emit / (air_temperature**2.5)
344
+ )
243
345
 
244
346
  dry_adiabatic_lapse_rate = constants.g / constants.c_pd
245
347
  for _ in range(n_iter):
246
348
  z_est = 0.5 * (z_1 + z_2)
247
349
  rhs = thermo.e_sat_ice(air_temperature + dry_adiabatic_lapse_rate * z_est) / (
248
- constants.R_v * (air_temperature + dry_adiabatic_lapse_rate * z_est)
350
+ constants.R_v * (air_temperature + dry_adiabatic_lapse_rate * z_est) ** 3.5
249
351
  )
250
352
  z_1[lhs > rhs] = z_est[lhs > rhs]
251
353
  z_2[lhs < rhs] = z_est[lhs < rhs]
@@ -268,10 +370,10 @@ def plume_area(wingspan: npt.NDArray[np.floating] | float) -> npt.NDArray[np.flo
268
370
 
269
371
  Notes
270
372
  -----
271
- - See eq. (A6) and (A7) in :cite:`unterstrasserPropertiesYoungContrails2016`.
373
+ - See Appendix A2 in eq. (A6) and (A7) in :cite:`lottermoserHighResolutionEarlyContrails2025`.
272
374
  """
273
375
  r_plume = 1.5 + 0.314 * wingspan
274
- return 2.0 * 2.0 * np.pi * r_plume**2
376
+ return 2.0 * np.pi * r_plume**2
275
377
 
276
378
 
277
379
  def z_desc_length_scale(
@@ -368,7 +470,7 @@ def _survival_fraction_from_length_scale(
368
470
  npt.NDArray[np.floating]
369
471
  Fraction of ice particle number surviving the wake vortex phase
370
472
  """
371
- f_surv = 0.45 + (1.19 / np.pi) * np.arctan(-1.35 + (z_total / 100.0))
473
+ f_surv = 0.42 + (1.31 / np.pi) * np.arctan(-1.00 + (z_total / 100.0))
372
474
  np.clip(f_surv, 0.0, 1.0, out=f_surv)
373
475
  return f_surv
374
476
 
@@ -88,6 +88,9 @@ class CocipGrid(models.Model):
88
88
  met_variables = cocip.Cocip.met_variables
89
89
  rad_variables = cocip.Cocip.rad_variables
90
90
  processed_met_variables = cocip.Cocip.processed_met_variables
91
+ generic_rad_variables = cocip.Cocip.generic_rad_variables
92
+ ecmwf_rad_variables = cocip.Cocip.ecmwf_rad_variables
93
+ gfs_rad_variables = cocip.Cocip.gfs_rad_variables
91
94
 
92
95
  #: Met data is not optional
93
96
  met: MetDataset
@@ -98,7 +98,7 @@ def flame_temperature(t_3: ArrayScalarLike) -> ArrayScalarLike:
98
98
  ArrayScalarLike
99
99
  Flame temperature at the combustion chamber, [:math:`K`]
100
100
  """
101
- return 0.9 * t_3 + 2120
101
+ return 0.9 * t_3 + 2120.0
102
102
 
103
103
 
104
104
  def bc_mass_concentration_fox(
@@ -125,7 +125,11 @@ def bc_mass_concentration_fox(
125
125
  npt.NDArray[np.floating]:
126
126
  Black carbon mass concentration for ground conditions, [:math:`mg m^{-3}`]
127
127
  """
128
- return fuel_flow * (356 * np.exp(-6390 / t_fl) - 608 * afr * np.exp(-19778 / t_fl))
128
+ # avoid float32 -> float64 promotion
129
+ coeff = 356.0 * np.exp(np.float32(-6390.0) / t_fl) - 608.0 * afr * np.exp(
130
+ np.float32(-19778.0) / t_fl
131
+ )
132
+ return fuel_flow * coeff
129
133
 
130
134
 
131
135
  def bc_mass_concentration_cruise_fox(
@@ -209,7 +213,7 @@ def dopelheuer_lecht_scaling_factor(
209
213
  ----------
210
214
  - :cite:`dopelheuerInfluenceEnginePerformance1998`
211
215
  """
212
- exp_term = np.exp(20000 / t_fl_cru) / np.exp(20000 / t_fl_ref)
216
+ exp_term = np.exp(20000.0 / t_fl_cru - 20000.0 / t_fl_ref)
213
217
  return (afr_ref / afr_cru) ** 2.5 * (p_3_cru / p_3_ref) ** 1.35 * exp_term
214
218
 
215
219
 
@@ -295,7 +299,7 @@ def turbine_inlet_temperature_imfox(afr: npt.NDArray[np.floating]) -> npt.NDArra
295
299
  ----------
296
300
  - :cite:`abrahamsonPredictiveModelDevelopment2016`
297
301
  """
298
- return 490 + 42266 / afr
302
+ return 490.0 + 42266.0 / afr
299
303
 
300
304
 
301
305
  def bc_mass_concentration_imfox(
@@ -325,9 +329,10 @@ def bc_mass_concentration_imfox(
325
329
  npt.NDArray[np.floating]
326
330
  Black carbon mass concentration, [:math:`mg m^{-3}`]
327
331
  """
328
- exp_term = np.exp(13.6 - fuel_hydrogen)
329
- formation_term = 295 * np.exp(-6390 / t_4)
330
- oxidation_term = 608 * afr * np.exp(-19778 / t_4)
332
+ # avoid float32 -> float64 promotion
333
+ exp_term = np.exp(np.float32(13.6) - fuel_hydrogen)
334
+ formation_term = 295.0 * np.exp(np.float32(-6390.0) / t_4)
335
+ oxidation_term = 608.0 * afr * np.exp(np.float32(-19778.0) / t_4)
331
336
  return fuel_flow_per_engine * exp_term * (formation_term - oxidation_term)
332
337
 
333
338
 
@@ -453,11 +458,11 @@ def number_emissions_index_fractal_aggregates(
453
458
  nvpm_ei_m: npt.NDArray[np.floating],
454
459
  gmd: npt.NDArray[np.floating],
455
460
  *,
456
- gsd: float | npt.NDArray[np.floating] = 1.80,
457
- rho_bc: float = 1770,
458
- k_tem: float = 1.621e-5,
459
- d_tem: float = 0.39,
460
- d_fm: float = 2.76,
461
+ gsd: float | np.floating | npt.NDArray[np.floating] = np.float32(1.80), # avoid promotion
462
+ rho_bc: float | np.floating = np.float32(1770.0),
463
+ k_tem: float | np.floating = np.float32(1.621e-5),
464
+ d_tem: float | np.floating = np.float32(0.39),
465
+ d_fm: float | np.floating = np.float32(2.76),
461
466
  ) -> npt.NDArray[np.floating]:
462
467
  """
463
468
  Estimate the black carbon number emission index using the fractal aggregates (FA) model.
@@ -494,9 +499,9 @@ def number_emissions_index_fractal_aggregates(
494
499
  - ``rho_bc``: :cite:`parkMeasurementInherentMaterial2004`
495
500
  - ``k_tem``, ``d_tem``: :cite:`dastanpourObservationsCorrelationPrimary2014`
496
501
  """
497
- phi = 3 * d_tem + (1 - d_tem) * d_fm
502
+ phi = 3.0 * d_tem + (1.0 - d_tem) * d_fm
498
503
  exponential_term = np.exp(0.5 * phi**2 * np.log(gsd) ** 2)
499
- denom = rho_bc * (np.pi / 6) * k_tem ** (3 - d_fm) * gmd**phi * exponential_term
504
+ denom = rho_bc * (np.pi / 6.0) * k_tem ** (3.0 - d_fm) * gmd**phi * exponential_term
500
505
  return nvpm_ei_m / denom
501
506
 
502
507
 
@@ -169,7 +169,7 @@ class Emissions(Model):
169
169
  )
170
170
 
171
171
  if "n_engine" not in self.source.attrs:
172
- aircraft_type = self.source.attrs.get("aircraft_type")
172
+ aircraft_type = self.source.get_constant("aircraft_type", None)
173
173
  self.source.attrs["n_engine"] = self.default_engines.at[aircraft_type, "n_engine"]
174
174
 
175
175
  try:
@@ -192,7 +192,7 @@ class Emissions(Model):
192
192
  try:
193
193
  edb_gaseous = self.edb_engine_gaseous[engine_uid] # type: ignore[index]
194
194
  except KeyError:
195
- self.source["thrust_setting"] = np.full(shape=len(self.source), fill_value=np.nan)
195
+ self.source["thrust_setting"] = np.full(len(self.source), np.nan, dtype=np.float32)
196
196
  else:
197
197
  self.source["thrust_setting"] = get_thrust_setting(
198
198
  edb_gaseous,
@@ -297,9 +297,9 @@ class Emissions(Model):
297
297
  """
298
298
  self.source.attrs["gaseous_data_source"] = "Constant"
299
299
 
300
- nox_ei = np.full(shape=len(self.source), fill_value=15.14)
301
- co_ei = np.full(shape=len(self.source), fill_value=3.61)
302
- hc_ei = np.full(shape=len(self.source), fill_value=0.520)
300
+ nox_ei = np.full(shape=len(self.source), fill_value=15.14, dtype=np.float32)
301
+ co_ei = np.full(shape=len(self.source), fill_value=3.61, dtype=np.float32)
302
+ hc_ei = np.full(shape=len(self.source), fill_value=0.520, dtype=np.float32)
303
303
 
304
304
  self.source["nox_ei"] = nox_ei * 1e-3 # g-NOx/kg-fuel to kg-NOx/kg-fuel
305
305
  self.source["co_ei"] = co_ei * 1e-3 # g-CO/kg-fuel to kg-CO/kg-fuel
@@ -493,8 +493,8 @@ class Emissions(Model):
493
493
  - :cite:`schumannDehydrationEffectsContrails2015`
494
494
  """
495
495
  nvpm_data_source = "Constant"
496
- nvpm_ei_m = np.full(shape=len(self.source), fill_value=(0.088 * 1e-3)) # g to kg
497
- nvpm_ei_n = np.full(shape=len(self.source), fill_value=self.params["default_nvpm_ei_n"])
496
+ nvpm_ei_m = np.full(len(self.source), 0.088 * 1e-3, dtype=np.float32) # g to kg
497
+ nvpm_ei_n = np.full(len(self.source), self.params["default_nvpm_ei_n"], dtype=np.float32)
498
498
  return nvpm_data_source, nvpm_ei_m, nvpm_ei_n
499
499
 
500
500
  def _total_pollutant_emissions(self) -> None:
@@ -916,7 +916,7 @@ def get_thrust_setting(
916
916
  )
917
917
 
918
918
  thrust_setting = fuel_flow_per_engine / edb_gaseous.ff_100
919
- thrust_setting.clip(0.03, 1, out=thrust_setting) # clip in place
919
+ thrust_setting.clip(0.03, 1.0, out=thrust_setting) # clip in place
920
920
  return thrust_setting
921
921
 
922
922
 
@@ -121,7 +121,7 @@ class PSFlight(AircraftPerformance):
121
121
  def eval_flight(self, fl: Flight) -> Flight:
122
122
  # Ensure aircraft type is available
123
123
  try:
124
- aircraft_type = fl.attrs["aircraft_type"]
124
+ aircraft_type = fl.get_constant("aircraft_type")
125
125
  except KeyError as exc:
126
126
  msg = "`aircraft_type` required on flight attrs"
127
127
  raise KeyError(msg) from exc
@@ -112,5 +112,6 @@ c_r: float = 0.9
112
112
  # Flight
113
113
  # ------
114
114
 
115
- #: Nominal rate of climb/descent of aircraft [:math:`m \ s^{-1}``]
115
+ #: Nominal rate of climb/descent of aircraft [:math:`m \ s^{-1}`].
116
+ #: Note [:math:`12.7 m \ s^{-1} = 2500 ft \ min^{-1}`].
116
117
  nominal_rocd: float = 12.7
@@ -1,8 +1,8 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: pycontrails
3
- Version: 0.54.6
3
+ Version: 0.54.7
4
4
  Summary: Python library for modeling aviation climate impacts
5
- Author-email: Breakthrough Energy <py@contrails.org>
5
+ Author-email: "Contrails.org" <py@contrails.org>
6
6
  License: Apache-2.0
7
7
  Project-URL: Changelog, https://py.contrails.org/changelog.html
8
8
  Project-URL: Documentation, https://py.contrails.org
@@ -29,7 +29,7 @@ License-File: LICENSE
29
29
  License-File: NOTICE
30
30
  Requires-Dist: dask>=2022.3
31
31
  Requires-Dist: numpy>=1.22
32
- Requires-Dist: pandas>=2.2
32
+ Requires-Dist: pandas>=2.0
33
33
  Requires-Dist: scipy>=1.10
34
34
  Requires-Dist: typing-extensions>=4.5; python_version < "3.12"
35
35
  Requires-Dist: xarray>=2022.3
@@ -1,4 +1,4 @@
1
- Copyright (c) 2021-present Breakthrough Energy
1
+ Copyright (c) 2021-present Contrails.org and the Breakthrough Energy Foundation
2
2
 
3
3
 
4
4
  Attribution
@@ -1,31 +1,30 @@
1
- pycontrails-0.54.6.dist-info/RECORD,,
2
- pycontrails-0.54.6.dist-info/LICENSE,sha256=gJ-h7SFFD1mCfR6a7HILvEtodDT6Iig8bLXdgqR6ucA,10175
3
- pycontrails-0.54.6.dist-info/WHEEL,sha256=ezfKMaDztqf77C8lvQ0NCnZxkTaOaKLprqJ8q932MhU,109
4
- pycontrails-0.54.6.dist-info/NOTICE,sha256=TeY5lUhEbf5ouzABkrRUsUDJzS5v9jDEYADpwku4mgA,1929
5
- pycontrails-0.54.6.dist-info/top_level.txt,sha256=Z8J1R_AiBAyCVjNw6jYLdrA68PrQqTg0t3_Yek_IZ0Q,29
6
- pycontrails-0.54.6.dist-info/METADATA,sha256=M7bIMAsry7V5uDqs2GHOi2g-gN3Q0L-24BywhVyTAeM,9113
7
- pycontrails/_version.py,sha256=3tdcrURWC_cFIzJJ0AUbLpq4j7N9WBPvv2IFchdGJSw,413
8
- pycontrails/__init__.py,sha256=vomSUHQ1d7ru2-3LgnmczzuPJw0kHmBSaCSVOpqvEtQ,2004
1
+ pycontrails-0.54.7.dist-info/RECORD,,
2
+ pycontrails-0.54.7.dist-info/LICENSE,sha256=gJ-h7SFFD1mCfR6a7HILvEtodDT6Iig8bLXdgqR6ucA,10175
3
+ pycontrails-0.54.7.dist-info/WHEEL,sha256=ezfKMaDztqf77C8lvQ0NCnZxkTaOaKLprqJ8q932MhU,109
4
+ pycontrails-0.54.7.dist-info/NOTICE,sha256=fiBPdjYibMpDzf8hqcn7TvAQ-yeK10q_Nqq24DnskYg,1962
5
+ pycontrails-0.54.7.dist-info/top_level.txt,sha256=Z8J1R_AiBAyCVjNw6jYLdrA68PrQqTg0t3_Yek_IZ0Q,29
6
+ pycontrails-0.54.7.dist-info/METADATA,sha256=DFXvJSrBL-pJrxfU5pLgCxGr0B4dCPNll7OmAZWjbuY,9109
7
+ pycontrails/_version.py,sha256=xFXeQmKQBcVm0YCwIVk_9_pSpczqhMiPo8kIlvHU_lI,413
8
+ pycontrails/__init__.py,sha256=9ypSB2fKZlKghTvSrjWo6OHm5qfASwiTIvlMew3Olu4,2037
9
9
  pycontrails/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10
- pycontrails/core/vector.py,sha256=N-B-VSNX5bz-FwzSRBdbO3fZ1A8Xh3odbTs_6rnZty4,71263
11
- pycontrails/core/models.py,sha256=5ppjLNbxWvS3OfiWfhfVDWjbjw5YhOPqFMedOpV4tBk,39808
10
+ pycontrails/core/vector.py,sha256=IO7Xq63JdxbjH4h7RMJkepOi7-E568ZuxM8DeYvtnL4,73240
11
+ pycontrails/core/models.py,sha256=iuGy9pQU-OI-AqPQyROxshSn2LbLcKNjXQcMEZw2IzA,42340
12
12
  pycontrails/core/interpolation.py,sha256=wovjj3TAf3xonVxjarclpvZLyLq6N7wZQQXsI9hT3YA,25713
13
13
  pycontrails/core/fleet.py,sha256=0hi_N4R93St-7iD29SE0EnadpBEl_p9lSGtDwpWvGkk,16704
14
- pycontrails/core/rgi_cython.cpython-310-darwin.so,sha256=uP9oUFVqvmdnb7gVDDqmiytOshTG7yRIfvBllrSUZ9w,310832
15
- pycontrails/core/flight.py,sha256=Eba7XmfjGEoFSdWJfSnOmWknAU0c0PPNZtTYrb3u_m4,80659
14
+ pycontrails/core/rgi_cython.cpython-310-darwin.so,sha256=BQIOMfcT50pSkkC1lZ_IAMqPo45gkYE2QEZTGFd7q4c,310832
15
+ pycontrails/core/flight.py,sha256=QZTGeZVnZ14UUWHSqgCSU49g_EGQZel-hzKwm_9dcFY,80653
16
16
  pycontrails/core/fuel.py,sha256=kJZ3P1lPm1L6rdPREM55XQ-VfJ_pt35cP4sO2Nnvmjs,4332
17
17
  pycontrails/core/polygon.py,sha256=EmfHPj0e58whsHvR-3YvDgMWkvMFgp_BgwaoG8IZ4n0,18044
18
18
  pycontrails/core/cache.py,sha256=XG_RCIemv1xatDBmaVyxnoYdi8gD2gkUvjfvRi9RsJA,28068
19
19
  pycontrails/core/__init__.py,sha256=p0O09HxdeXU0X5Z3zrHMlTfXa92YumT3fJ8wJBI5ido,856
20
20
  pycontrails/core/flightplan.py,sha256=_7j4puAMiSe2aqHXcENR58c-N8crjUp4nbi3O2u7Adg,7335
21
- pycontrails/core/met.py,sha256=5b2K4PQWcAO5X8Mb3LLJ224_06WDyDEGUIeE3_EB3zA,103599
22
- pycontrails/core/aircraft_performance.py,sha256=quODa49uv0DVwl-qEYQ2ePLeapOD0GjyNBO3HoSD3M0,28004
23
- pycontrails/core/airports.py,sha256=3IGP337nqzXAXktT7Ju95i0I8GDVwHMlCaCv2bs4Kkk,6738
24
- pycontrails/core/met_var.py,sha256=U3q5Ddc1wN5EtT7a5Iy5lixCT3Mmv75C9wScY9MLnc8,11544
21
+ pycontrails/core/met.py,sha256=l2ORveZxCMGsV2Db74xbn_QIuXKMdh5WpFtCZcDtWzY,103734
22
+ pycontrails/core/aircraft_performance.py,sha256=ww5YBZkCPiUxRZERI5bUmxBeiF6rIrS2AsZyv8mVjvE,27400
23
+ pycontrails/core/airports.py,sha256=ubYo-WvxKPd_dUcADx6yew9Tqh1a4VJDgX7aFqLYwB8,6775
24
+ pycontrails/core/met_var.py,sha256=lAbp3cko_rzMk_u0kq-F27sUXUxUKikUvCNycwp9ILY,12020
25
25
  pycontrails/core/coordinates.py,sha256=0ySsHtqTon7GMbuwmmxMbI92j3ueMteJZh4xxNm5zto,5391
26
26
  pycontrails/datalib/goes.py,sha256=mCEuDYdt1GIBA-sbDq5LdC6ZRvWJ28uaaBTnsXE4syc,26555
27
27
  pycontrails/datalib/landsat.py,sha256=r6366rEF7fOA7mT5KySCPGJplgGE5LvBw5fMqk-U1oM,19697
28
- pycontrails/datalib/spire.py,sha256=66SnMdA8KOS69USjKmqrJmTKPK08Ehih9tnlsCt-AJw,25331
29
28
  pycontrails/datalib/__init__.py,sha256=hW9NWdFPC3y_2vHMteQ7GgQdop3917MkDaf5ZhU2RBY,369
30
29
  pycontrails/datalib/sentinel.py,sha256=hYSxIlQnyJHqtHWlKn73HOK_1pm-_IbGebmkHnh4UcA,17172
31
30
  pycontrails/datalib/_met_utils/metsource.py,sha256=omgrBrAap11G5hV8a9qS3umJVuwoX_Mca6QctRa6xn8,24116
@@ -46,6 +45,9 @@ pycontrails/datalib/_leo_utils/static/bq_roi_query.sql,sha256=xq6-tJyz0-bUwW0KjQ
46
45
  pycontrails/datalib/gfs/gfs.py,sha256=3tFiR7IObHcFmhGOdb-SJ7QQJSk6tF_6qkyi-pLrIdE,22393
47
46
  pycontrails/datalib/gfs/variables.py,sha256=4ALR4zhYW8tQVlNVHrd0CK8oRNSe_2OkW3ELeaImtAI,3135
48
47
  pycontrails/datalib/gfs/__init__.py,sha256=pXNjb9cJC6ngpuCnoHnmVZ2RHzbHZ0AlsyGvgcdcl2E,684
48
+ pycontrails/datalib/spire/spire.py,sha256=jDNlkgytLw6pO1YMMFwcxRaeFLsuct8lk63XuWr0lqg,24391
49
+ pycontrails/datalib/spire/__init__.py,sha256=3-My8yQItS6PL0DqXgNaltLqvN6T7nbnNnLD-sy7kt4,186
50
+ pycontrails/datalib/spire/exceptions.py,sha256=U0V_nZTLhxJwrzldvU9PdESx8-zLddRH3FmzkJyFyrI,1714
49
51
  pycontrails/ext/synthetic_flight.py,sha256=ByuJDfpuK5WaGMj41wflfzH6zwI1nejVcQXC4JoMvSI,16795
50
52
  pycontrails/ext/cirium.py,sha256=DFPfRwLDwddpucAPRQhyT4bDGh0VvvoViMUd3pidam8,415
51
53
  pycontrails/ext/empirical_grid.py,sha256=FPNQA0x4nVwBXFlbs3DgIapSrXFYhoc8b8IX0M4xhBc,4363
@@ -61,13 +63,13 @@ pycontrails/models/tau_cirrus.py,sha256=0K7cdHBGaahuWDM0FG1HGqbvhkwyWKHMInwctkBW
61
63
  pycontrails/models/__init__.py,sha256=dQTOLQb7RdUdUwslt5se__5y_ymbInBexQmNrmAeOdE,33
62
64
  pycontrails/models/issr.py,sha256=AYLYLHxtG8je5UG6x1zLV0ul89MJPqe5Xk0oWIyZ7b0,7378
63
65
  pycontrails/models/sac.py,sha256=lV1Or0AaLxuS1Zo5V8h5c1fkSKC-hKEgiFm7bmmusWw,15946
64
- pycontrails/models/accf.py,sha256=meIcgojYvHgm3de9iro2Bv0M4y9ta5VPwcqGAMEbBp8,13663
66
+ pycontrails/models/accf.py,sha256=egdBa4_G3BUaoUQYWvVlTlAIWpLEuNdtCxlK3eckLOc,13599
65
67
  pycontrails/models/dry_advection.py,sha256=FqUvRFbnwe4esHBYDayn3iu7R2UUuaQwY8x2oToxNI0,19164
66
68
  pycontrails/models/pcr.py,sha256=ZzbEuTOuDdUmmL5T3Wk3HL-O8XzX3HMnn98WcPbASaU,5348
67
69
  pycontrails/models/emissions/__init__.py,sha256=CZB2zIkLUI3NGNmq2ddvRYjEtiboY6PWJjiEiXj_zII,478
68
70
  pycontrails/models/emissions/ffm2.py,sha256=mAvBHnp-p3hIn2fjKGq50eaMHi0jcb5hA5uXbJGeE9I,12068
69
- pycontrails/models/emissions/emissions.py,sha256=iAUJQFVzFDllKlDRVzIEZRm18IryFQedbzq3h2GmFcQ,47691
70
- pycontrails/models/emissions/black_carbon.py,sha256=HV56zz5-9nrIxDqL2-F6aa7XQYZyv26IDkLHo_Jop9A,20346
71
+ pycontrails/models/emissions/emissions.py,sha256=qt689n4MuWw4XZ0NneBTNR2RqVG3XRYafgkkdSc1yEw,47757
72
+ pycontrails/models/emissions/black_carbon.py,sha256=d0CCQz_-W4Pne-ywS_Rc2plMAe4Azb0jT0QCii3K4aE,20695
71
73
  pycontrails/models/emissions/static/edb-nvpm-v29b-engines.csv,sha256=NatpVI1D2tTDLK7uVvlanm9DhfFB44nmFA4aocUcXco,77318
72
74
  pycontrails/models/emissions/static/edb-gaseous-v29b-engines.csv,sha256=jCjt7cP6sqLdbDp5NUoaqllVkZNE7NJtSnbB3rX_zQI,127523
73
75
  pycontrails/models/emissions/static/default-engine-uids.csv,sha256=3blb0aqtM8YRsyT1WDo0UYTBtv1h4BwXRIC_Ll9fhnI,6217
@@ -82,17 +84,17 @@ pycontrails/models/humidity_scaling/quantiles/era5-pressure-level-quantiles.pq,s
82
84
  pycontrails/models/humidity_scaling/quantiles/era5-model-level-quantiles.pq,sha256=pShCvNUo0NYtAHhT9IBRuj38X9jejdlKfv-ZoOKmtKI,35943
83
85
  pycontrails/models/cocip/radiative_forcing.py,sha256=0zTXQXANYC3oIDyYEUHkaJxuwTe0-GojxBipEutWGxU,45031
84
86
  pycontrails/models/cocip/wind_shear.py,sha256=m6ZlWjORfI-lI-D74Z_dIMOHnK4FDYmkb0S6vSpKTO8,3868
85
- pycontrails/models/cocip/cocip.py,sha256=QbbgSPEI2Lw8mftaxznpUgdNytnMk7O89VsTKBDvQ2w,102407
87
+ pycontrails/models/cocip/cocip.py,sha256=LXopyTZsi7QjOJLmoI9uKXeyvt-JU1jkiAgm_G_askQ,103756
86
88
  pycontrails/models/cocip/output_formats.py,sha256=YXYfm32NsI3OkZn4htAOceMIDG31ulehUiMUCqu8hEQ,83713
87
89
  pycontrails/models/cocip/__init__.py,sha256=CWrkNd6S3ZJq04pjTc2W22sVAJeJD3bJJRy_zLW8Kkc,962
88
- pycontrails/models/cocip/cocip_params.py,sha256=rwVW1SnjKnzztlxcxxaCztgzRbIPkMJqC2GefUKQZ6w,12341
90
+ pycontrails/models/cocip/cocip_params.py,sha256=34_F7mXyJpSfek7iRhLVj6JaZeSoFmfcxx2WmmZN42Q,12534
89
91
  pycontrails/models/cocip/wake_vortex.py,sha256=YmOuv_oWJ9-fmTx9PVHr6gsXwex0qzLhvoZIJNB9rsk,14515
90
92
  pycontrails/models/cocip/cocip_uncertainty.py,sha256=XFWYIEVcmbOO9bP7BMdQXbCiQa0OSvfyr71CWtNON6E,12237
91
93
  pycontrails/models/cocip/radiative_heating.py,sha256=1U4SQWwogtyQ2u6J996kAHP0OfpZ3hH2_x4Cyt3Cy8U,18984
92
94
  pycontrails/models/cocip/contrail_properties.py,sha256=H0D2FQodX6jW3EmAxQNxGS8erOU0EW2MSAAOLB0LyQc,56202
93
- pycontrails/models/cocip/unterstrasser_wake_vortex.py,sha256=M6tdl5ZfNDtiLwJOrXg3sBesf6Larg-5JchsVlJNsG4,14675
95
+ pycontrails/models/cocip/unterstrasser_wake_vortex.py,sha256=edMHuWKzFN1P4EMWC2HRv5ZS_rUI7Q5Nw3LsYkrI0mE,18936
94
96
  pycontrails/models/ps_model/__init__.py,sha256=Fuum5Rq8ya8qkvbeq2wh6NDo-42RCRnK1Y-2syYy0Ck,553
95
- pycontrails/models/ps_model/ps_model.py,sha256=e-rd3b61futqHFQPXbOj3o-7hIuxHZH0fUzmYI4JDow,32255
97
+ pycontrails/models/ps_model/ps_model.py,sha256=svv9_T5rYDhFRXdnod4xfia1HPQCb9vG9z3fHGjkABU,32262
96
98
  pycontrails/models/ps_model/ps_aircraft_params.py,sha256=pD1xpTBX6ML2Pie78kypNibzE5AkvqnAIaTyEMfciuY,13350
97
99
  pycontrails/models/ps_model/ps_operational_limits.py,sha256=XwMHO8yu8EZUWtxRgjRKwxmCrmKGoHO7Ob6nlfkrthI,16441
98
100
  pycontrails/models/ps_model/ps_grid.py,sha256=DTUXTxYIQ-6iquiCGtScOqkPvakz9F57DxUHQ3JmXIA,26071
@@ -100,10 +102,10 @@ pycontrails/models/ps_model/static/ps-synonym-list-20240524.csv,sha256=ksrpQTHkx
100
102
  pycontrails/models/ps_model/static/ps-aircraft-params-20240524.csv,sha256=3eNhSwzut0gon04k2EYKKaXRvQSUlau3yBAbHS0EBao,25784
101
103
  pycontrails/models/cocipgrid/cocip_grid_params.py,sha256=l4vBPrOKCJDz5Y1uMjmOGVyUcSWgfZtFWbjW968OPz8,5875
102
104
  pycontrails/models/cocipgrid/__init__.py,sha256=ar6bF_8Pusbb-myujz_q5ntFylQTNH8yiM8fxP7Zk30,262
103
- pycontrails/models/cocipgrid/cocip_grid.py,sha256=uLQ86xK12LJ43KKb-CdSaDDamj4GyfDbiHp_NefotZU,91151
105
+ pycontrails/models/cocipgrid/cocip_grid.py,sha256=vMzkgPxUjef4vVPDHlCOCn7AJJozBWicuHhaqC208M8,91325
104
106
  pycontrails/physics/geo.py,sha256=5THIXgpaHBQdSYWLgtK4mV_8e1hWW9XeTsSHOShFMeA,36323
105
107
  pycontrails/physics/units.py,sha256=BC0e0l_pDeijqN179tXl8eX_Qpw8d17MVujBu1SV3IE,12293
106
- pycontrails/physics/constants.py,sha256=pHQQmccMUwuNnY4hFtm3L8G2rnUQcfJnroyQr8HAVeM,3146
108
+ pycontrails/physics/constants.py,sha256=xWy7OkDOJNM6umq5dYiuzwG0aTEl5aECLxEpg3Z2SBQ,3202
107
109
  pycontrails/physics/__init__.py,sha256=_1eWbEy6evEWdfJCEkwDiSdpiDNzNWEPVqaPekHyhwU,44
108
110
  pycontrails/physics/thermo.py,sha256=sWGpKa12daSpqZYNgyXd8Ii5nfA_1Mm5mMbnM5GsW-E,12787
109
111
  pycontrails/physics/jet.py,sha256=RoHns04wi2EGYWQqYxnr6LUAV-CqsgLhXdrUCrBHAGk,30307