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

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,520 @@
1
+ """
2
+ Radiative heating of contrail cirrus.
3
+
4
+ Contrails absorb incoming solar radiation and outgoing longwave radiation, causing it to heat up.
5
+
6
+ 1. The additional heating energy drives a local updraft
7
+ (but this is negligible and not included in CoCiP);
8
+ 2. The differential heating rate drives local turbulence.
9
+ As radiative emission from the contrail is limited by its low temperature,
10
+ the net result is deposition of radiative energy in the contrail.
11
+
12
+ These equations are provided by Ulrich Schumann and not included in any scientific publications,
13
+ The scientific rationale of including these effects can be found in the references below:
14
+
15
+ References
16
+ ----------
17
+ - :cite:`jensenSpreadingGrowthContrails1998`
18
+ - :cite:`schumannLifeCycleIndividual2017`
19
+ """
20
+
21
+ from __future__ import annotations
22
+
23
+ from dataclasses import dataclass
24
+
25
+ import numpy as np
26
+ import numpy.typing as npt
27
+
28
+ from pycontrails.physics import constants
29
+
30
+
31
+ @dataclass
32
+ class RadiativeHeatingConstants:
33
+ """Constants/coefficients used to calculate the radiative heating rate.
34
+
35
+ Constants/coefficients used to calculate the:
36
+ - shortwave differential heating rate
37
+ - longwave differential heating rate
38
+ - shortwave heating rate
39
+ - longwave heating rate
40
+
41
+ These coefficients were calibrated based on a least-squares fit relative
42
+ to the libRadtran radiative transfer model. (Ulrich Schumann, personal communication).
43
+
44
+ References
45
+ ----------
46
+ - :cite:`schumannRadiativeHeatingContrail2010`
47
+ """
48
+
49
+ #: Coefficients for shortwave differential heating rate
50
+ dacth: float = 0.205747e01
51
+ dacth3: float = 0.898366e00
52
+ dbcth: float = 0.791045e00
53
+ dccth: float = 0.612725e00
54
+ ddcth: float = 0.517342e-02
55
+ dexalb: float = 0.267568e01
56
+ dfrsw: float = 0.139286e01
57
+ dgalbs: float = 0.178497e01
58
+ d_gamma: float = 0.142104e01
59
+ d_gamma_s: float = 0.882497e00
60
+ dqsw: float = 0.631427e-01
61
+ draddsw: float = 0.261780e00
62
+ dtt: float = 0.339171e-01
63
+
64
+ #: Coefficients for longwave differential heating rate
65
+ dak: float = 0.357181e01
66
+ dcrhi: float = 0.623019e-01
67
+ ddelta: float = 0.198000e01
68
+ dfrlw: float = 0.609262e00
69
+ dqlw: float = 0.100000e-05
70
+ dqrlw: float = 0.160286e00
71
+ draddlw: float = 0.898529e-05
72
+ dsigma: float = 0.159884e-06
73
+
74
+ #: Coefficients for shortwave heating rate
75
+ acth: float = 0.156899e01
76
+ bcth: float = 0.875130e00
77
+ ccth: float = 0.112445e01
78
+ dcth: float = 0.236688e-01
79
+ exal_b: float = 0.410705e00
80
+ fr_sw: float = 0.537577e01
81
+ gamma_r: float = 0.762254e00
82
+ q_sw: float = 0.454176e-01
83
+ radd_sw: float = 0.991554e00
84
+ ttt: float = 0.985031e-01
85
+
86
+ #: Coefficients for longwave heating rate
87
+ ak: float = 0.294930e01
88
+ crhi: float = 0.174422e00
89
+ czlw: float = 0.393884e-01
90
+ delta: float = 0.860746e00
91
+ fr_lw: float = 0.760423e00
92
+ q_lw: float = 0.152075e02
93
+ radd_lw: float = 0.308486e-02
94
+ sigma: float = 0.253499e-04
95
+
96
+
97
+ RAD_HEAT = RadiativeHeatingConstants()
98
+
99
+
100
+ def convective_velocity_scale(
101
+ depth_eff: npt.NDArray[np.floating],
102
+ eff_heat_rate: npt.NDArray[np.floating],
103
+ air_temperature: npt.NDArray[np.floating],
104
+ ) -> npt.NDArray[np.floating]:
105
+ """
106
+ Calculate the convective velocity scale, i.e., vertical mixing rate.
107
+
108
+ Parameters
109
+ ----------
110
+ depth_eff : npt.NDArray[np.floating]
111
+ Effective depth of the contrail plume, [:math:`m`]
112
+ eff_heat_rate: npt.NDArray[np.floating]
113
+ Effective heating rate, i.e., rate of which the contrail plume is heated, [:math:`K s^{-1}`]
114
+ air_temperature : npt.NDArray[np.floating]
115
+ Ambient temperature for each waypoint, [:math:`K`]
116
+
117
+ Returns
118
+ -------
119
+ npt.NDArray[np.floating]
120
+ Convective velocity scale, [:math:`m s^{-1}`]
121
+ """
122
+ return ((constants.g * depth_eff**2 * np.maximum(-eff_heat_rate, 0)) / air_temperature) ** (
123
+ 1 / 3
124
+ )
125
+
126
+
127
+ def effective_heating_rate(
128
+ d_heat_rate: npt.NDArray[np.floating],
129
+ cumul_rad_heat: npt.NDArray[np.floating],
130
+ dT_dz: npt.NDArray[np.floating],
131
+ depth: npt.NDArray[np.floating],
132
+ ) -> npt.NDArray[np.floating]:
133
+ """Calculate effective heating rate.
134
+
135
+ The effective heating rate accounts for the heat required to overcome the stable stratification.
136
+ Turbulence will occur after the cumulative heating overcomes the stable stratification.
137
+
138
+ Parameters
139
+ ----------
140
+ d_heat_rate: npt.NDArray[np.floating]
141
+ Differential heating rate, i.e., rate of which the contrail
142
+ plume is heated, [:math:`K s^{-1}`]
143
+ cumul_rad_heat: npt.NDArray[np.floating]
144
+ Cumulative solar and terrestrial radiative heating energy
145
+ absorbed by the contrail, [:math:`K`]
146
+ dT_dz: npt.NDArray[np.floating]
147
+ Temperature gradient with respect to altitude (dz), [:math:`K m^{-1}`]
148
+ depth : npt.NDArray[np.floating]
149
+ Contrail depth at each waypoint, [:math:`m`]
150
+
151
+ Returns
152
+ -------
153
+ npt.NDArray[np.floating]
154
+ Effective heating rate, [:math:`K s^{-1}`]
155
+ """
156
+ filt = cumul_rad_heat > 0.0
157
+ heat_denom = 0.5 * dT_dz[filt] * depth[filt]
158
+ heat_denom.clip(min=0.0, out=heat_denom)
159
+
160
+ heat_ratio = np.zeros_like(cumul_rad_heat)
161
+ heat_ratio[filt] = cumul_rad_heat[filt] / (cumul_rad_heat[filt] + heat_denom)
162
+
163
+ return d_heat_rate * heat_ratio
164
+
165
+
166
+ def differential_heating_rate(
167
+ air_temperature: npt.NDArray[np.floating],
168
+ rhi: npt.NDArray[np.floating],
169
+ rho_air: npt.NDArray[np.floating],
170
+ r_ice_vol: npt.NDArray[np.floating],
171
+ depth_eff: npt.NDArray[np.floating],
172
+ tau_contrail: npt.NDArray[np.floating],
173
+ tau_cirrus: npt.NDArray[np.floating],
174
+ sd0: npt.NDArray[np.floating],
175
+ sdr: npt.NDArray[np.floating],
176
+ rsr: npt.NDArray[np.floating],
177
+ olr: npt.NDArray[np.floating],
178
+ ) -> npt.NDArray[np.floating]:
179
+ """
180
+ Calculate the differential heating rate affecting the contrail plume.
181
+
182
+ Differential heating rate is the heating rate difference between the upper and
183
+ lower half of the cirrus layer. The radiative heating effect is dominated by the
184
+ longwave component. Therefore, this output will always be a positive
185
+ value (i.e., warmer at the upper contrail edge and cooler at the bottom).
186
+
187
+ Parameters
188
+ ----------
189
+ air_temperature : npt.NDArray[np.floating]
190
+ Ambient temperature at each waypoint, [:math:`K`]
191
+ rhi : npt.NDArray[np.floating]
192
+ Relative humidity with respect to ice at each waypoint
193
+ rho_air : npt.NDArray[np.floating]
194
+ Density of air for each waypoint, [:math:`kg m^{-3}`]
195
+ r_ice_vol : npt.NDArray[np.floating]
196
+ Ice particle volume mean radius, [:math:`m`]
197
+ depth_eff : npt.NDArray[np.floating]
198
+ Effective depth of the contrail plume, [:math:`m`]
199
+ tau_contrail : npt.NDArray[np.floating]
200
+ Contrail optical depth for each waypoint
201
+ tau_cirrus : npt.NDArray[np.floating]
202
+ Optical depth of numerical weather prediction (NWP) cirrus above the contrail
203
+ sd0 : npt.NDArray[np.floating]
204
+ Solar constant, [:math:`W m^{-2}`]
205
+ sdr : npt.NDArray[np.floating]
206
+ Solar direct radiation, [:math:`W m^{-2}`]
207
+ rsr : npt.NDArray[np.floating]
208
+ Reflected solar radiation, [:math:`W m^{-2}`]
209
+ olr : npt.NDArray[np.floating]
210
+ Outgoing longwave radiation at each waypoint, [:math:`W m^{-2}`]
211
+
212
+ Returns
213
+ -------
214
+ npt.NDArray[np.floating]
215
+ Differential heating rate, [:math:`K s^{-1}`]
216
+ """
217
+ r_ice_vol_um = r_ice_vol * 1e6
218
+ cp_contrail = contrail_heat_capacity(rho_air, depth_eff)
219
+ d_heat_rate_sw = differential_heating_rate_shortwave(
220
+ cp_contrail, r_ice_vol_um, tau_contrail, tau_cirrus, sd0, sdr, rsr
221
+ )
222
+ d_heat_rate_lw = differential_heating_rate_longwave(
223
+ air_temperature, rhi, cp_contrail, r_ice_vol_um, tau_contrail, tau_cirrus, olr
224
+ )
225
+ return np.minimum(d_heat_rate_sw + d_heat_rate_lw, 0.0)
226
+
227
+
228
+ def differential_heating_rate_shortwave(
229
+ cp_contrail: npt.NDArray[np.floating],
230
+ r_ice_vol_um: npt.NDArray[np.floating],
231
+ tau_contrail: npt.NDArray[np.floating],
232
+ tau_cirrus: npt.NDArray[np.floating],
233
+ sd0: npt.NDArray[np.floating],
234
+ sdr: npt.NDArray[np.floating],
235
+ rsr: npt.NDArray[np.floating],
236
+ ) -> npt.NDArray[np.floating]:
237
+ r"""
238
+ Calculate shortwave differential heating rate.
239
+
240
+ Incoming solar radiation heats the contrail top. Therefore, this output will
241
+ always be a positive value (i.e., warmer at the upper contrail edge and cooler
242
+ at the bottom). This effect is approximated using a parametric model
243
+ that is calibrated with the libRadtran radiative transfer model.
244
+
245
+ Parameters
246
+ ----------
247
+ cp_contrail : npt.NDArray[np.floating]
248
+ Contrail heat capacity per unit length and width, [:math:`J K^{-1} m^{-2}`]
249
+ r_ice_vol_um : npt.NDArray[np.floating]
250
+ Ice particle volume mean radius, [:math:`\mu m`]
251
+ tau_contrail : npt.NDArray[np.floating]
252
+ Contrail optical depth for each waypoint
253
+ tau_cirrus : npt.NDArray[np.floating]
254
+ Optical depth of numerical weather prediction (NWP) cirrus above the contrail
255
+ sd0 : npt.NDArray[np.floating]
256
+ Solar constant, [:math:`W m^{-2}`]
257
+ sdr : npt.NDArray[np.floating]
258
+ Solar direct radiation, [:math:`W m^{-2}`]
259
+ rsr : npt.NDArray[np.floating]
260
+ Reflected solar radiation, [:math:`W m^{-2}`]
261
+
262
+ Returns
263
+ -------
264
+ npt.NDArray[np.floating]
265
+ Shortwave component of the differential heating rate, [:math:`K s^{-1}`]
266
+ """
267
+ # short circuit if no waypoints have sdr > 0
268
+ if not np.any(sdr > 0):
269
+ return np.zeros_like(sdr)
270
+
271
+ mue = np.minimum(sdr / sd0, 1.0)
272
+ tau_eff = tau_contrail / (mue + 1.0e-6)
273
+
274
+ return (
275
+ (1 - np.exp(-RAD_HEAT.dqsw * r_ice_vol_um))
276
+ * (RAD_HEAT.dtt * sdr - RAD_HEAT.ddcth * rsr)
277
+ * tau_contrail
278
+ * (-RAD_HEAT.d_gamma_s * tau_contrail + tau_eff)
279
+ * (1 / cp_contrail)
280
+ * (1 - RAD_HEAT.dacth * mue + RAD_HEAT.d_gamma * mue**2 + (RAD_HEAT.dacth3 - 1) * mue**3)
281
+ * np.exp(RAD_HEAT.dbcth * tau_cirrus - RAD_HEAT.dccth * tau_cirrus / (mue + 1e-6))
282
+ * np.exp(-RAD_HEAT.dgalbs * tau_contrail * (1 - mue) ** RAD_HEAT.dexalb)
283
+ * mue**RAD_HEAT.draddsw
284
+ * (1 + RAD_HEAT.dfrsw * (1 - mue) ** 2)
285
+ )
286
+
287
+
288
+ def differential_heating_rate_longwave(
289
+ air_temperature: npt.NDArray[np.floating],
290
+ rhi: npt.NDArray[np.floating],
291
+ cp_contrail: npt.NDArray[np.floating],
292
+ r_ice_vol_um: npt.NDArray[np.floating],
293
+ tau_contrail: npt.NDArray[np.floating],
294
+ tau_cirrus: npt.NDArray[np.floating],
295
+ olr: npt.NDArray[np.floating],
296
+ ) -> npt.NDArray[np.floating]:
297
+ r"""
298
+ Calculate longwave differential heating rate.
299
+
300
+ Contrails absorb outgoing longwave radiation emitted from the warm surface
301
+ below and heats it up. Therefore, this output will always be a negative value
302
+ (i.e., warmer at the lower contrail edge and cooler at the top). This effect
303
+ is approximated using a parametric model that is calibrated with the
304
+ libRadtran radiative transfer model.
305
+
306
+ Parameters
307
+ ----------
308
+ air_temperature : npt.NDArray[np.floating]
309
+ Ambient temperature at each waypoint, [:math:`K`]
310
+ rhi : npt.NDArray[np.floating]
311
+ Relative humidity with respect to ice at each waypoint
312
+ cp_contrail : npt.NDArray[np.floating]
313
+ Contrail heat capacity per unit length and width, [:math:`J K^{-1} m^{-2}`]
314
+ r_ice_vol_um : npt.NDArray[np.floating]
315
+ Ice particle volume mean radius, [:math:`\mu m`]
316
+ tau_contrail : npt.NDArray[np.floating]
317
+ Contrail optical depth for each waypoint
318
+ tau_cirrus : npt.NDArray[np.floating]
319
+ Optical depth of numerical weather prediction (NWP) cirrus above the contrail
320
+ olr : npt.NDArray[np.floating]
321
+ Outgoing longwave radiation at each waypoint, [:math:`W m^{-2}`]
322
+
323
+ Returns
324
+ -------
325
+ npt.NDArray[np.floating]
326
+ Longwave component of the differential heating rate, [:math:`K s^{-1}`]
327
+ """
328
+ cool = RAD_HEAT.dsigma * air_temperature**RAD_HEAT.dak
329
+ epsc = 1 - np.exp(-RAD_HEAT.ddelta * (tau_contrail + tau_cirrus))
330
+ return (
331
+ -RAD_HEAT.dfrlw
332
+ * (1 / cp_contrail)
333
+ * (olr - cool)
334
+ * (epsc / RAD_HEAT.ddelta)
335
+ * tau_contrail
336
+ * np.exp(-RAD_HEAT.dqlw * tau_cirrus)
337
+ * np.maximum(1 - RAD_HEAT.draddlw * 10 / (r_ice_vol_um + 30), 0.0)
338
+ * (1 - np.exp(-RAD_HEAT.dqrlw * r_ice_vol_um))
339
+ * np.exp(-(rhi - 0.9) * RAD_HEAT.dcrhi)
340
+ )
341
+
342
+
343
+ def heating_rate(
344
+ air_temperature: npt.NDArray[np.floating],
345
+ rhi: npt.NDArray[np.floating],
346
+ rho_air: npt.NDArray[np.floating],
347
+ r_ice_vol: npt.NDArray[np.floating],
348
+ depth_eff: npt.NDArray[np.floating],
349
+ tau_contrail: npt.NDArray[np.floating],
350
+ tau_cirrus: npt.NDArray[np.floating],
351
+ sd0: npt.NDArray[np.floating],
352
+ sdr: npt.NDArray[np.floating],
353
+ rsr: npt.NDArray[np.floating],
354
+ olr: npt.NDArray[np.floating],
355
+ ) -> npt.NDArray[np.floating]:
356
+ """
357
+ Calculate the heating rate affecting the contrail plume.
358
+
359
+ This is the average heating rate over the contrail cirrus layer.
360
+
361
+ Parameters
362
+ ----------
363
+ air_temperature : npt.NDArray[np.floating]
364
+ Ambient temperature at each waypoint, [:math:`K`]
365
+ rhi : npt.NDArray[np.floating]
366
+ Relative humidity with respect to ice at each waypoint
367
+ rho_air : npt.NDArray[np.floating]
368
+ Density of air for each waypoint, [:math:`kg m^{-3}`]
369
+ r_ice_vol : npt.NDArray[np.floating]
370
+ Ice particle volume mean radius, [:math:`m`]
371
+ depth_eff : npt.NDArray[np.floating]
372
+ Effective depth of the contrail plume, [:math:`m`]
373
+ tau_contrail : npt.NDArray[np.floating]
374
+ Contrail optical depth for each waypoint
375
+ tau_cirrus : npt.NDArray[np.floating]
376
+ Optical depth of numerical weather prediction (NWP) cirrus above the contrail
377
+ sd0 : npt.NDArray[np.floating]
378
+ Solar constant, [:math:`W m^{-2}`]
379
+ sdr : npt.NDArray[np.floating]
380
+ Solar direct radiation, [:math:`W m^{-2}`]
381
+ rsr : npt.NDArray[np.floating]
382
+ Reflected solar radiation, [:math:`W m^{-2}`]
383
+ olr : npt.NDArray[np.floating]
384
+ Outgoing longwave radiation at each waypoint, [:math:`W m^{-2}`]
385
+
386
+ Returns
387
+ -------
388
+ npt.NDArray[np.floating]
389
+ Heating rate, [:math:`K s^{-1}`]
390
+ """
391
+ r_ice_vol_um = r_ice_vol * 1e6
392
+ cp_contrail = contrail_heat_capacity(rho_air, depth_eff)
393
+ heat_rate_sw = heating_rate_shortwave(
394
+ cp_contrail, r_ice_vol_um, tau_contrail, tau_cirrus, sd0, sdr, rsr
395
+ )
396
+ heat_rate_lw = heating_rate_longwave(
397
+ air_temperature, rhi, cp_contrail, r_ice_vol_um, tau_contrail, tau_cirrus, olr
398
+ )
399
+ return heat_rate_sw + heat_rate_lw
400
+
401
+
402
+ def heating_rate_shortwave(
403
+ cp_contrail: npt.NDArray[np.floating],
404
+ r_ice_vol_um: npt.NDArray[np.floating],
405
+ tau_contrail: npt.NDArray[np.floating],
406
+ tau_cirrus: npt.NDArray[np.floating],
407
+ sd0: npt.NDArray[np.floating],
408
+ sdr: npt.NDArray[np.floating],
409
+ rsr: npt.NDArray[np.floating],
410
+ ) -> npt.NDArray[np.floating]:
411
+ r"""Calculate shortwave heating rate.
412
+
413
+ Parameters
414
+ ----------
415
+ cp_contrail : npt.NDArray[np.floating]
416
+ Contrail heat capacity per unit length and width, [:math:`J K^{-1} m^{-2}`]
417
+ r_ice_vol_um : npt.NDArray[np.floating]
418
+ Ice particle volume mean radius, [:math:`\mu m`]
419
+ tau_contrail : npt.NDArray[np.floating]
420
+ Contrail optical depth for each waypoint
421
+ tau_cirrus : npt.NDArray[np.floating]
422
+ Optical depth of numerical weather prediction (NWP) cirrus above the contrail
423
+ sd0 : npt.NDArray[np.floating]
424
+ Solar constant, [:math:`W m^{-2}`]
425
+ sdr : npt.NDArray[np.floating]
426
+ Solar direct radiation, [:math:`W m^{-2}`]
427
+ rsr : npt.NDArray[np.floating]
428
+ Reflected solar radiation, [:math:`W m^{-2}`]
429
+
430
+ Returns
431
+ -------
432
+ npt.NDArray[np.floating]
433
+ Shortwave component of heating rate, [:math:`K s^{-1}`]
434
+ """
435
+ # short circuit if no waypoints have sdr > 0
436
+ if not np.any(sdr > 0):
437
+ return np.zeros_like(sdr)
438
+
439
+ mue = np.minimum(sdr / sd0, 1.0)
440
+ tau_eff = tau_contrail / (mue + 1.0e-6)
441
+ heat_rate_sw = (
442
+ (1 - np.exp(-RAD_HEAT.q_sw * r_ice_vol_um))
443
+ * (RAD_HEAT.ttt * sdr + RAD_HEAT.dcth * rsr)
444
+ * tau_eff
445
+ * (1 / cp_contrail)
446
+ * (1 - RAD_HEAT.acth * mue + RAD_HEAT.gamma_r * mue**2)
447
+ * np.exp(RAD_HEAT.bcth * tau_cirrus - RAD_HEAT.ccth * tau_cirrus / (mue + 1e-6))
448
+ * np.exp(-tau_contrail * (1 - mue) ** 2)
449
+ * mue
450
+ )
451
+ return np.maximum(heat_rate_sw, 0)
452
+
453
+
454
+ def heating_rate_longwave(
455
+ air_temperature: npt.NDArray[np.floating],
456
+ rhi: npt.NDArray[np.floating],
457
+ cp_contrail: npt.NDArray[np.floating],
458
+ r_ice_vol_um: npt.NDArray[np.floating],
459
+ tau_contrail: npt.NDArray[np.floating],
460
+ tau_cirrus: npt.NDArray[np.floating],
461
+ olr: npt.NDArray[np.floating],
462
+ ) -> npt.NDArray[np.floating]:
463
+ r"""Calculate longwave heating rate.
464
+
465
+ Parameters
466
+ ----------
467
+ air_temperature : npt.NDArray[np.floating]
468
+ Ambient temperature at each waypoint, [:math:`K`]
469
+ rhi : npt.NDArray[np.floating]
470
+ Relative humidity with respect to ice at each waypoint
471
+ cp_contrail : npt.NDArray[np.floating]
472
+ Contrail heat capacity per unit length and width, [:math:`J K^{-1} m^{-2}`]
473
+ r_ice_vol_um : npt.NDArray[np.floating]
474
+ Ice particle volume mean radius, [:math:`\mu m`]
475
+ tau_contrail : npt.NDArray[np.floating]
476
+ Contrail optical depth for each waypoint
477
+ tau_cirrus : npt.NDArray[np.floating]
478
+ Optical depth of numerical weather prediction (NWP) cirrus above the contrail
479
+ olr : npt.NDArray[np.floating]
480
+ Outgoing longwave radiation at each waypoint, [:math:`W m^{-2}`]
481
+
482
+ Returns
483
+ -------
484
+ npt.NDArray[np.floating]
485
+ Longwave component of heating rate, [:math:`K s^{-1}`]
486
+ """
487
+ fzlw = np.exp(-(rhi - 0.9) * RAD_HEAT.czlw)
488
+ cool = RAD_HEAT.sigma * air_temperature**RAD_HEAT.ak
489
+ epsc = 1 - np.exp(-RAD_HEAT.delta * (tau_contrail + tau_cirrus))
490
+ heat_rate_lw = (
491
+ np.exp(-(rhi - 0.9) * RAD_HEAT.crhi)
492
+ * RAD_HEAT.fr_lw
493
+ * (1 / cp_contrail)
494
+ * (olr / fzlw - cool * fzlw)
495
+ * (epsc / RAD_HEAT.delta)
496
+ * np.exp(-RAD_HEAT.q_lw * tau_cirrus)
497
+ * (1 + RAD_HEAT.radd_lw / (r_ice_vol_um + 30))
498
+ )
499
+ return np.maximum(heat_rate_lw, 0)
500
+
501
+
502
+ def contrail_heat_capacity(
503
+ rho_air: npt.NDArray[np.floating], depth_eff: npt.NDArray[np.floating]
504
+ ) -> npt.NDArray[np.floating]:
505
+ """
506
+ Calculate contrail heat capacity per unit length and width.
507
+
508
+ Parameters
509
+ ----------
510
+ rho_air : npt.NDArray[np.floating]
511
+ density of air for each waypoint, [:math:`kg m^{-3}`]
512
+ depth_eff: npt.NDArray[np.floating]
513
+ Effective depth of the contrail plume, [:math:`m`]
514
+
515
+ Returns
516
+ -------
517
+ npt.NDArray[np.floating]
518
+ Contrail heat capacity per unit length and width, [:math:`J K^{-1} m^{-2}`]
519
+ """
520
+ return depth_eff * rho_air * constants.c_pd