pycontrails 0.49.5__cp310-cp310-win_amd64.whl → 0.50.1__cp310-cp310-win_amd64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of pycontrails might be problematic. Click here for more details.
- pycontrails/_version.py +2 -2
- pycontrails/core/datalib.py +60 -38
- pycontrails/core/flight.py +11 -6
- pycontrails/core/interpolation.py +39 -1
- pycontrails/core/met.py +14 -16
- pycontrails/core/met_var.py +2 -2
- pycontrails/core/models.py +7 -3
- pycontrails/core/rgi_cython.cp310-win_amd64.pyd +0 -0
- pycontrails/core/vector.py +15 -13
- pycontrails/datalib/ecmwf/__init__.py +4 -0
- pycontrails/datalib/ecmwf/arco_era5.py +577 -0
- pycontrails/datalib/ecmwf/common.py +1 -1
- pycontrails/datalib/ecmwf/era5.py +2 -5
- pycontrails/datalib/ecmwf/variables.py +18 -0
- pycontrails/datalib/gfs/gfs.py +2 -2
- pycontrails/datalib/goes.py +14 -12
- pycontrails/models/cocip/cocip.py +48 -8
- pycontrails/models/cocip/cocip_params.py +20 -1
- 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/cocipgrid/cocip_grid_params.py +25 -19
- pycontrails/models/issr.py +1 -1
- pycontrails/physics/constants.py +6 -0
- pycontrails/utils/dependencies.py +13 -11
- {pycontrails-0.49.5.dist-info → pycontrails-0.50.1.dist-info}/METADATA +4 -2
- {pycontrails-0.49.5.dist-info → pycontrails-0.50.1.dist-info}/RECORD +32 -30
- {pycontrails-0.49.5.dist-info → pycontrails-0.50.1.dist-info}/WHEEL +1 -1
- {pycontrails-0.49.5.dist-info → pycontrails-0.50.1.dist-info}/LICENSE +0 -0
- {pycontrails-0.49.5.dist-info → pycontrails-0.50.1.dist-info}/NOTICE +0 -0
- {pycontrails-0.49.5.dist-info → pycontrails-0.50.1.dist-info}/top_level.txt +0 -0
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,12 +26,13 @@ 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
|
)
|
|
32
33
|
from pycontrails.models.cocip.cocip_params import CocipFlightParams
|
|
33
34
|
from pycontrails.models.emissions.emissions import Emissions
|
|
34
|
-
from pycontrails.physics import geo, thermo, units
|
|
35
|
+
from pycontrails.physics import constants, geo, thermo, units
|
|
35
36
|
|
|
36
37
|
logger = logging.getLogger(__name__)
|
|
37
38
|
|
|
@@ -534,10 +535,11 @@ class Cocip(Model):
|
|
|
534
535
|
self.source.size,
|
|
535
536
|
)
|
|
536
537
|
if not intersection.any():
|
|
537
|
-
|
|
538
|
+
msg = (
|
|
538
539
|
"No intersection between flight waypoints and met domain. "
|
|
539
|
-
"Rerun Cocip with
|
|
540
|
+
"Rerun Cocip with met overlapping flight."
|
|
540
541
|
)
|
|
542
|
+
raise ValueError(msg)
|
|
541
543
|
|
|
542
544
|
# STEP 4: Begin met interpolation
|
|
543
545
|
# Unfortunately we use both "u_wind" and "eastward_wind" to refer to the
|
|
@@ -837,9 +839,9 @@ class Cocip(Model):
|
|
|
837
839
|
T_critical_sac = self._sac_flight["T_critical_sac"]
|
|
838
840
|
|
|
839
841
|
# Flight performance parameters
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
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
|
|
843
845
|
|
|
844
846
|
nvpm_ei_n = self._sac_flight.get_data_or_attr("nvpm_ei_n")
|
|
845
847
|
ei_h2o = self._sac_flight.fuel.ei_h2o
|
|
@@ -889,11 +891,27 @@ class Cocip(Model):
|
|
|
889
891
|
air_temperature, air_pressure, air_pressure_1
|
|
890
892
|
)
|
|
891
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
|
+
|
|
892
911
|
n_ice_per_m_1 = contrail_properties.ice_particle_number(
|
|
893
912
|
nvpm_ei_n=nvpm_ei_n,
|
|
894
913
|
fuel_dist=fuel_dist,
|
|
895
|
-
|
|
896
|
-
iwc_1=iwc_1,
|
|
914
|
+
f_surv=f_surv,
|
|
897
915
|
air_temperature=air_temperature,
|
|
898
916
|
T_crit_sac=T_critical_sac,
|
|
899
917
|
min_ice_particle_number_nvpm_ei_n=self.params["min_ice_particle_number_nvpm_ei_n"],
|
|
@@ -1101,6 +1119,9 @@ class Cocip(Model):
|
|
|
1101
1119
|
|
|
1102
1120
|
# Initially set energy forcing to 0 because the contrail just formed (age = 0)
|
|
1103
1121
|
contrail["ef"] = np.zeros_like(contrail["n_ice_per_m"])
|
|
1122
|
+
if self.params["compute_atr20"]:
|
|
1123
|
+
contrail["global_yearly_mean_rf"] = np.zeros_like(contrail["n_ice_per_m"])
|
|
1124
|
+
contrail["atr20"] = np.zeros_like(contrail["n_ice_per_m"])
|
|
1104
1125
|
|
|
1105
1126
|
if not self.params["filter_sac"]:
|
|
1106
1127
|
contrail["sac"] = self._downwash_flight["sac"]
|
|
@@ -1226,6 +1247,10 @@ class Cocip(Model):
|
|
|
1226
1247
|
|
|
1227
1248
|
# Perform all aggregations
|
|
1228
1249
|
agg_dict = {"ef": ["sum"], "age": ["max"]}
|
|
1250
|
+
if self.params["compute_atr20"]:
|
|
1251
|
+
agg_dict["global_yearly_mean_rf"] = ["sum"]
|
|
1252
|
+
agg_dict["atr20"] = ["sum"]
|
|
1253
|
+
|
|
1229
1254
|
rad_keys = ["sdr", "rsr", "olr", "rf_sw", "rf_lw", "rf_net"]
|
|
1230
1255
|
for key in rad_keys:
|
|
1231
1256
|
if self.params["verbose_outputs"]:
|
|
@@ -1236,6 +1261,10 @@ class Cocip(Model):
|
|
|
1236
1261
|
aggregated = grouped.agg(agg_dict)
|
|
1237
1262
|
aggregated.columns = [f"{k1}_{k2}" for k1, k2 in aggregated.columns]
|
|
1238
1263
|
aggregated = aggregated.rename(columns={"ef_sum": "ef", "age_max": "contrail_age"})
|
|
1264
|
+
if self.params["compute_atr20"]:
|
|
1265
|
+
aggregated = aggregated.rename(
|
|
1266
|
+
columns={"global_yearly_mean_rf_sum": "global_yearly_mean_rf", "atr20_sum": "atr20"}
|
|
1267
|
+
)
|
|
1239
1268
|
|
|
1240
1269
|
# Join the two
|
|
1241
1270
|
df = df.join(aggregated)
|
|
@@ -2372,6 +2401,14 @@ def calc_timestep_contrail_evolution(
|
|
|
2372
2401
|
contrail_2["ef"] = energy_forcing_2
|
|
2373
2402
|
contrail_2["age"][energy_forcing_2 == 0.0] = np.timedelta64(0, "ns")
|
|
2374
2403
|
|
|
2404
|
+
if params["compute_atr20"]:
|
|
2405
|
+
contrail_2["global_yearly_mean_rf"] = (
|
|
2406
|
+
contrail_2["ef"] / constants.surface_area_earth / constants.seconds_per_year
|
|
2407
|
+
)
|
|
2408
|
+
contrail_2["atr20"] = (
|
|
2409
|
+
params["global_rf_to_atr20_factor"] * contrail_2["global_yearly_mean_rf"]
|
|
2410
|
+
)
|
|
2411
|
+
|
|
2375
2412
|
# filter contrail_2 by persistent waypoints, if any continuous segments are left
|
|
2376
2413
|
logger.debug(
|
|
2377
2414
|
"Fraction of waypoints surviving: %s / %s",
|
|
@@ -2408,6 +2445,9 @@ def calc_timestep_contrail_evolution(
|
|
|
2408
2445
|
continuous = final_contrail["continuous"]
|
|
2409
2446
|
final_contrail["ef"][~continuous] = 0.0
|
|
2410
2447
|
final_contrail["age"][~continuous] = np.timedelta64(0, "ns")
|
|
2448
|
+
if params["compute_atr20"]:
|
|
2449
|
+
final_contrail["global_yearly_mean_rf"][~continuous] = 0.0
|
|
2450
|
+
final_contrail["atr20"][~continuous] = 0.0
|
|
2411
2451
|
return final_contrail
|
|
2412
2452
|
|
|
2413
2453
|
|
|
@@ -102,7 +102,7 @@ class CocipParams(ModelParams):
|
|
|
102
102
|
met_latitude_buffer: tuple[float, float] = (10.0, 10.0)
|
|
103
103
|
|
|
104
104
|
#: Met level buffer [:math:`hPa`] for Cocip initialization and evolution.
|
|
105
|
-
met_level_buffer: tuple[float, float] = (
|
|
105
|
+
met_level_buffer: tuple[float, float] = (40.0, 40.0)
|
|
106
106
|
|
|
107
107
|
# ---------
|
|
108
108
|
# Filtering
|
|
@@ -136,6 +136,16 @@ class CocipParams(ModelParams):
|
|
|
136
136
|
#: :attr:`CocipGridParams.verbose_outputs_evolution`.
|
|
137
137
|
verbose_outputs: bool = False
|
|
138
138
|
|
|
139
|
+
#: Add additional metric of ATR20 and global yearly mean RF to model output.
|
|
140
|
+
#: These are not standard CoCiP outputs but based on the derivation used
|
|
141
|
+
#: in the first supplement to :cite:`yinPredictingClimateImpact2023`. ATR20 is defined
|
|
142
|
+
#: as the average temperature response over a 20 year horizon.
|
|
143
|
+
compute_atr20: bool = False
|
|
144
|
+
|
|
145
|
+
#: Constant factor used to convert global- and year-mean RF, [:math:`W m^{-2}`],
|
|
146
|
+
#: to ATR20, [:math:`K`], given by :cite:`yinPredictingClimateImpact2023`.
|
|
147
|
+
global_rf_to_atr20_factor: float = 0.0151
|
|
148
|
+
|
|
139
149
|
# ----------------
|
|
140
150
|
# Model parameters
|
|
141
151
|
# ----------------
|
|
@@ -174,6 +184,15 @@ class CocipParams(ModelParams):
|
|
|
174
184
|
#: :attr:`radiative_heating_effects` is enabled.
|
|
175
185
|
max_depth: float = 1500.0
|
|
176
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
|
+
|
|
177
196
|
#: Experimental. Radiative heating effects on contrail cirrus properties.
|
|
178
197
|
#: Terrestrial and solar radiances warm the contrail ice particles and cause
|
|
179
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
|
"""
|