pycontrails 0.50.0__cp39-cp39-macosx_11_0_arm64.whl → 0.50.1__cp39-cp39-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.
- pycontrails/_version.py +2 -2
- pycontrails/core/flight.py +11 -6
- pycontrails/core/interpolation.py +39 -1
- pycontrails/core/met.py +10 -12
- pycontrails/core/rgi_cython.cpython-39-darwin.so +0 -0
- pycontrails/core/vector.py +10 -8
- pycontrails/datalib/goes.py +14 -12
- pycontrails/models/cocip/cocip.py +22 -5
- pycontrails/models/cocip/cocip_params.py +11 -2
- pycontrails/models/cocip/contrail_properties.py +4 -9
- pycontrails/models/cocip/unterstrasser_wake_vortex.py +403 -0
- pycontrails/models/cocip/wake_vortex.py +22 -1
- pycontrails/models/cocipgrid/cocip_grid.py +103 -6
- pycontrails/models/issr.py +1 -1
- {pycontrails-0.50.0.dist-info → pycontrails-0.50.1.dist-info}/METADATA +1 -1
- {pycontrails-0.50.0.dist-info → pycontrails-0.50.1.dist-info}/RECORD +20 -19
- {pycontrails-0.50.0.dist-info → pycontrails-0.50.1.dist-info}/LICENSE +0 -0
- {pycontrails-0.50.0.dist-info → pycontrails-0.50.1.dist-info}/NOTICE +0 -0
- {pycontrails-0.50.0.dist-info → pycontrails-0.50.1.dist-info}/WHEEL +0 -0
- {pycontrails-0.50.0.dist-info → pycontrails-0.50.1.dist-info}/top_level.txt +0 -0
pycontrails/_version.py
CHANGED
pycontrails/core/flight.py
CHANGED
|
@@ -787,6 +787,13 @@ class Flight(GeoVectorDataset):
|
|
|
787
787
|
Waypoints are resampled according to the frequency ``freq``. Values for :attr:`data`
|
|
788
788
|
columns ``longitude``, ``latitude``, and ``altitude`` are interpolated.
|
|
789
789
|
|
|
790
|
+
Resampled waypoints will include all multiples of ``freq`` between the flight
|
|
791
|
+
start and end time. For example, when resampling to a frequency of 1 minute,
|
|
792
|
+
a flight that starts at 2020/1/1 00:00:59 and ends at 2020/1/1 00:01:01
|
|
793
|
+
will return a single waypoint at 2020/1/1 00:01:00, whereas a flight that
|
|
794
|
+
starts at 2020/1/1 00:01:01 and ends at 2020/1/1 00:01:59 will return an empty
|
|
795
|
+
flight.
|
|
796
|
+
|
|
790
797
|
Parameters
|
|
791
798
|
----------
|
|
792
799
|
freq : str, optional
|
|
@@ -1349,8 +1356,8 @@ class Flight(GeoVectorDataset):
|
|
|
1349
1356
|
>>> # Intersect and attach
|
|
1350
1357
|
>>> fl["air_temperature"] = fl.intersect_met(met['air_temperature'])
|
|
1351
1358
|
>>> fl["air_temperature"]
|
|
1352
|
-
array([235.
|
|
1353
|
-
234.
|
|
1359
|
+
array([235.94657007, 235.95766965, 235.96873412, ..., 234.59917962,
|
|
1360
|
+
234.60387402, 234.60845312])
|
|
1354
1361
|
|
|
1355
1362
|
>>> # Length (in meters) of waypoints whose temperature exceeds 236K
|
|
1356
1363
|
>>> fl.length_met("air_temperature", threshold=236)
|
|
@@ -2063,11 +2070,9 @@ def _resample_to_freq(df: pd.DataFrame, freq: str) -> tuple[pd.DataFrame, pd.Dat
|
|
|
2063
2070
|
|
|
2064
2071
|
# Manually create a new index that includes all the original index values
|
|
2065
2072
|
# and the resampled-to-freq index values.
|
|
2066
|
-
t0 = df.index[0]
|
|
2073
|
+
t0 = df.index[0].ceil(freq)
|
|
2067
2074
|
t1 = df.index[-1]
|
|
2068
|
-
t = pd.date_range(t0, t1, freq=freq, name="time")
|
|
2069
|
-
if t[0] < t0:
|
|
2070
|
-
t = t[1:]
|
|
2075
|
+
t = pd.date_range(t0, t1, freq=freq, name="time")
|
|
2071
2076
|
|
|
2072
2077
|
concat_arr = np.concatenate([df.index, t])
|
|
2073
2078
|
concat_arr = np.unique(concat_arr)
|
|
@@ -71,7 +71,9 @@ class PycontrailsRegularGridInterpolator(scipy.interpolate.RegularGridInterpolat
|
|
|
71
71
|
|
|
72
72
|
self.grid = points
|
|
73
73
|
self.values = values
|
|
74
|
-
|
|
74
|
+
# TODO: consider supporting updated tensor-product spline methods
|
|
75
|
+
# see https://github.com/scipy/scipy/releases/tag/v1.13.0
|
|
76
|
+
self.method = _pick_method(scipy.__version__, method)
|
|
75
77
|
self.bounds_error = bounds_error
|
|
76
78
|
self.fill_value = fill_value
|
|
77
79
|
|
|
@@ -219,6 +221,42 @@ class PycontrailsRegularGridInterpolator(scipy.interpolate.RegularGridInterpolat
|
|
|
219
221
|
raise ValueError(msg)
|
|
220
222
|
|
|
221
223
|
|
|
224
|
+
def _pick_method(scipy_version: str, method: str) -> str:
|
|
225
|
+
"""Select an interpolation method.
|
|
226
|
+
|
|
227
|
+
For scipy versions 1.13.0 and later, fall back on legacy implementations
|
|
228
|
+
of tensor-product spline methods. The default implementations in 1.13.0
|
|
229
|
+
and later are incompatible with this class.
|
|
230
|
+
|
|
231
|
+
Parameters
|
|
232
|
+
----------
|
|
233
|
+
scipy_version : str
|
|
234
|
+
scipy version (major.minor.patch)
|
|
235
|
+
|
|
236
|
+
method : str
|
|
237
|
+
Interpolation method. Passed into :class:`scipy.interpolate.RegularGridInterpolator`
|
|
238
|
+
as-is unless ``scipy_version`` is 1.13.0 or later and ``method`` is ``"slinear"``,
|
|
239
|
+
``"cubic"``, or ``"quintic"``. In this case, ``"_legacy"`` is appended to ``method``.
|
|
240
|
+
|
|
241
|
+
Returns
|
|
242
|
+
-------
|
|
243
|
+
str
|
|
244
|
+
Interpolation method adjusted for compatibility with this class.
|
|
245
|
+
"""
|
|
246
|
+
try:
|
|
247
|
+
version = scipy_version.split(".")
|
|
248
|
+
major = int(version[0])
|
|
249
|
+
minor = int(version[1])
|
|
250
|
+
except (IndexError, ValueError) as exc:
|
|
251
|
+
msg = f"Failed to parse major and minor version from {scipy_version}"
|
|
252
|
+
raise ValueError(msg) from exc
|
|
253
|
+
|
|
254
|
+
reimplemented_methods = ["slinear", "cubic", "quintic"]
|
|
255
|
+
if major > 1 or (major == 1 and minor >= 13) and method in reimplemented_methods:
|
|
256
|
+
return method + "_legacy"
|
|
257
|
+
return method
|
|
258
|
+
|
|
259
|
+
|
|
222
260
|
def _floatize_time(
|
|
223
261
|
time: npt.NDArray[np.datetime64], offset: np.datetime64
|
|
224
262
|
) -> npt.NDArray[np.float64]:
|
pycontrails/core/met.py
CHANGED
|
@@ -674,13 +674,10 @@ class MetDataset(MetBase):
|
|
|
674
674
|
>>> da = mda.data # Underlying `xarray` object
|
|
675
675
|
|
|
676
676
|
>>> # Check out a few values
|
|
677
|
-
>>> da[5:
|
|
678
|
-
array([[224.
|
|
679
|
-
[224.
|
|
680
|
-
[224.
|
|
681
|
-
[224.10617, 224.43282, 224.7777 , 225.17812, 225.62166],
|
|
682
|
-
[224.11115, 224.44028, 224.7835 , 225.18393, 225.62663]],
|
|
683
|
-
dtype=float32)
|
|
677
|
+
>>> da[5:8, 5:8, 1, 1].values
|
|
678
|
+
array([[224.08959005, 224.41374427, 224.75945349],
|
|
679
|
+
[224.09456429, 224.42037658, 224.76525676],
|
|
680
|
+
[224.10036756, 224.42617985, 224.77106004]])
|
|
684
681
|
|
|
685
682
|
>>> # Mean temperature over entire array
|
|
686
683
|
>>> da.mean().load().item()
|
|
@@ -1618,15 +1615,15 @@ class MetDataArray(MetBase):
|
|
|
1618
1615
|
|
|
1619
1616
|
>>> # Interpolation at a grid point agrees with value
|
|
1620
1617
|
>>> mda.interpolate(1, 2, 300, np.datetime64('2022-03-01T14:00'))
|
|
1621
|
-
array([241.
|
|
1618
|
+
array([241.91972984])
|
|
1622
1619
|
|
|
1623
1620
|
>>> da = mda.data
|
|
1624
1621
|
>>> da.sel(longitude=1, latitude=2, level=300, time=np.datetime64('2022-03-01T14')).item()
|
|
1625
|
-
241.
|
|
1622
|
+
241.9197298421629
|
|
1626
1623
|
|
|
1627
1624
|
>>> # Interpolation off grid
|
|
1628
1625
|
>>> mda.interpolate(1.1, 2.1, 290, np.datetime64('2022-03-01 13:10'))
|
|
1629
|
-
array([239.
|
|
1626
|
+
array([239.83793798])
|
|
1630
1627
|
|
|
1631
1628
|
>>> # Interpolate along path
|
|
1632
1629
|
>>> longitude = np.linspace(1, 2, 10)
|
|
@@ -1634,8 +1631,9 @@ class MetDataArray(MetBase):
|
|
|
1634
1631
|
>>> level = np.linspace(200, 300, 10)
|
|
1635
1632
|
>>> time = pd.date_range("2022-03-01T14", periods=10, freq="5min")
|
|
1636
1633
|
>>> mda.interpolate(longitude, latitude, level, time)
|
|
1637
|
-
array([220.
|
|
1638
|
-
|
|
1634
|
+
array([220.44347694, 223.08900738, 225.74338924, 228.41642088,
|
|
1635
|
+
231.10858599, 233.54857391, 235.71504913, 237.86478872,
|
|
1636
|
+
239.99274623, 242.10792167])
|
|
1639
1637
|
"""
|
|
1640
1638
|
# Load if necessary
|
|
1641
1639
|
if not self.in_memory:
|
|
Binary file
|
pycontrails/core/vector.py
CHANGED
|
@@ -1694,12 +1694,14 @@ class GeoVectorDataset(VectorDataset):
|
|
|
1694
1694
|
|
|
1695
1695
|
>>> # Intersect
|
|
1696
1696
|
>>> fl.intersect_met(met['air_temperature'], method='nearest')
|
|
1697
|
-
array([231.
|
|
1698
|
-
231.
|
|
1697
|
+
array([231.62969892, 230.72604651, 232.24318771, 231.88338483,
|
|
1698
|
+
231.06429438, 231.59073409, 231.65125393, 231.93064004,
|
|
1699
|
+
232.03344087, 231.65954432])
|
|
1699
1700
|
|
|
1700
1701
|
>>> fl.intersect_met(met['air_temperature'], method='linear')
|
|
1701
|
-
array([225.
|
|
1702
|
-
|
|
1702
|
+
array([225.77794552, 225.13908414, 226.231218 , 226.31831528,
|
|
1703
|
+
225.56102321, 225.81192149, 226.03192642, 226.22056121,
|
|
1704
|
+
226.03770174, 225.63226188])
|
|
1703
1705
|
|
|
1704
1706
|
>>> # Interpolate and attach to `Flight` instance
|
|
1705
1707
|
>>> for key in met:
|
|
@@ -1708,11 +1710,11 @@ class GeoVectorDataset(VectorDataset):
|
|
|
1708
1710
|
>>> # Show the final three columns of the dataframe
|
|
1709
1711
|
>>> fl.dataframe.iloc[:, -3:].head()
|
|
1710
1712
|
time air_temperature specific_humidity
|
|
1711
|
-
0 2022-03-01 00:00:00 225.
|
|
1713
|
+
0 2022-03-01 00:00:00 225.777946 0.000132
|
|
1712
1714
|
1 2022-03-01 00:13:20 225.139084 0.000132
|
|
1713
|
-
2 2022-03-01 00:26:40 226.
|
|
1714
|
-
3 2022-03-01 00:40:00 226.
|
|
1715
|
-
4 2022-03-01 00:53:20 225.
|
|
1715
|
+
2 2022-03-01 00:26:40 226.231218 0.000107
|
|
1716
|
+
3 2022-03-01 00:40:00 226.318315 0.000171
|
|
1717
|
+
4 2022-03-01 00:53:20 225.561022 0.000109
|
|
1716
1718
|
|
|
1717
1719
|
"""
|
|
1718
1720
|
# Override use_indices in certain situations
|
pycontrails/datalib/goes.py
CHANGED
|
@@ -50,7 +50,7 @@ except ModuleNotFoundError as exc:
|
|
|
50
50
|
|
|
51
51
|
|
|
52
52
|
#: Default channels to use if none are specified. These are the channels
|
|
53
|
-
#: required by the MIT ash color scheme.
|
|
53
|
+
#: required by the SEVIRI (MIT) ash color scheme.
|
|
54
54
|
DEFAULT_CHANNELS = "C11", "C14", "C15"
|
|
55
55
|
|
|
56
56
|
#: The time at which the GOES scan mode changed from mode 3 to mode 6. This
|
|
@@ -203,10 +203,10 @@ def gcs_goes_path(
|
|
|
203
203
|
GOES Region of interest.
|
|
204
204
|
channels : str | Iterable[str]
|
|
205
205
|
Set of channels or bands for CMIP data. The 16 possible channels are
|
|
206
|
-
represented by the strings "C01" to "C16". For the
|
|
206
|
+
represented by the strings "C01" to "C16". For the SEVIRI ash color scheme,
|
|
207
207
|
set ``channels=("C11", "C14", "C15")``. For the true color scheme,
|
|
208
208
|
set ``channels=("C01", "C02", "C03")``. By default, the channels
|
|
209
|
-
required by the
|
|
209
|
+
required by the SEVIRI ash color scheme are used.
|
|
210
210
|
|
|
211
211
|
Returns
|
|
212
212
|
-------
|
|
@@ -306,10 +306,10 @@ class GOES:
|
|
|
306
306
|
|
|
307
307
|
channels : str | set[str] | None
|
|
308
308
|
Set of channels or bands for CMIP data. The 16 possible channels are
|
|
309
|
-
represented by the strings "C01" to "C16". For the
|
|
309
|
+
represented by the strings "C01" to "C16". For the SEVIRI ash color scheme,
|
|
310
310
|
set ``channels=("C11", "C14", "C15")``. For the true color scheme,
|
|
311
311
|
set ``channels=("C01", "C02", "C03")``. By default, the channels
|
|
312
|
-
required by the
|
|
312
|
+
required by the SEVIRI ash color scheme are used. The channels must have
|
|
313
313
|
a common horizontal resolution. The resolutions are:
|
|
314
314
|
|
|
315
315
|
- C01: 1.0 km
|
|
@@ -585,7 +585,7 @@ def _concat_c02(ds1: XArrayType, ds2: XArrayType) -> XArrayType:
|
|
|
585
585
|
def extract_goes_visualization(
|
|
586
586
|
da: xr.DataArray,
|
|
587
587
|
color_scheme: str = "ash",
|
|
588
|
-
ash_convention: str = "
|
|
588
|
+
ash_convention: str = "SEVIRI",
|
|
589
589
|
gamma: float = 2.2,
|
|
590
590
|
) -> tuple[npt.NDArray[np.float32], ccrs.Geostationary, tuple[float, float, float, float]]:
|
|
591
591
|
"""Extract artifacts for visualizing GOES data with the given color scheme.
|
|
@@ -597,7 +597,7 @@ def extract_goes_visualization(
|
|
|
597
597
|
required by :func:`to_ash`.
|
|
598
598
|
color_scheme : str = {"ash", "true"}
|
|
599
599
|
Color scheme to use for visualization.
|
|
600
|
-
ash_convention : str = {"
|
|
600
|
+
ash_convention : str = {"SEVIRI", "standard"}
|
|
601
601
|
Passed into :func:`to_ash`. Only used if ``color_scheme="ash"``.
|
|
602
602
|
gamma : float = 2.2
|
|
603
603
|
Passed into :func:`to_true_color`. Only used if ``color_scheme="true"``.
|
|
@@ -672,17 +672,18 @@ def to_true_color(da: xr.DataArray, gamma: float = 2.2) -> npt.NDArray[np.float3
|
|
|
672
672
|
return np.dstack([red, green, blue])
|
|
673
673
|
|
|
674
674
|
|
|
675
|
-
def to_ash(da: xr.DataArray, convention: str = "
|
|
675
|
+
def to_ash(da: xr.DataArray, convention: str = "SEVIRI") -> npt.NDArray[np.float32]:
|
|
676
676
|
"""Compute 3d RGB array for the ASH color scheme.
|
|
677
677
|
|
|
678
678
|
Parameters
|
|
679
679
|
----------
|
|
680
680
|
da : xr.DataArray
|
|
681
681
|
DataArray of GOES data with appropriate channels.
|
|
682
|
-
convention : str = {"
|
|
682
|
+
convention : str = {"SEVIRI", "standard"}
|
|
683
683
|
Convention for color space.
|
|
684
684
|
|
|
685
|
-
-
|
|
685
|
+
- SEVIRI convention requires channels C11, C14, C15.
|
|
686
|
+
Used in :cite:`kulikSatellitebasedDetectionContrails2019`.
|
|
686
687
|
- Standard convention requires channels C11, C13, C14, C15
|
|
687
688
|
|
|
688
689
|
Returns
|
|
@@ -693,6 +694,7 @@ def to_ash(da: xr.DataArray, convention: str = "MIT") -> npt.NDArray[np.float32]
|
|
|
693
694
|
References
|
|
694
695
|
----------
|
|
695
696
|
- `Ash RGB quick guide (the color space and color interpretations) <https://rammb.cira.colostate.edu/training/visit/quick_guides/GOES_Ash_RGB.pdf>`_
|
|
697
|
+
- :cite:`SEVIRIRGBCal`
|
|
696
698
|
- :cite:`kulikSatellitebasedDetectionContrails2019`
|
|
697
699
|
|
|
698
700
|
Examples
|
|
@@ -716,7 +718,7 @@ def to_ash(da: xr.DataArray, convention: str = "MIT") -> npt.NDArray[np.float32]
|
|
|
716
718
|
green = c14 - c11
|
|
717
719
|
blue = c13
|
|
718
720
|
|
|
719
|
-
elif convention
|
|
721
|
+
elif convention in ["SEVIRI", "MIT"]: # retain MIT for backwards compatibility
|
|
720
722
|
c11 = da.sel(band_id=11).values # 8.44
|
|
721
723
|
c14 = da.sel(band_id=14).values # 11.19
|
|
722
724
|
c15 = da.sel(band_id=15).values # 12.27
|
|
@@ -726,7 +728,7 @@ def to_ash(da: xr.DataArray, convention: str = "MIT") -> npt.NDArray[np.float32]
|
|
|
726
728
|
blue = c14
|
|
727
729
|
|
|
728
730
|
else:
|
|
729
|
-
raise ValueError("Convention must be either '
|
|
731
|
+
raise ValueError("Convention must be either 'SEVIRI' or 'standard'")
|
|
730
732
|
|
|
731
733
|
# See colostate pdf for slightly wider values
|
|
732
734
|
red = _clip_and_scale(red, -4.0, 2.0)
|
|
@@ -26,6 +26,7 @@ from pycontrails.models.cocip import (
|
|
|
26
26
|
contrail_properties,
|
|
27
27
|
radiative_forcing,
|
|
28
28
|
radiative_heating,
|
|
29
|
+
unterstrasser_wake_vortex,
|
|
29
30
|
wake_vortex,
|
|
30
31
|
wind_shear,
|
|
31
32
|
)
|
|
@@ -838,9 +839,9 @@ class Cocip(Model):
|
|
|
838
839
|
T_critical_sac = self._sac_flight["T_critical_sac"]
|
|
839
840
|
|
|
840
841
|
# Flight performance parameters
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
842
|
+
fuel_flow = self._sac_flight.get_data_or_attr("fuel_flow")
|
|
843
|
+
true_airspeed = self._sac_flight["true_airspeed"]
|
|
844
|
+
fuel_dist = fuel_flow / true_airspeed
|
|
844
845
|
|
|
845
846
|
nvpm_ei_n = self._sac_flight.get_data_or_attr("nvpm_ei_n")
|
|
846
847
|
ei_h2o = self._sac_flight.fuel.ei_h2o
|
|
@@ -890,11 +891,27 @@ class Cocip(Model):
|
|
|
890
891
|
air_temperature, air_pressure, air_pressure_1
|
|
891
892
|
)
|
|
892
893
|
iwc_1 = contrail_properties.iwc_post_wake_vortex(iwc, iwc_ad)
|
|
894
|
+
|
|
895
|
+
if self.params["unterstrasser_ice_survival_fraction"]:
|
|
896
|
+
wingspan = self._sac_flight.get_data_or_attr("wingspan")
|
|
897
|
+
rhi_0 = thermo.rhi(specific_humidity, air_temperature, air_pressure)
|
|
898
|
+
f_surv = unterstrasser_wake_vortex.ice_particle_number_survival_fraction(
|
|
899
|
+
air_temperature,
|
|
900
|
+
rhi_0,
|
|
901
|
+
ei_h2o,
|
|
902
|
+
wingspan,
|
|
903
|
+
true_airspeed,
|
|
904
|
+
fuel_flow,
|
|
905
|
+
nvpm_ei_n,
|
|
906
|
+
0.5 * depth, # Taking the mid-point of the contrail plume
|
|
907
|
+
)
|
|
908
|
+
else:
|
|
909
|
+
f_surv = contrail_properties.ice_particle_survival_fraction(iwc, iwc_1)
|
|
910
|
+
|
|
893
911
|
n_ice_per_m_1 = contrail_properties.ice_particle_number(
|
|
894
912
|
nvpm_ei_n=nvpm_ei_n,
|
|
895
913
|
fuel_dist=fuel_dist,
|
|
896
|
-
|
|
897
|
-
iwc_1=iwc_1,
|
|
914
|
+
f_surv=f_surv,
|
|
898
915
|
air_temperature=air_temperature,
|
|
899
916
|
T_crit_sac=T_critical_sac,
|
|
900
917
|
min_ice_particle_number_nvpm_ei_n=self.params["min_ice_particle_number_nvpm_ei_n"],
|
|
@@ -138,12 +138,12 @@ class CocipParams(ModelParams):
|
|
|
138
138
|
|
|
139
139
|
#: Add additional metric of ATR20 and global yearly mean RF to model output.
|
|
140
140
|
#: These are not standard CoCiP outputs but based on the derivation used
|
|
141
|
-
#: in the first supplement to :cite:`
|
|
141
|
+
#: in the first supplement to :cite:`yinPredictingClimateImpact2023`. ATR20 is defined
|
|
142
142
|
#: as the average temperature response over a 20 year horizon.
|
|
143
143
|
compute_atr20: bool = False
|
|
144
144
|
|
|
145
145
|
#: Constant factor used to convert global- and year-mean RF, [:math:`W m^{-2}`],
|
|
146
|
-
#: to ATR20, [:math:`K`], given by :cite:`
|
|
146
|
+
#: to ATR20, [:math:`K`], given by :cite:`yinPredictingClimateImpact2023`.
|
|
147
147
|
global_rf_to_atr20_factor: float = 0.0151
|
|
148
148
|
|
|
149
149
|
# ----------------
|
|
@@ -184,6 +184,15 @@ class CocipParams(ModelParams):
|
|
|
184
184
|
#: :attr:`radiative_heating_effects` is enabled.
|
|
185
185
|
max_depth: float = 1500.0
|
|
186
186
|
|
|
187
|
+
#: Experimental. Improved ice crystal number survival fraction in the wake vortex phase.
|
|
188
|
+
#: Implement :cite:`unterstrasserPropertiesYoungContrails2016`, who developed a
|
|
189
|
+
#: parametric model that estimates the survival fraction of the contrail ice crystal
|
|
190
|
+
#: number after the wake vortex phase based on the results from large eddy simulations.
|
|
191
|
+
#: This replicates Fig. 4 of :cite:`karcherFormationRadiativeForcing2018`.
|
|
192
|
+
#:
|
|
193
|
+
#: .. versionadded:: 0.50.1
|
|
194
|
+
unterstrasser_ice_survival_fraction: bool = False
|
|
195
|
+
|
|
187
196
|
#: Experimental. Radiative heating effects on contrail cirrus properties.
|
|
188
197
|
#: Terrestrial and solar radiances warm the contrail ice particles and cause
|
|
189
198
|
#: convective turbulence. This effect is expected to enhance vertical mixing
|
|
@@ -204,8 +204,7 @@ def iwc_post_wake_vortex(
|
|
|
204
204
|
def ice_particle_number(
|
|
205
205
|
nvpm_ei_n: npt.NDArray[np.float64],
|
|
206
206
|
fuel_dist: npt.NDArray[np.float64],
|
|
207
|
-
|
|
208
|
-
iwc_1: npt.NDArray[np.float64],
|
|
207
|
+
f_surv: npt.NDArray[np.float64],
|
|
209
208
|
air_temperature: npt.NDArray[np.float64],
|
|
210
209
|
T_crit_sac: npt.NDArray[np.float64],
|
|
211
210
|
min_ice_particle_number_nvpm_ei_n: float,
|
|
@@ -223,11 +222,8 @@ def ice_particle_number(
|
|
|
223
222
|
black carbon number emissions index, [:math:`kg^{-1}`]
|
|
224
223
|
fuel_dist : npt.NDArray[np.float64]
|
|
225
224
|
fuel consumption of the flight segment per distance traveled, [:math:`kg m^{-1}`]
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
phase, [:math:`kg_{H_{2}O}/kg_{air}`]
|
|
229
|
-
iwc_1 : npt.NDArray[np.float64]
|
|
230
|
-
ice water content after the wake vortex phase, [:math:`kg_{H_{2}O}/kg_{air}`]
|
|
225
|
+
f_surv : npt.NDArray[np.float64]
|
|
226
|
+
Fraction of contrail ice particle number that survive the wake vortex phase.
|
|
231
227
|
air_temperature : npt.NDArray[np.float64]
|
|
232
228
|
ambient temperature for each waypoint, [:math:`K`]
|
|
233
229
|
T_crit_sac : npt.NDArray[np.float64]
|
|
@@ -241,7 +237,6 @@ def ice_particle_number(
|
|
|
241
237
|
npt.NDArray[np.float64]
|
|
242
238
|
initial number of ice particles per distance after the wake vortex phase, [:math:`# m^{-1}`]
|
|
243
239
|
"""
|
|
244
|
-
f_surv = ice_particle_survival_factor(iwc, iwc_1)
|
|
245
240
|
f_activation = ice_particle_activation_rate(air_temperature, T_crit_sac)
|
|
246
241
|
nvpm_ei_n_activated = nvpm_ei_n * f_activation
|
|
247
242
|
return fuel_dist * np.maximum(nvpm_ei_n_activated, min_ice_particle_number_nvpm_ei_n) * f_surv
|
|
@@ -289,7 +284,7 @@ def ice_particle_activation_rate(
|
|
|
289
284
|
return -0.661 * np.exp(d_temp) + 1.0
|
|
290
285
|
|
|
291
286
|
|
|
292
|
-
def
|
|
287
|
+
def ice_particle_survival_fraction(
|
|
293
288
|
iwc: npt.NDArray[np.float64], iwc_1: npt.NDArray[np.float64]
|
|
294
289
|
) -> npt.NDArray[np.float64]:
|
|
295
290
|
"""
|
|
@@ -0,0 +1,403 @@
|
|
|
1
|
+
"""Wave-vortex downwash functions from Unterstrasser (2016).
|
|
2
|
+
|
|
3
|
+
Notes
|
|
4
|
+
-----
|
|
5
|
+
:cite:`unterstrasserPropertiesYoungContrails2016` provides a parameterized model of the
|
|
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.
|
|
9
|
+
|
|
10
|
+
For comparison, CoCiP assumes that ``f_surv`` is equal to the change in the contrail ice water
|
|
11
|
+
content (by mass) before and after the wake vortex phase. However, for larger (smaller) ice
|
|
12
|
+
particles, their survival fraction by number could be smaller (larger) than their survival fraction
|
|
13
|
+
by mass. This is particularly important in the "soot-poor" scenario, for example, in cleaner
|
|
14
|
+
lean-burn engines where their soot emissions can be 3-4 orders of magnitude lower than conventional
|
|
15
|
+
RQL engines.
|
|
16
|
+
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
from __future__ import annotations
|
|
20
|
+
|
|
21
|
+
import numpy as np
|
|
22
|
+
import numpy.typing as npt
|
|
23
|
+
|
|
24
|
+
from pycontrails.models.cocip.wake_vortex import wake_vortex_separation
|
|
25
|
+
from pycontrails.physics import constants, thermo
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def ice_particle_number_survival_fraction(
|
|
29
|
+
air_temperature: npt.NDArray[np.float64],
|
|
30
|
+
rhi_0: npt.NDArray[np.float64],
|
|
31
|
+
ei_h2o: npt.NDArray[np.float64] | float,
|
|
32
|
+
wingspan: npt.NDArray[np.float64] | float,
|
|
33
|
+
true_airspeed: npt.NDArray[np.float64],
|
|
34
|
+
fuel_flow: npt.NDArray[np.float64],
|
|
35
|
+
aei_n: npt.NDArray[np.float64],
|
|
36
|
+
z_desc: npt.NDArray[np.float64],
|
|
37
|
+
) -> npt.NDArray[np.float64]:
|
|
38
|
+
r"""
|
|
39
|
+
Calculate fraction of ice particle number surviving the wake vortex phase and required inputs.
|
|
40
|
+
|
|
41
|
+
This implementation is based on the work of :cite:`unterstrasserPropertiesYoungContrails2016`
|
|
42
|
+
and is an improved estimation compared with
|
|
43
|
+
:func:`contrail_properties.ice_particle_survival_fraction`.
|
|
44
|
+
|
|
45
|
+
Parameters
|
|
46
|
+
----------
|
|
47
|
+
air_temperature : npt.NDArray[np.float64]
|
|
48
|
+
ambient temperature for each waypoint, [:math:`K`]
|
|
49
|
+
rhi_0: npt.NDArray[np.float64]
|
|
50
|
+
Relative humidity with respect to ice at the flight waypoint
|
|
51
|
+
ei_h2o : npt.NDArray[np.float64] | float
|
|
52
|
+
Emission index of water vapor, [:math:`kg \ kg^{-1}`]
|
|
53
|
+
wingspan : npt.NDArray[np.float64] | float
|
|
54
|
+
aircraft wingspan, [:math:`m`]
|
|
55
|
+
true_airspeed : npt.NDArray[np.float64]
|
|
56
|
+
true airspeed for each waypoint, [:math:`m s^{-1}`]
|
|
57
|
+
fuel_flow : npt.NDArray[np.float64]
|
|
58
|
+
Fuel mass flow rate, [:math:`kg s^{-1}`]
|
|
59
|
+
aei_n : npt.NDArray[np.float64]
|
|
60
|
+
Apparent ice crystal number emissions index at contrail formation, [:math:`kg^{-1}`]
|
|
61
|
+
z_desc : npt.NDArray[np.float64]
|
|
62
|
+
Final vertical displacement of the wake vortex, ``dz_max`` in :mod:`wake_vortex.py`,
|
|
63
|
+
[:math:`m`].
|
|
64
|
+
|
|
65
|
+
Returns
|
|
66
|
+
-------
|
|
67
|
+
npt.NDArray[np.float64]
|
|
68
|
+
Fraction of contrail ice particle number that survive the wake vortex phase.
|
|
69
|
+
|
|
70
|
+
References
|
|
71
|
+
----------
|
|
72
|
+
- :cite:`unterstrasserPropertiesYoungContrails2016`
|
|
73
|
+
|
|
74
|
+
Notes
|
|
75
|
+
-----
|
|
76
|
+
- See eq. (3), (9), and (10) in :cite:`unterstrasserPropertiesYoungContrails2016`.
|
|
77
|
+
- For consistency in CoCiP, ``z_desc`` should be calculated using :func:`dz_max` instead of
|
|
78
|
+
using :func:`z_desc_length_scale`.
|
|
79
|
+
"""
|
|
80
|
+
# Length scales
|
|
81
|
+
z_atm = z_atm_length_scale(air_temperature, rhi_0)
|
|
82
|
+
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)
|
|
85
|
+
return _survival_fraction_from_length_scale(z_total)
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def z_total_length_scale(
|
|
89
|
+
aei_n: npt.NDArray[np.float64],
|
|
90
|
+
z_atm: npt.NDArray[np.float64],
|
|
91
|
+
z_emit: npt.NDArray[np.float64],
|
|
92
|
+
z_desc: npt.NDArray[np.float64],
|
|
93
|
+
) -> npt.NDArray[np.float64]:
|
|
94
|
+
"""
|
|
95
|
+
Calculate the total length-scale effect of the wake vortex downwash.
|
|
96
|
+
|
|
97
|
+
Parameters
|
|
98
|
+
----------
|
|
99
|
+
aei_n : npt.NDArray[np.float64]
|
|
100
|
+
Apparent ice crystal number emissions index at contrail formation, [:math:`kg^{-1}`]
|
|
101
|
+
z_atm : npt.NDArray[np.float64]
|
|
102
|
+
Length-scale effect of ambient supersaturation on the ice crystal mass budget, [:math:`m`]
|
|
103
|
+
z_emit : npt.NDArray[np.float64]
|
|
104
|
+
Length-scale effect of water vapour emissions on the ice crystal mass budget, [:math:`m`]
|
|
105
|
+
z_desc : npt.NDArray[np.float64]
|
|
106
|
+
Final vertical displacement of the wake vortex, `dz_max` in `wake_vortex.py`, [:math:`m`]
|
|
107
|
+
|
|
108
|
+
Returns
|
|
109
|
+
-------
|
|
110
|
+
npt.NDArray[np.float64]
|
|
111
|
+
Total length-scale effect of the wake vortex downwash, [:math:`m`]
|
|
112
|
+
"""
|
|
113
|
+
alpha_base = (aei_n / 2.8e14) ** (-0.18)
|
|
114
|
+
alpha_atm = 1.7 * alpha_base
|
|
115
|
+
alpha_emit = 1.15 * alpha_base
|
|
116
|
+
|
|
117
|
+
z_total = alpha_atm * z_atm + alpha_emit * z_emit - 0.6 * z_desc
|
|
118
|
+
|
|
119
|
+
z_total.clip(min=0.0, out=z_total)
|
|
120
|
+
return z_total
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def z_atm_length_scale(
|
|
124
|
+
air_temperature: npt.NDArray[np.float64],
|
|
125
|
+
rhi_0: npt.NDArray[np.float64],
|
|
126
|
+
*,
|
|
127
|
+
n_iter: int = 10,
|
|
128
|
+
) -> npt.NDArray[np.float64]:
|
|
129
|
+
"""Calculate the length-scale effect of ambient supersaturation on the ice crystal mass budget.
|
|
130
|
+
|
|
131
|
+
Parameters
|
|
132
|
+
----------
|
|
133
|
+
air_temperature : npt.NDArray[np.float64]
|
|
134
|
+
Ambient temperature for each waypoint, [:math:`K`].
|
|
135
|
+
rhi_0 : npt.NDArray[np.float64]
|
|
136
|
+
Relative humidity with respect to ice at the flight waypoint.
|
|
137
|
+
n_iter : int
|
|
138
|
+
Number of iterations, set to 10 as default where ``z_atm`` is accurate to within +-1 m.
|
|
139
|
+
|
|
140
|
+
Returns
|
|
141
|
+
-------
|
|
142
|
+
npt.NDArray[np.float64]
|
|
143
|
+
The effect of the ambient supersaturation on the ice crystal mass budget,
|
|
144
|
+
provided as a length scale equivalent, [:math:`m`].
|
|
145
|
+
|
|
146
|
+
Notes
|
|
147
|
+
-----
|
|
148
|
+
- See eq. (5) in :cite:`unterstrasserPropertiesYoungContrails2016`.
|
|
149
|
+
"""
|
|
150
|
+
# Only perform operation when the ambient condition is supersaturated w.r.t. ice
|
|
151
|
+
issr = rhi_0 > 1.0
|
|
152
|
+
|
|
153
|
+
rhi_issr = rhi_0[issr]
|
|
154
|
+
air_temperature_issr = air_temperature[issr]
|
|
155
|
+
|
|
156
|
+
# Solve non-linear equation numerically using the bisection method
|
|
157
|
+
# Did not use scipy functions because it is unstable when dealing with np.arrays
|
|
158
|
+
z_1 = np.zeros_like(rhi_issr)
|
|
159
|
+
z_2 = np.full_like(rhi_issr, 1000.0)
|
|
160
|
+
lhs = rhi_issr * thermo.e_sat_ice(air_temperature_issr) / air_temperature_issr
|
|
161
|
+
|
|
162
|
+
dry_adiabatic_lapse_rate = constants.g / constants.c_pd
|
|
163
|
+
for _ in range(n_iter):
|
|
164
|
+
z_est = 0.5 * (z_1 + z_2)
|
|
165
|
+
rhs = (thermo.e_sat_ice(air_temperature_issr + dry_adiabatic_lapse_rate * z_est)) / (
|
|
166
|
+
air_temperature_issr + dry_adiabatic_lapse_rate * z_est
|
|
167
|
+
)
|
|
168
|
+
z_1[lhs > rhs] = z_est[lhs > rhs]
|
|
169
|
+
z_2[lhs < rhs] = z_est[lhs < rhs]
|
|
170
|
+
|
|
171
|
+
out = np.zeros_like(rhi_0)
|
|
172
|
+
out[issr] = 0.5 * (z_1 + z_2)
|
|
173
|
+
return out
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
def emitted_water_vapour_concentration(
|
|
177
|
+
ei_h2o: npt.NDArray[np.float64] | float,
|
|
178
|
+
wingspan: npt.NDArray[np.float64] | float,
|
|
179
|
+
true_airspeed: npt.NDArray[np.float64],
|
|
180
|
+
fuel_flow: npt.NDArray[np.float64],
|
|
181
|
+
) -> npt.NDArray[np.float64]:
|
|
182
|
+
r"""
|
|
183
|
+
Calculate aircraft-emitted water vapour concentration in the plume.
|
|
184
|
+
|
|
185
|
+
Parameters
|
|
186
|
+
----------
|
|
187
|
+
ei_h2o : npt.NDArray[np.float64] | float
|
|
188
|
+
Emission index of water vapor, [:math:`kg \ kg^{-1}`]
|
|
189
|
+
wingspan : npt.NDArray[np.float64] | float
|
|
190
|
+
aircraft wingspan, [:math:`m`]
|
|
191
|
+
true_airspeed : npt.NDArray[np.float64]
|
|
192
|
+
true airspeed for each waypoint, [:math:`m s^{-1}`]
|
|
193
|
+
fuel_flow : npt.NDArray[np.float64]
|
|
194
|
+
Fuel mass flow rate, [:math:`kg s^{-1}`]
|
|
195
|
+
|
|
196
|
+
Returns
|
|
197
|
+
-------
|
|
198
|
+
npt.NDArray[np.float64]
|
|
199
|
+
Aircraft-emitted water vapour concentration in the plume, [:math:`kg m^{-3}`]
|
|
200
|
+
|
|
201
|
+
Notes
|
|
202
|
+
-----
|
|
203
|
+
- See eq. (6) and (A8) in :cite:`unterstrasserPropertiesYoungContrails2016`.
|
|
204
|
+
"""
|
|
205
|
+
h2o_per_dist = (ei_h2o * fuel_flow) / true_airspeed
|
|
206
|
+
area_p = plume_area(wingspan)
|
|
207
|
+
return h2o_per_dist / area_p
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
def z_emit_length_scale(
|
|
211
|
+
rho_emit: npt.NDArray[np.float64], air_temperature: npt.NDArray[np.float64], *, n_iter: int = 10
|
|
212
|
+
) -> npt.NDArray[np.float64]:
|
|
213
|
+
"""Calculate the length-scale effect of water vapour emissions on the ice crystal mass budget.
|
|
214
|
+
|
|
215
|
+
Parameters
|
|
216
|
+
----------
|
|
217
|
+
rho_emit : npt.NDArray[np.float64] | float
|
|
218
|
+
Aircraft-emitted water vapour concentration in the plume, [:math:`kg m^{-3}`]
|
|
219
|
+
air_temperature : npt.NDArray[np.float64]
|
|
220
|
+
ambient temperature for each waypoint, [:math:`K`]
|
|
221
|
+
n_iter : int
|
|
222
|
+
Number of iterations, set to 10 as default where ``z_emit`` is accurate to within +-1 m.
|
|
223
|
+
|
|
224
|
+
Returns
|
|
225
|
+
-------
|
|
226
|
+
npt.NDArray[np.float64]
|
|
227
|
+
The effect of the aircraft water vapour emission on the ice crystal mass budget,
|
|
228
|
+
provided as a length scale equivalent, [:math:`m`]
|
|
229
|
+
|
|
230
|
+
Notes
|
|
231
|
+
-----
|
|
232
|
+
- See eq. (7) in :cite:`unterstrasserPropertiesYoungContrails2016`.
|
|
233
|
+
"""
|
|
234
|
+
# Solve non-linear equation numerically using the bisection method
|
|
235
|
+
# Did not use scipy functions because it is unstable when dealing with np.arrays
|
|
236
|
+
z_1 = np.zeros_like(rho_emit)
|
|
237
|
+
z_2 = np.full_like(rho_emit, 1000.0)
|
|
238
|
+
|
|
239
|
+
lhs = (thermo.e_sat_ice(air_temperature) / (constants.R_v * air_temperature)) + rho_emit
|
|
240
|
+
|
|
241
|
+
dry_adiabatic_lapse_rate = constants.g / constants.c_pd
|
|
242
|
+
for _ in range(n_iter):
|
|
243
|
+
z_est = 0.5 * (z_1 + z_2)
|
|
244
|
+
rhs = thermo.e_sat_ice(air_temperature + dry_adiabatic_lapse_rate * z_est) / (
|
|
245
|
+
constants.R_v * (air_temperature + dry_adiabatic_lapse_rate * z_est)
|
|
246
|
+
)
|
|
247
|
+
z_1[lhs > rhs] = z_est[lhs > rhs]
|
|
248
|
+
z_2[lhs < rhs] = z_est[lhs < rhs]
|
|
249
|
+
|
|
250
|
+
return 0.5 * (z_1 + z_2)
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
def plume_area(wingspan: npt.NDArray[np.float64] | float) -> npt.NDArray[np.float64] | float:
|
|
254
|
+
"""Calculate area of the wake-vortex plume.
|
|
255
|
+
|
|
256
|
+
Parameters
|
|
257
|
+
----------
|
|
258
|
+
wingspan : npt.NDArray[np.float64] | float
|
|
259
|
+
aircraft wingspan, [:math:`m`]
|
|
260
|
+
|
|
261
|
+
Returns
|
|
262
|
+
-------
|
|
263
|
+
float
|
|
264
|
+
Area of two wake-vortex plumes, [:math:`m^{2}`]
|
|
265
|
+
|
|
266
|
+
Notes
|
|
267
|
+
-----
|
|
268
|
+
- See eq. (A6) and (A7) in :cite:`unterstrasserPropertiesYoungContrails2016`.
|
|
269
|
+
"""
|
|
270
|
+
r_plume = 1.5 + 0.314 * wingspan
|
|
271
|
+
return 2.0 * 2.0 * np.pi * r_plume**2
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
def z_desc_length_scale(
|
|
275
|
+
wingspan: npt.NDArray[np.float64] | float,
|
|
276
|
+
air_temperature: npt.NDArray[np.float64],
|
|
277
|
+
air_pressure: npt.NDArray[np.float64],
|
|
278
|
+
true_airspeed: npt.NDArray[np.float64],
|
|
279
|
+
aircraft_mass: npt.NDArray[np.float64],
|
|
280
|
+
dT_dz: npt.NDArray[np.float64],
|
|
281
|
+
) -> npt.NDArray[np.float64]:
|
|
282
|
+
"""Calculate the final vertical displacement of the wake vortex.
|
|
283
|
+
|
|
284
|
+
Parameters
|
|
285
|
+
----------
|
|
286
|
+
wingspan : npt.NDArray[np.float64] | float
|
|
287
|
+
aircraft wingspan, [:math:`m`]
|
|
288
|
+
air_temperature : npt.NDArray[np.float64]
|
|
289
|
+
ambient temperature for each waypoint, [:math:`K`]
|
|
290
|
+
air_pressure : npt.NDArray[np.float64]
|
|
291
|
+
pressure altitude at each waypoint, [:math:`Pa`]
|
|
292
|
+
true_airspeed : npt.NDArray[np.float64]
|
|
293
|
+
true airspeed for each waypoint, [:math:`m s^{-1}`]
|
|
294
|
+
aircraft_mass : npt.NDArray[np.float64] | float
|
|
295
|
+
aircraft mass for each waypoint, [:math:`kg`]
|
|
296
|
+
dT_dz : npt.NDArray[np.float64]
|
|
297
|
+
potential temperature gradient, [:math:`K m^{-1}`]
|
|
298
|
+
|
|
299
|
+
Returns
|
|
300
|
+
-------
|
|
301
|
+
npt.NDArray[np.float64]
|
|
302
|
+
Final vertical displacement of the wake vortex, [:math:`m`]
|
|
303
|
+
|
|
304
|
+
Notes
|
|
305
|
+
-----
|
|
306
|
+
- See eq. (4) in :cite:`unterstrasserPropertiesYoungContrails2016`.
|
|
307
|
+
"""
|
|
308
|
+
gamma_0 = _initial_wake_vortex_circulation(
|
|
309
|
+
wingspan, air_temperature, air_pressure, true_airspeed, aircraft_mass
|
|
310
|
+
)
|
|
311
|
+
n_bv = thermo.brunt_vaisala_frequency(air_pressure, air_temperature, dT_dz)
|
|
312
|
+
return ((8.0 * gamma_0) / (np.pi * n_bv)) ** 0.5
|
|
313
|
+
|
|
314
|
+
|
|
315
|
+
def _initial_wake_vortex_circulation(
|
|
316
|
+
wingspan: npt.NDArray[np.float64] | float,
|
|
317
|
+
air_temperature: npt.NDArray[np.float64],
|
|
318
|
+
air_pressure: npt.NDArray[np.float64],
|
|
319
|
+
true_airspeed: npt.NDArray[np.float64],
|
|
320
|
+
aircraft_mass: npt.NDArray[np.float64],
|
|
321
|
+
) -> npt.NDArray[np.float64]:
|
|
322
|
+
"""Calculate initial wake vortex circulation.
|
|
323
|
+
|
|
324
|
+
Parameters
|
|
325
|
+
----------
|
|
326
|
+
wingspan : npt.NDArray[np.float64] | float
|
|
327
|
+
aircraft wingspan, [:math:`m`]
|
|
328
|
+
air_temperature : npt.NDArray[np.float64]
|
|
329
|
+
ambient temperature for each waypoint, [:math:`K`]
|
|
330
|
+
air_pressure : npt.NDArray[np.float64]
|
|
331
|
+
pressure altitude at each waypoint, [:math:`Pa`]
|
|
332
|
+
true_airspeed : npt.NDArray[np.float64]
|
|
333
|
+
true airspeed for each waypoint, [:math:`m s^{-1}`]
|
|
334
|
+
aircraft_mass : npt.NDArray[np.float64] | float
|
|
335
|
+
aircraft mass for each waypoint, [:math:`kg`]
|
|
336
|
+
|
|
337
|
+
Returns
|
|
338
|
+
-------
|
|
339
|
+
npt.NDArray[np.float64]
|
|
340
|
+
Initial wake vortex circulation, [:math:`m^{2} s^{-1}`]
|
|
341
|
+
|
|
342
|
+
Notes
|
|
343
|
+
-----
|
|
344
|
+
- This is a measure of the strength/intensity of the wake vortex circulation.
|
|
345
|
+
- See eq. (A1) in :cite:`unterstrasserPropertiesYoungContrails2016`.
|
|
346
|
+
"""
|
|
347
|
+
b_0 = wake_vortex_separation(wingspan)
|
|
348
|
+
rho_air = thermo.rho_d(air_temperature, air_pressure)
|
|
349
|
+
return (constants.g * aircraft_mass) / (rho_air * b_0 * true_airspeed)
|
|
350
|
+
|
|
351
|
+
|
|
352
|
+
def _survival_fraction_from_length_scale(
|
|
353
|
+
z_total: npt.NDArray[np.float64],
|
|
354
|
+
) -> npt.NDArray[np.float64]:
|
|
355
|
+
"""
|
|
356
|
+
Calculate fraction of ice particle number surviving the wake vortex phase.
|
|
357
|
+
|
|
358
|
+
Parameters
|
|
359
|
+
----------
|
|
360
|
+
z_total : npt.NDArray[np.float64]
|
|
361
|
+
Total length-scale effect of the wake vortex downwash, [:math:`m`]
|
|
362
|
+
|
|
363
|
+
Returns
|
|
364
|
+
-------
|
|
365
|
+
npt.NDArray[np.float64]
|
|
366
|
+
Fraction of ice particle number surviving the wake vortex phase
|
|
367
|
+
"""
|
|
368
|
+
f_surv = 0.45 + (1.19 / np.pi) * np.arctan(-1.35 + (z_total / 100.0))
|
|
369
|
+
np.clip(f_surv, 0.0, 1.0, out=f_surv)
|
|
370
|
+
return f_surv
|
|
371
|
+
|
|
372
|
+
|
|
373
|
+
def initial_contrail_depth(
|
|
374
|
+
z_desc: npt.NDArray[np.float64],
|
|
375
|
+
f_surv: npt.NDArray[np.float64],
|
|
376
|
+
) -> npt.NDArray[np.float64]:
|
|
377
|
+
"""Calculate initial contrail depth using :cite:`unterstrasserPropertiesYoungContrails2016`.
|
|
378
|
+
|
|
379
|
+
Parameters
|
|
380
|
+
----------
|
|
381
|
+
z_desc : npt.NDArray[np.float64]
|
|
382
|
+
Final vertical displacement of the wake vortex, ``dz_max`` in :mod:`wake_vortex.py`,
|
|
383
|
+
[:math:`m`].
|
|
384
|
+
f_surv : npt.NDArray[np.float64]
|
|
385
|
+
Fraction of contrail ice particle number that survive the wake vortex phase.
|
|
386
|
+
See :func:`ice_particle_survival_fraction`.
|
|
387
|
+
|
|
388
|
+
Returns
|
|
389
|
+
-------
|
|
390
|
+
npt.NDArray[np.float64]
|
|
391
|
+
Initial contrail depth, [:math:`m`]
|
|
392
|
+
|
|
393
|
+
Notes
|
|
394
|
+
-----
|
|
395
|
+
- See eq. (12), and (13) in :cite:`unterstrasserPropertiesYoungContrails2016`.
|
|
396
|
+
- For consistency in CoCiP, `z_desc` should be calculated using :func:`dz_max` instead of
|
|
397
|
+
using :func:`z_desc_length_scale`.
|
|
398
|
+
"""
|
|
399
|
+
return z_desc * np.where(
|
|
400
|
+
f_surv <= 0.2,
|
|
401
|
+
6.0 * f_surv,
|
|
402
|
+
0.15 * f_surv + (6.0 - 0.15) * 0.2,
|
|
403
|
+
)
|
|
@@ -1,4 +1,25 @@
|
|
|
1
|
-
"""Wave-vortex downwash functions.
|
|
1
|
+
"""Wave-vortex downwash functions.
|
|
2
|
+
|
|
3
|
+
This module includes equations from the original CoCiP model
|
|
4
|
+
:cite:`schumannContrailCirrusPrediction2012`. An alternative set of equations based on
|
|
5
|
+
:cite:`unterstrasserPropertiesYoungContrails2016` is available in
|
|
6
|
+
:py:mod:`unterstrasser_wake_vortex`.
|
|
7
|
+
|
|
8
|
+
Unterstrasser Notes
|
|
9
|
+
-------------------
|
|
10
|
+
|
|
11
|
+
Improved estimation of the survival fraction of the contrail ice crystal number ``f_surv``
|
|
12
|
+
during the wake-vortex phase. This is a parameterised model that is developed based on
|
|
13
|
+
outputs provided by large eddy simulations.
|
|
14
|
+
|
|
15
|
+
For comparison, CoCiP assumes that ``f_surv`` is equal to the change in the contrail ice water
|
|
16
|
+
content (by mass) before and after the wake vortex phase. However, for larger (smaller) ice
|
|
17
|
+
particles, their survival fraction by number could be smaller (larger) than their survival fraction
|
|
18
|
+
by mass. This is particularly important in the "soot-poor" scenario, for example, in cleaner
|
|
19
|
+
lean-burn engines where their soot emissions can be 3-4 orders of magnitude lower than conventional
|
|
20
|
+
RQL engines.
|
|
21
|
+
|
|
22
|
+
"""
|
|
2
23
|
|
|
3
24
|
from __future__ import annotations
|
|
4
25
|
|
|
@@ -11,6 +11,7 @@ from typing import TYPE_CHECKING, Any, NoReturn, TypeVar, overload
|
|
|
11
11
|
import numpy as np
|
|
12
12
|
import numpy.typing as npt
|
|
13
13
|
import pandas as pd
|
|
14
|
+
import xarray as xr
|
|
14
15
|
|
|
15
16
|
import pycontrails
|
|
16
17
|
from pycontrails.core import models
|
|
@@ -20,7 +21,7 @@ from pycontrails.models import humidity_scaling, sac
|
|
|
20
21
|
from pycontrails.models.cocip import cocip, contrail_properties, wake_vortex, wind_shear
|
|
21
22
|
from pycontrails.models.cocipgrid.cocip_grid_params import CocipGridParams
|
|
22
23
|
from pycontrails.models.emissions import Emissions
|
|
23
|
-
from pycontrails.physics import geo, thermo, units
|
|
24
|
+
from pycontrails.physics import constants, geo, thermo, units
|
|
24
25
|
from pycontrails.utils import dependencies
|
|
25
26
|
|
|
26
27
|
if TYPE_CHECKING:
|
|
@@ -122,6 +123,13 @@ class CocipGrid(models.Model):
|
|
|
122
123
|
msg = "Parameter 'radiative_heating_effects' is not yet implemented in CocipGrid"
|
|
123
124
|
raise NotImplementedError(msg)
|
|
124
125
|
|
|
126
|
+
if self.params["unterstrasser_ice_survival_fraction"]:
|
|
127
|
+
msg = (
|
|
128
|
+
"Parameter 'unterstrasser_ice_survival_fraction' is not "
|
|
129
|
+
"yet implemented in CocipGrid"
|
|
130
|
+
)
|
|
131
|
+
raise NotImplementedError(msg)
|
|
132
|
+
|
|
125
133
|
self._target_dtype = np.result_type(*self.met.data.values())
|
|
126
134
|
|
|
127
135
|
@overload
|
|
@@ -304,23 +312,76 @@ class CocipGrid(models.Model):
|
|
|
304
312
|
``time_end``, new slices are selected from the larger ``self.met`` and
|
|
305
313
|
``self.rad`` data. The slicing only occurs in the time domain.
|
|
306
314
|
|
|
315
|
+
The end of currently-used ``met`` and ``rad`` will be used as the start
|
|
316
|
+
of newly-selected met slices when possible to avoid losing and re-loading
|
|
317
|
+
already-loaded met data.
|
|
318
|
+
|
|
307
319
|
If ``self.params["downselect_met"]`` is True, :func:`_downselect_met` has
|
|
308
320
|
already performed a spatial downselection of the met data.
|
|
309
321
|
"""
|
|
310
|
-
|
|
322
|
+
|
|
323
|
+
if met is None:
|
|
311
324
|
# idx is the first index at which self.met.variables["time"].to_numpy() >= time_end
|
|
312
325
|
idx = np.searchsorted(self.met.indexes["time"].to_numpy(), time_end)
|
|
313
326
|
sl = slice(max(0, idx - 1), idx + 1)
|
|
314
327
|
logger.debug("Select met slice %s", sl)
|
|
315
328
|
met = MetDataset(self.met.data.isel(time=sl), copy=False)
|
|
316
329
|
|
|
317
|
-
|
|
330
|
+
elif time_end > met.indexes["time"].to_numpy()[-1]:
|
|
331
|
+
current_times = met.indexes["time"].to_numpy()
|
|
332
|
+
all_times = self.met.indexes["time"].to_numpy()
|
|
333
|
+
# idx is the first index at which all_times >= time_end
|
|
334
|
+
idx = np.searchsorted(all_times, time_end)
|
|
335
|
+
sl = slice(max(0, idx - 1), idx + 1)
|
|
336
|
+
|
|
337
|
+
# case 1: cannot re-use end of current met as start of new met
|
|
338
|
+
if current_times[-1] != all_times[sl.start]:
|
|
339
|
+
logger.debug("Select met slice %s", sl)
|
|
340
|
+
met = MetDataset(self.met.data.isel(time=sl), copy=False)
|
|
341
|
+
# case 2: can re-use end of current met plus one step of new met
|
|
342
|
+
elif sl.start < all_times.size - 1:
|
|
343
|
+
sl = slice(sl.start + 1, sl.stop)
|
|
344
|
+
logger.debug("Reuse end of met and select met slice %s", sl)
|
|
345
|
+
met = MetDataset(
|
|
346
|
+
xr.concat((met.data.isel(time=[-1]), self.met.data.isel(time=sl)), dim="time"),
|
|
347
|
+
copy=False,
|
|
348
|
+
)
|
|
349
|
+
# case 3: can re-use end of current met and nothing else
|
|
350
|
+
else:
|
|
351
|
+
logger.debug("Reuse end of met")
|
|
352
|
+
met = MetDataset(met.data.isel(time=[-1]), copy=False)
|
|
353
|
+
|
|
354
|
+
if rad is None:
|
|
318
355
|
# idx is the first index at which self.rad.variables["time"].to_numpy() >= time_end
|
|
319
356
|
idx = np.searchsorted(self.rad.indexes["time"].to_numpy(), time_end)
|
|
320
357
|
sl = slice(max(0, idx - 1), idx + 1)
|
|
321
358
|
logger.debug("Select rad slice %s", sl)
|
|
322
359
|
rad = MetDataset(self.rad.data.isel(time=sl), copy=False)
|
|
323
360
|
|
|
361
|
+
elif time_end > rad.indexes["time"].to_numpy()[-1]:
|
|
362
|
+
current_times = rad.indexes["time"].to_numpy()
|
|
363
|
+
all_times = self.rad.indexes["time"].to_numpy()
|
|
364
|
+
# idx is the first index at which all_times >= time_end
|
|
365
|
+
idx = np.searchsorted(all_times, time_end)
|
|
366
|
+
sl = slice(max(0, idx - 1), idx + 1)
|
|
367
|
+
|
|
368
|
+
# case 1: cannot re-use end of current rad as start of new rad
|
|
369
|
+
if current_times[-1] != all_times[sl.start]:
|
|
370
|
+
logger.debug("Select rad slice %s", sl)
|
|
371
|
+
rad = MetDataset(self.rad.data.isel(time=sl), copy=False)
|
|
372
|
+
# case 2: can re-use end of current rad plus one step of new rad
|
|
373
|
+
elif sl.start < all_times.size - 1:
|
|
374
|
+
sl = slice(sl.start + 1, sl.stop)
|
|
375
|
+
logger.debug("Reuse end of rad and select rad slice %s", sl)
|
|
376
|
+
rad = MetDataset(
|
|
377
|
+
xr.concat((rad.data.isel(time=[-1]), self.rad.data.isel(time=sl)), dim="time"),
|
|
378
|
+
copy=False,
|
|
379
|
+
)
|
|
380
|
+
# case 3: can re-use end of current rad and nothing else
|
|
381
|
+
else:
|
|
382
|
+
logger.debug("Reuse end of rad")
|
|
383
|
+
rad = MetDataset(rad.data.isel(time=[-1]), copy=False)
|
|
384
|
+
|
|
324
385
|
return met, rad
|
|
325
386
|
|
|
326
387
|
def _attach_verbose_outputs_evolution(self, contrail_list: list[GeoVectorDataset]) -> None:
|
|
@@ -398,6 +459,17 @@ class CocipGrid(models.Model):
|
|
|
398
459
|
nominal_segment_length=segment_length,
|
|
399
460
|
attrs=attrs,
|
|
400
461
|
)
|
|
462
|
+
|
|
463
|
+
if self.params["compute_atr20"]:
|
|
464
|
+
self.source["global_yearly_mean_rf_per_m"] = (
|
|
465
|
+
self.source["ef_per_m"].data
|
|
466
|
+
/ constants.surface_area_earth
|
|
467
|
+
/ constants.seconds_per_year
|
|
468
|
+
)
|
|
469
|
+
self.source["atr20_per_m"] = (
|
|
470
|
+
self.params["global_rf_to_atr20_factor"]
|
|
471
|
+
* self.source["global_yearly_mean_rf_per_m"].data
|
|
472
|
+
)
|
|
401
473
|
else:
|
|
402
474
|
self.source = result_merge_source(
|
|
403
475
|
result=summary,
|
|
@@ -406,6 +478,18 @@ class CocipGrid(models.Model):
|
|
|
406
478
|
nominal_segment_length=segment_length,
|
|
407
479
|
attrs=attrs,
|
|
408
480
|
)
|
|
481
|
+
|
|
482
|
+
if self.params["compute_atr20"]:
|
|
483
|
+
self.source["global_yearly_mean_rf_per_m"] = (
|
|
484
|
+
self.source["ef_per_m"]
|
|
485
|
+
/ constants.surface_area_earth
|
|
486
|
+
/ constants.seconds_per_year
|
|
487
|
+
)
|
|
488
|
+
self.source["atr20_per_m"] = (
|
|
489
|
+
self.params["global_rf_to_atr20_factor"]
|
|
490
|
+
* self.source["global_yearly_mean_rf_per_m"]
|
|
491
|
+
)
|
|
492
|
+
|
|
409
493
|
return self.source
|
|
410
494
|
|
|
411
495
|
# ---------------------------
|
|
@@ -1459,11 +1543,11 @@ def find_initial_persistent_contrails(
|
|
|
1459
1543
|
air_pressure_1=air_pressure_1,
|
|
1460
1544
|
)
|
|
1461
1545
|
iwc_1 = contrail_properties.iwc_post_wake_vortex(iwc, iwc_ad)
|
|
1546
|
+
f_surv = contrail_properties.ice_particle_survival_fraction(iwc, iwc_1)
|
|
1462
1547
|
n_ice_per_m = contrail_properties.ice_particle_number(
|
|
1463
1548
|
nvpm_ei_n=nvpm_ei_n,
|
|
1464
1549
|
fuel_dist=fuel_dist,
|
|
1465
|
-
|
|
1466
|
-
iwc_1=iwc_1,
|
|
1550
|
+
f_surv=f_surv,
|
|
1467
1551
|
air_temperature=air_temperature,
|
|
1468
1552
|
T_crit_sac=T_crit_sac,
|
|
1469
1553
|
min_ice_particle_number_nvpm_ei_n=params["min_ice_particle_number_nvpm_ei_n"],
|
|
@@ -2241,6 +2325,14 @@ def _contrail_grid_variable_attrs() -> dict[str, dict[str, str]]:
|
|
|
2241
2325
|
"long_name": "Ice water content after the wake vortex phase",
|
|
2242
2326
|
"units": "kg_h2o / kg_air",
|
|
2243
2327
|
},
|
|
2328
|
+
"global_yearly_mean_rf_per_m": {
|
|
2329
|
+
"long_name": "Global yearly mean RF per meter of flight trajectory",
|
|
2330
|
+
"units": "W / m**2 / m",
|
|
2331
|
+
},
|
|
2332
|
+
"atr20_per_m": {
|
|
2333
|
+
"long_name": "Average Temperature Response over a 20 year horizon",
|
|
2334
|
+
"units": "K / m",
|
|
2335
|
+
},
|
|
2244
2336
|
}
|
|
2245
2337
|
|
|
2246
2338
|
|
|
@@ -2249,7 +2341,12 @@ def _supported_verbose_outputs_formation() -> set[str]:
|
|
|
2249
2341
|
|
|
2250
2342
|
Uses output of :func:`_contrail_grid_variable_attrs` as a source of truth.
|
|
2251
2343
|
"""
|
|
2252
|
-
return set(_contrail_grid_variable_attrs()) - {
|
|
2344
|
+
return set(_contrail_grid_variable_attrs()) - {
|
|
2345
|
+
"contrail_age",
|
|
2346
|
+
"ef_per_m",
|
|
2347
|
+
"global_yearly_mean_rf_per_m",
|
|
2348
|
+
"atr20_per_m",
|
|
2349
|
+
}
|
|
2253
2350
|
|
|
2254
2351
|
|
|
2255
2352
|
def _warn_not_wrap(met: MetDataset) -> None:
|
pycontrails/models/issr.py
CHANGED
|
@@ -59,7 +59,7 @@ class ISSR(Model):
|
|
|
59
59
|
>>> out1 = model.eval()
|
|
60
60
|
>>> issr1 = out1["issr"]
|
|
61
61
|
>>> issr1.proportion # Get proportion of values with ice supersaturation
|
|
62
|
-
0.
|
|
62
|
+
0.11414134603859523
|
|
63
63
|
|
|
64
64
|
>>> # Run with a lower threshold
|
|
65
65
|
>>> out2 = model.eval(rhi_threshold=0.95)
|
|
@@ -1,30 +1,30 @@
|
|
|
1
|
-
pycontrails-0.50.
|
|
2
|
-
pycontrails-0.50.
|
|
3
|
-
pycontrails-0.50.
|
|
4
|
-
pycontrails-0.50.
|
|
5
|
-
pycontrails-0.50.
|
|
6
|
-
pycontrails-0.50.
|
|
7
|
-
pycontrails/_version.py,sha256=
|
|
1
|
+
pycontrails-0.50.1.dist-info/RECORD,,
|
|
2
|
+
pycontrails-0.50.1.dist-info/LICENSE,sha256=gJ-h7SFFD1mCfR6a7HILvEtodDT6Iig8bLXdgqR6ucA,10175
|
|
3
|
+
pycontrails-0.50.1.dist-info/WHEEL,sha256=t3aNIuHimB-eyeerOmc50nLML0b4_R6yjydcdcJkGHg,108
|
|
4
|
+
pycontrails-0.50.1.dist-info/NOTICE,sha256=gKI8DcN1WhiXB2SFRKDogcjONldGubTvBxiOYdC4CXU,1926
|
|
5
|
+
pycontrails-0.50.1.dist-info/top_level.txt,sha256=Z8J1R_AiBAyCVjNw6jYLdrA68PrQqTg0t3_Yek_IZ0Q,29
|
|
6
|
+
pycontrails-0.50.1.dist-info/METADATA,sha256=1nCqWg9spKCx-KVQMf5MoR3P6UdQUZduyUkcSpNw0UA,8367
|
|
7
|
+
pycontrails/_version.py,sha256=K3H_dVBJVDHyT8uwW9bvlPeCpq530Wdl5b-toKl3Ogk,413
|
|
8
8
|
pycontrails/__init__.py,sha256=c_Vtz7CvdiVAL8ggluash9-8tGcLO_5Vvu-3_Ie47CE,1985
|
|
9
9
|
pycontrails/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
10
|
-
pycontrails/core/vector.py,sha256=
|
|
11
|
-
pycontrails/core/rgi_cython.cpython-39-darwin.so,sha256=
|
|
10
|
+
pycontrails/core/vector.py,sha256=P61Rv1M040A1SRHmdWVR_RfXcWa6faM330n-9499ilI,71722
|
|
11
|
+
pycontrails/core/rgi_cython.cpython-39-darwin.so,sha256=PNeZZB5Ycm381jj2txYbYR_zyMegM7e0pwm8dOfIYWA,317628
|
|
12
12
|
pycontrails/core/models.py,sha256=sipUAH_r0A1TxQ0yBjaPj8WC33Piw34ABHyrmRePfhg,39003
|
|
13
|
-
pycontrails/core/interpolation.py,sha256=
|
|
13
|
+
pycontrails/core/interpolation.py,sha256=FFYdUnTzGnkjoGs-FKsK3Y3nSK5lQI-mpq5HPALZ-y4,25495
|
|
14
14
|
pycontrails/core/fleet.py,sha256=84oTb8RJ3-bPVvZn3O2ljMEZLwJ9Q-E455ZzQJ4QaQU,16075
|
|
15
|
-
pycontrails/core/flight.py,sha256=
|
|
15
|
+
pycontrails/core/flight.py,sha256=B5CrKxuRyOD7Er7LPph6txFiLjdpNXQAgtbKOqsEPmg,76295
|
|
16
16
|
pycontrails/core/fuel.py,sha256=kJZ3P1lPm1L6rdPREM55XQ-VfJ_pt35cP4sO2Nnvmjs,4332
|
|
17
17
|
pycontrails/core/polygon.py,sha256=eX0gLL6OA_3658hjT0uraoyQWn4WvKYS-uxh3YCkTZY,17685
|
|
18
18
|
pycontrails/core/datalib.py,sha256=tqYR69vVeY6LsKgtOYkZU1sEJNrbrwjqRPC1eK8mQaU,23270
|
|
19
19
|
pycontrails/core/cache.py,sha256=rCBZiRSVoFkRwf_ay7O-eba2PaYoXdlJSz1znZPuOQk,27957
|
|
20
20
|
pycontrails/core/__init__.py,sha256=4ZE7x1gMa_Q7GcgcWpPP9fQ-sTulCXWmMjsCJ0odakY,840
|
|
21
21
|
pycontrails/core/flightplan.py,sha256=s7tHbjMFbHAJkSWV6hPkghuW6jDb1n5UhWAo9XbJ9z0,7349
|
|
22
|
-
pycontrails/core/met.py,sha256=
|
|
22
|
+
pycontrails/core/met.py,sha256=1TGotmJK6Ks_hmC8QGNknFuAWoNLiSFQuNAveCLcPyk,92303
|
|
23
23
|
pycontrails/core/aircraft_performance.py,sha256=4yNGfArt741HNFjxpWvEu86BTzie9LaPjC4KH3dXYts,21966
|
|
24
24
|
pycontrails/core/airports.py,sha256=aeyAXVkioIRomrP79UtNrxindL4f1DJyXFaojZCuBBw,6758
|
|
25
25
|
pycontrails/core/met_var.py,sha256=EhrLGdrCAp8cKb-3Whd_ttLMZn4_zLMhE-QyFinESqo,9197
|
|
26
26
|
pycontrails/core/coordinates.py,sha256=vVITA90x0Jx-UQG2XMm3JAKKsIrFUcU861xV-L9czTI,5291
|
|
27
|
-
pycontrails/datalib/goes.py,sha256=
|
|
27
|
+
pycontrails/datalib/goes.py,sha256=Myz53yENqopOOsVT7K-M16SLvoBPPnOg-14fq6DvzdQ,26359
|
|
28
28
|
pycontrails/datalib/__init__.py,sha256=s5b8W6scXgespKZjPcaC-8jyLd3FqZqN0BZccLxaGuE,237
|
|
29
29
|
pycontrails/datalib/ecmwf/arco_era5.py,sha256=wwBIXZxRRV98g42kNYalirsn9hmq3QBSD_4zdoIEwEg,20258
|
|
30
30
|
pycontrails/datalib/ecmwf/era5.py,sha256=dGEqXtjuE0OgWxOtkMmBtegeMmo1n6nb2cUrNkXx7HM,18285
|
|
@@ -51,7 +51,7 @@ pycontrails/utils/dependencies.py,sha256=ATP45xYdUbIyGFzgbOe5SbokMytvB84TcexUEFn
|
|
|
51
51
|
pycontrails/models/pcc.py,sha256=7hIlg_4-F6Ce7KVFyuIZBZY6uDr1h4KRMqBDlpGkzHE,11116
|
|
52
52
|
pycontrails/models/tau_cirrus.py,sha256=yNYw4ukT68w2ATGFZr3p8AZxB6A2xufXQq7XP2U51y0,5026
|
|
53
53
|
pycontrails/models/__init__.py,sha256=dQTOLQb7RdUdUwslt5se__5y_ymbInBexQmNrmAeOdE,33
|
|
54
|
-
pycontrails/models/issr.py,sha256=
|
|
54
|
+
pycontrails/models/issr.py,sha256=k8yCKCtKLW0ECC0QIs-ID1zbr6vHJfeBm9Shq3oaA3U,7351
|
|
55
55
|
pycontrails/models/sac.py,sha256=B-0acGuYs2ajFYtSMGP4LO328OsAjQ7v2bnQ5HCw4Ak,15961
|
|
56
56
|
pycontrails/models/accf.py,sha256=20XWID_6aoFLDOfanf5ncay_azPbZSA2NoowJSZbM20,12559
|
|
57
57
|
pycontrails/models/dry_advection.py,sha256=_HJA02GPjlJH2OKhX3BAvQWJX0iV-bOu57hWPl2Ue60,16145
|
|
@@ -68,14 +68,15 @@ pycontrails/models/humidity_scaling/__init__.py,sha256=nqsab_j9BCwMbTfCn4BjXMdhI
|
|
|
68
68
|
pycontrails/models/humidity_scaling/quantiles/era5-quantiles.pq,sha256=tfYhbafF9Z-gGCg6VQ1YBlOaK_01e65Dc6s9b-hQ6Zo,286375
|
|
69
69
|
pycontrails/models/cocip/radiative_forcing.py,sha256=ERuFcYMo0_1iiOricnZ8D4ext23bMnTCeZwg9vd6Vzs,44944
|
|
70
70
|
pycontrails/models/cocip/wind_shear.py,sha256=p8d3iaNzxPA3MoxFEM1ZDKt0aticoD6U9cv0QmbuBzs,3860
|
|
71
|
-
pycontrails/models/cocip/cocip.py,sha256=
|
|
71
|
+
pycontrails/models/cocip/cocip.py,sha256=0ILMcjbgsM00rCXAeo9UkSnUsmvq1XvORhNPI-ReNcM,97591
|
|
72
72
|
pycontrails/models/cocip/output_formats.py,sha256=d9naGliEEVuk2ER0KZEY5R2FHQZIY5NcP6PamlHcBM0,77149
|
|
73
73
|
pycontrails/models/cocip/__init__.py,sha256=7Wy_CnmVqg_Gpg2UhIlisJOJ3naL6c5BBzTSJqdbiM4,902
|
|
74
|
-
pycontrails/models/cocip/cocip_params.py,sha256=
|
|
75
|
-
pycontrails/models/cocip/wake_vortex.py,sha256=
|
|
74
|
+
pycontrails/models/cocip/cocip_params.py,sha256=R4bewge3xLgWYbBbGwd8e8r0NlaFx2IaQPZEfiqJZRI,11392
|
|
75
|
+
pycontrails/models/cocip/wake_vortex.py,sha256=i_OF193KK5BCMdVCgK0_4Aqn55f6rnL4WDWEac8um-w,14421
|
|
76
76
|
pycontrails/models/cocip/cocip_uncertainty.py,sha256=4JtlCVFpLBnPRlvyEp9QFpRfHFK9joSTnxe0NJdONG4,11784
|
|
77
77
|
pycontrails/models/cocip/radiative_heating.py,sha256=YRpwfXgFnf89iuJiIM96q-jbdcMAwlX8QLsADTKMABE,18848
|
|
78
|
-
pycontrails/models/cocip/contrail_properties.py,sha256=
|
|
78
|
+
pycontrails/models/cocip/contrail_properties.py,sha256=tycCxKf8j9GvVYDQBPxjtp6xLll-r00C0XW-w1jGbMI,55594
|
|
79
|
+
pycontrails/models/cocip/unterstrasser_wake_vortex.py,sha256=kDxFpAIkcqqhGmwXoxv3_cSESj1Ur45GbLJF56IACJs,14573
|
|
79
80
|
pycontrails/models/ps_model/__init__.py,sha256=5L-HympF1gJaZ6xiNkIQJygJhkDxM3-ejS_T2z-83hQ,495
|
|
80
81
|
pycontrails/models/ps_model/ps_model.py,sha256=t2NJu8ct5MSCQAqFinZOzpHKSmroOuiJFx0ornCAuEY,31496
|
|
81
82
|
pycontrails/models/ps_model/ps_aircraft_params.py,sha256=7PlJtTo5EP7FLHKQ-EK4jFswYyA8t2dA0xcyMlPiqG8,12104
|
|
@@ -84,7 +85,7 @@ pycontrails/models/ps_model/ps_grid.py,sha256=PyuiGKvknx6x34FGeNoxJ47uVf90Bd-Egb
|
|
|
84
85
|
pycontrails/models/ps_model/static/ps-aircraft-params-20240209.csv,sha256=lZ_gzhOfB-Ul8VUnzyiffYyLTRJ3wab1rlOgh_N-byA,14679
|
|
85
86
|
pycontrails/models/cocipgrid/cocip_grid_params.py,sha256=l4vBPrOKCJDz5Y1uMjmOGVyUcSWgfZtFWbjW968OPz8,5875
|
|
86
87
|
pycontrails/models/cocipgrid/__init__.py,sha256=ar6bF_8Pusbb-myujz_q5ntFylQTNH8yiM8fxP7Zk30,262
|
|
87
|
-
pycontrails/models/cocipgrid/cocip_grid.py,sha256=
|
|
88
|
+
pycontrails/models/cocipgrid/cocip_grid.py,sha256=wHhScJQNXI1Ax7gNFiXPU_lQiQNk4QLvq-TfnwBqAZU,94108
|
|
88
89
|
pycontrails/physics/geo.py,sha256=lqEpTLhex2r-o-6EHSSEiPC8xqZ-NlwwCBob9-cJA_w,30240
|
|
89
90
|
pycontrails/physics/units.py,sha256=W_pOCZ7abjQnUIFhrMEelPTrk18ihJQitPSpnTd2toI,12256
|
|
90
91
|
pycontrails/physics/constants.py,sha256=pHQQmccMUwuNnY4hFtm3L8G2rnUQcfJnroyQr8HAVeM,3146
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|