pycontrails 0.58.0__cp314-cp314-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/__init__.py +70 -0
- pycontrails/_version.py +34 -0
- pycontrails/core/__init__.py +30 -0
- pycontrails/core/aircraft_performance.py +679 -0
- pycontrails/core/airports.py +228 -0
- pycontrails/core/cache.py +889 -0
- pycontrails/core/coordinates.py +174 -0
- pycontrails/core/fleet.py +483 -0
- pycontrails/core/flight.py +2185 -0
- pycontrails/core/flightplan.py +228 -0
- pycontrails/core/fuel.py +140 -0
- pycontrails/core/interpolation.py +702 -0
- pycontrails/core/met.py +2931 -0
- pycontrails/core/met_var.py +387 -0
- pycontrails/core/models.py +1321 -0
- pycontrails/core/polygon.py +549 -0
- pycontrails/core/rgi_cython.cp314-win_amd64.pyd +0 -0
- pycontrails/core/vector.py +2249 -0
- pycontrails/datalib/__init__.py +12 -0
- pycontrails/datalib/_met_utils/metsource.py +746 -0
- pycontrails/datalib/ecmwf/__init__.py +73 -0
- pycontrails/datalib/ecmwf/arco_era5.py +345 -0
- pycontrails/datalib/ecmwf/common.py +114 -0
- pycontrails/datalib/ecmwf/era5.py +554 -0
- pycontrails/datalib/ecmwf/era5_model_level.py +490 -0
- pycontrails/datalib/ecmwf/hres.py +804 -0
- pycontrails/datalib/ecmwf/hres_model_level.py +466 -0
- pycontrails/datalib/ecmwf/ifs.py +287 -0
- pycontrails/datalib/ecmwf/model_levels.py +435 -0
- pycontrails/datalib/ecmwf/static/model_level_dataframe_v20240418.csv +139 -0
- pycontrails/datalib/ecmwf/variables.py +268 -0
- pycontrails/datalib/geo_utils.py +261 -0
- pycontrails/datalib/gfs/__init__.py +28 -0
- pycontrails/datalib/gfs/gfs.py +656 -0
- pycontrails/datalib/gfs/variables.py +104 -0
- pycontrails/datalib/goes.py +757 -0
- pycontrails/datalib/himawari/__init__.py +27 -0
- pycontrails/datalib/himawari/header_struct.py +266 -0
- pycontrails/datalib/himawari/himawari.py +667 -0
- pycontrails/datalib/landsat.py +589 -0
- pycontrails/datalib/leo_utils/__init__.py +5 -0
- pycontrails/datalib/leo_utils/correction.py +266 -0
- pycontrails/datalib/leo_utils/landsat_metadata.py +300 -0
- pycontrails/datalib/leo_utils/search.py +250 -0
- pycontrails/datalib/leo_utils/sentinel_metadata.py +748 -0
- pycontrails/datalib/leo_utils/static/bq_roi_query.sql +6 -0
- pycontrails/datalib/leo_utils/vis.py +59 -0
- pycontrails/datalib/sentinel.py +650 -0
- pycontrails/datalib/spire/__init__.py +5 -0
- pycontrails/datalib/spire/exceptions.py +62 -0
- pycontrails/datalib/spire/spire.py +604 -0
- pycontrails/ext/bada.py +42 -0
- pycontrails/ext/cirium.py +14 -0
- pycontrails/ext/empirical_grid.py +140 -0
- pycontrails/ext/synthetic_flight.py +431 -0
- pycontrails/models/__init__.py +1 -0
- pycontrails/models/accf.py +425 -0
- pycontrails/models/apcemm/__init__.py +8 -0
- pycontrails/models/apcemm/apcemm.py +983 -0
- pycontrails/models/apcemm/inputs.py +226 -0
- pycontrails/models/apcemm/static/apcemm_yaml_template.yaml +183 -0
- pycontrails/models/apcemm/utils.py +437 -0
- pycontrails/models/cocip/__init__.py +29 -0
- pycontrails/models/cocip/cocip.py +2742 -0
- pycontrails/models/cocip/cocip_params.py +305 -0
- pycontrails/models/cocip/cocip_uncertainty.py +291 -0
- pycontrails/models/cocip/contrail_properties.py +1530 -0
- pycontrails/models/cocip/output_formats.py +2270 -0
- pycontrails/models/cocip/radiative_forcing.py +1260 -0
- pycontrails/models/cocip/radiative_heating.py +520 -0
- pycontrails/models/cocip/unterstrasser_wake_vortex.py +508 -0
- pycontrails/models/cocip/wake_vortex.py +396 -0
- pycontrails/models/cocip/wind_shear.py +120 -0
- pycontrails/models/cocipgrid/__init__.py +9 -0
- pycontrails/models/cocipgrid/cocip_grid.py +2552 -0
- pycontrails/models/cocipgrid/cocip_grid_params.py +138 -0
- pycontrails/models/dry_advection.py +602 -0
- pycontrails/models/emissions/__init__.py +21 -0
- pycontrails/models/emissions/black_carbon.py +599 -0
- pycontrails/models/emissions/emissions.py +1353 -0
- pycontrails/models/emissions/ffm2.py +336 -0
- pycontrails/models/emissions/static/default-engine-uids.csv +239 -0
- pycontrails/models/emissions/static/edb-gaseous-v29b-engines.csv +596 -0
- pycontrails/models/emissions/static/edb-nvpm-v29b-engines.csv +215 -0
- pycontrails/models/extended_k15.py +1327 -0
- pycontrails/models/humidity_scaling/__init__.py +37 -0
- pycontrails/models/humidity_scaling/humidity_scaling.py +1075 -0
- pycontrails/models/humidity_scaling/quantiles/era5-model-level-quantiles.pq +0 -0
- pycontrails/models/humidity_scaling/quantiles/era5-pressure-level-quantiles.pq +0 -0
- pycontrails/models/issr.py +210 -0
- pycontrails/models/pcc.py +326 -0
- pycontrails/models/pcr.py +154 -0
- pycontrails/models/ps_model/__init__.py +18 -0
- pycontrails/models/ps_model/ps_aircraft_params.py +381 -0
- pycontrails/models/ps_model/ps_grid.py +701 -0
- pycontrails/models/ps_model/ps_model.py +1000 -0
- pycontrails/models/ps_model/ps_operational_limits.py +525 -0
- pycontrails/models/ps_model/static/ps-aircraft-params-20250328.csv +69 -0
- pycontrails/models/ps_model/static/ps-synonym-list-20250328.csv +104 -0
- pycontrails/models/sac.py +442 -0
- pycontrails/models/tau_cirrus.py +183 -0
- pycontrails/physics/__init__.py +1 -0
- pycontrails/physics/constants.py +117 -0
- pycontrails/physics/geo.py +1138 -0
- pycontrails/physics/jet.py +968 -0
- pycontrails/physics/static/iata-cargo-load-factors-20250221.csv +74 -0
- pycontrails/physics/static/iata-passenger-load-factors-20250221.csv +74 -0
- pycontrails/physics/thermo.py +551 -0
- pycontrails/physics/units.py +472 -0
- pycontrails/py.typed +0 -0
- pycontrails/utils/__init__.py +1 -0
- pycontrails/utils/dependencies.py +66 -0
- pycontrails/utils/iteration.py +13 -0
- pycontrails/utils/json.py +187 -0
- pycontrails/utils/temp.py +50 -0
- pycontrails/utils/types.py +163 -0
- pycontrails-0.58.0.dist-info/METADATA +180 -0
- pycontrails-0.58.0.dist-info/RECORD +122 -0
- pycontrails-0.58.0.dist-info/WHEEL +5 -0
- pycontrails-0.58.0.dist-info/licenses/LICENSE +178 -0
- pycontrails-0.58.0.dist-info/licenses/NOTICE +43 -0
- pycontrails-0.58.0.dist-info/top_level.txt +3 -0
|
@@ -0,0 +1,425 @@
|
|
|
1
|
+
"""Algorithmic Climate Change Functions."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import warnings
|
|
6
|
+
from dataclasses import dataclass
|
|
7
|
+
from typing import Any, overload
|
|
8
|
+
|
|
9
|
+
import xarray as xr
|
|
10
|
+
|
|
11
|
+
import pycontrails
|
|
12
|
+
from pycontrails.core.flight import Flight
|
|
13
|
+
from pycontrails.core.met import MetDataset
|
|
14
|
+
from pycontrails.core.met_var import (
|
|
15
|
+
AirTemperature,
|
|
16
|
+
EastwardWind,
|
|
17
|
+
Geopotential,
|
|
18
|
+
NorthwardWind,
|
|
19
|
+
RelativeHumidity,
|
|
20
|
+
SpecificHumidity,
|
|
21
|
+
)
|
|
22
|
+
from pycontrails.core.models import Model, ModelParams
|
|
23
|
+
from pycontrails.core.vector import GeoVectorDataset
|
|
24
|
+
from pycontrails.datalib import ecmwf
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def wide_body_jets() -> set[str]:
|
|
28
|
+
"""Return a set of wide body jets."""
|
|
29
|
+
return {
|
|
30
|
+
"A332",
|
|
31
|
+
"A333",
|
|
32
|
+
"A338",
|
|
33
|
+
"A339",
|
|
34
|
+
"A342",
|
|
35
|
+
"A343",
|
|
36
|
+
"A345",
|
|
37
|
+
"A356",
|
|
38
|
+
"A359",
|
|
39
|
+
"A388",
|
|
40
|
+
"B762",
|
|
41
|
+
"B763",
|
|
42
|
+
"B764",
|
|
43
|
+
"B772",
|
|
44
|
+
"B773",
|
|
45
|
+
"B778",
|
|
46
|
+
"B779",
|
|
47
|
+
"B788",
|
|
48
|
+
"B789",
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def regional_jets() -> set[str]:
|
|
53
|
+
"""Return a set of regional jets."""
|
|
54
|
+
return {
|
|
55
|
+
"CRJ1",
|
|
56
|
+
"CRJ2",
|
|
57
|
+
"CRJ7",
|
|
58
|
+
"CRJ8",
|
|
59
|
+
"CRJ9",
|
|
60
|
+
"CRJX",
|
|
61
|
+
"E135",
|
|
62
|
+
"E145",
|
|
63
|
+
"E170",
|
|
64
|
+
"E190",
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
@dataclass
|
|
69
|
+
class ACCFParams(ModelParams):
|
|
70
|
+
"""Default ACCF model parameters.
|
|
71
|
+
|
|
72
|
+
See `config-user.yml` definition at
|
|
73
|
+
https://github.com/dlr-pa/climaccf
|
|
74
|
+
"""
|
|
75
|
+
|
|
76
|
+
lat_bound: tuple[float, float] | None = None
|
|
77
|
+
lon_bound: tuple[float, float] | None = None
|
|
78
|
+
|
|
79
|
+
efficacy: bool = True
|
|
80
|
+
efficacy_option: str = "lee_2021"
|
|
81
|
+
|
|
82
|
+
accf_v: str = "V1.0"
|
|
83
|
+
|
|
84
|
+
ch4_scaling: float = 1.0
|
|
85
|
+
co2_scaling: float = 1.0
|
|
86
|
+
cont_scaling: float = 1.0
|
|
87
|
+
h2o_scaling: float = 1.0
|
|
88
|
+
o3_scaling: float = 1.0
|
|
89
|
+
|
|
90
|
+
forecast_step: float | None = None
|
|
91
|
+
|
|
92
|
+
sep_ri_rw: bool = False
|
|
93
|
+
|
|
94
|
+
climate_indicator: str = "ATR"
|
|
95
|
+
|
|
96
|
+
#: The horizontal resolution of the meteorological data in degrees.
|
|
97
|
+
#: If None, it will be inferred from the ``met`` dataset for :class:`MetDataset`
|
|
98
|
+
#: source, otherwise it will be set to 0.5.
|
|
99
|
+
horizontal_resolution: float | None = None
|
|
100
|
+
|
|
101
|
+
emission_scenario: str = "pulse"
|
|
102
|
+
|
|
103
|
+
time_horizon: int = 20
|
|
104
|
+
|
|
105
|
+
pfca: str = "PCFA-ISSR"
|
|
106
|
+
|
|
107
|
+
merged: bool = True
|
|
108
|
+
|
|
109
|
+
#: RHI Threshold
|
|
110
|
+
issr_rhi_threshold: float = 0.9
|
|
111
|
+
issr_temp_threshold: float = 235
|
|
112
|
+
|
|
113
|
+
sac_ei_h2o: float = 1.25
|
|
114
|
+
sac_q: float = 43000000.0
|
|
115
|
+
sac_eta: float = 0.3
|
|
116
|
+
|
|
117
|
+
nox_ei: str = "TTV"
|
|
118
|
+
|
|
119
|
+
PMO: bool = False
|
|
120
|
+
|
|
121
|
+
unit_K_per_kg_fuel: bool = False
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
class ACCF(Model):
|
|
125
|
+
"""Compute Algorithmic Climate Change Functions (ACCF).
|
|
126
|
+
|
|
127
|
+
This class is a wrapper over the DLR / UMadrid library
|
|
128
|
+
`climaccf <https://github.com/dlr-pa/climaccf>`_,
|
|
129
|
+
`DOI: 10.5281/zenodo.6977272 <https://doi.org/10.5281/zenodo.6977272>`_
|
|
130
|
+
|
|
131
|
+
Parameters
|
|
132
|
+
----------
|
|
133
|
+
met : MetDataset
|
|
134
|
+
Dataset containing "air_temperature" and "specific_humidity" variables
|
|
135
|
+
surface : MetDataset, optional
|
|
136
|
+
Dataset containing "surface_solar_downward_radiation" and
|
|
137
|
+
"top_net_thermal_radiation" variables
|
|
138
|
+
|
|
139
|
+
References
|
|
140
|
+
----------
|
|
141
|
+
- :cite:`dietmullerDlrpaClimaccfDataset2022`
|
|
142
|
+
- :cite:`dietmullerPythonLibraryComputing2022`
|
|
143
|
+
|
|
144
|
+
"""
|
|
145
|
+
|
|
146
|
+
name = "accr"
|
|
147
|
+
long_name = "algorithmic climate change functions"
|
|
148
|
+
met_variables = (
|
|
149
|
+
AirTemperature,
|
|
150
|
+
SpecificHumidity,
|
|
151
|
+
ecmwf.PotentialVorticity,
|
|
152
|
+
Geopotential,
|
|
153
|
+
(RelativeHumidity, ecmwf.RelativeHumidity),
|
|
154
|
+
NorthwardWind,
|
|
155
|
+
EastwardWind,
|
|
156
|
+
)
|
|
157
|
+
sur_variables = (ecmwf.SurfaceSolarDownwardRadiation, ecmwf.TopNetThermalRadiation)
|
|
158
|
+
default_params = ACCFParams
|
|
159
|
+
|
|
160
|
+
# This variable won't get used since we are not writing the output
|
|
161
|
+
# anywhere, but the library will complain if it's not defined
|
|
162
|
+
path_lib = "./"
|
|
163
|
+
|
|
164
|
+
def __init__(
|
|
165
|
+
self,
|
|
166
|
+
met: MetDataset,
|
|
167
|
+
surface: MetDataset | None = None,
|
|
168
|
+
params: dict[str, Any] | None = None,
|
|
169
|
+
**params_kwargs: Any,
|
|
170
|
+
) -> None:
|
|
171
|
+
# Normalize ECMWF variables
|
|
172
|
+
variables = self.ecmwf_met_variables()
|
|
173
|
+
met = met.standardize_variables(variables)
|
|
174
|
+
|
|
175
|
+
# If relative humidity is in percentage, convert to a proportion
|
|
176
|
+
if met["relative_humidity"].attrs.get("units") == "%":
|
|
177
|
+
met.data["relative_humidity"] /= 100.0
|
|
178
|
+
met.data["relative_humidity"].attrs["units"] = "1"
|
|
179
|
+
|
|
180
|
+
# Ignore humidity scaling warning
|
|
181
|
+
with warnings.catch_warnings():
|
|
182
|
+
warnings.filterwarnings("ignore", module="pycontrails.core.models")
|
|
183
|
+
super().__init__(met, params=params, **params_kwargs)
|
|
184
|
+
|
|
185
|
+
if surface:
|
|
186
|
+
surface = surface.copy()
|
|
187
|
+
surface = surface.standardize_variables(self.sur_variables)
|
|
188
|
+
surface.data = _rad_instantaneous_to_accumulated(surface.data)
|
|
189
|
+
self.surface = surface
|
|
190
|
+
|
|
191
|
+
@overload
|
|
192
|
+
def eval(self, source: Flight, **params: Any) -> Flight: ...
|
|
193
|
+
|
|
194
|
+
@overload
|
|
195
|
+
def eval(self, source: GeoVectorDataset, **params: Any) -> GeoVectorDataset: ...
|
|
196
|
+
|
|
197
|
+
@overload
|
|
198
|
+
def eval(self, source: MetDataset | None = ..., **params: Any) -> MetDataset: ...
|
|
199
|
+
|
|
200
|
+
def eval(
|
|
201
|
+
self, source: GeoVectorDataset | Flight | MetDataset | None = None, **params: Any
|
|
202
|
+
) -> GeoVectorDataset | Flight | MetDataset:
|
|
203
|
+
"""Evaluate accfs along flight trajectory or on meteorology grid.
|
|
204
|
+
|
|
205
|
+
Parameters
|
|
206
|
+
----------
|
|
207
|
+
source : GeoVectorDataset | Flight | MetDataset | None, optional
|
|
208
|
+
Input GeoVectorDataset or Flight.
|
|
209
|
+
If None, evaluates at the :attr:`met` grid points.
|
|
210
|
+
**params : Any
|
|
211
|
+
Overwrite model parameters before eval
|
|
212
|
+
|
|
213
|
+
Returns
|
|
214
|
+
-------
|
|
215
|
+
GeoVectorDataset | Flight | MetDataArray
|
|
216
|
+
Returns `np.nan` if interpolating outside meteorology grid.
|
|
217
|
+
|
|
218
|
+
Raises
|
|
219
|
+
------
|
|
220
|
+
NotImplementedError
|
|
221
|
+
Raises if input ``source`` is not supported.
|
|
222
|
+
"""
|
|
223
|
+
try:
|
|
224
|
+
from climaccf.accf import GeTaCCFs
|
|
225
|
+
except ModuleNotFoundError as e:
|
|
226
|
+
msg = (
|
|
227
|
+
"ACCF.eval method requires the 'climaccf' package. This can be installed "
|
|
228
|
+
"with 'pip install git+https://github.com/dlr-pa/climaccf.git'."
|
|
229
|
+
)
|
|
230
|
+
raise ModuleNotFoundError(msg) from e
|
|
231
|
+
|
|
232
|
+
self.update_params(params)
|
|
233
|
+
self.set_source(source)
|
|
234
|
+
|
|
235
|
+
if isinstance(self.source, GeoVectorDataset):
|
|
236
|
+
self.downselect_met()
|
|
237
|
+
if hasattr(self, "surface"):
|
|
238
|
+
self.surface = self.source.downselect_met(self.surface)
|
|
239
|
+
|
|
240
|
+
if self.params["horizontal_resolution"] is None:
|
|
241
|
+
if isinstance(self.source, MetDataset):
|
|
242
|
+
# Overwrite horizontal resolution to match met
|
|
243
|
+
longitude = self.source.data["longitude"].values
|
|
244
|
+
latitude = self.source.data["latitude"].values
|
|
245
|
+
if longitude.size > 1:
|
|
246
|
+
hres = abs(longitude[1] - longitude[0])
|
|
247
|
+
self.params["horizontal_resolution"] = float(hres)
|
|
248
|
+
elif latitude.size > 1:
|
|
249
|
+
hres = abs(latitude[1] - latitude[0])
|
|
250
|
+
self.params["horizontal_resolution"] = float(hres)
|
|
251
|
+
else:
|
|
252
|
+
self.params["horizontal_resolution"] = 0.5
|
|
253
|
+
else:
|
|
254
|
+
self.params["horizontal_resolution"] = 0.5
|
|
255
|
+
|
|
256
|
+
p_settings = _get_accf_config(self.params)
|
|
257
|
+
|
|
258
|
+
self.set_source_met()
|
|
259
|
+
self._generate_weather_store(p_settings)
|
|
260
|
+
|
|
261
|
+
# check aircraft type and set in config if needed
|
|
262
|
+
if self.params["nox_ei"] != "TTV":
|
|
263
|
+
if isinstance(self.source, Flight):
|
|
264
|
+
ac = self.source.attrs["aircraft_type"]
|
|
265
|
+
if ac in wide_body_jets():
|
|
266
|
+
p_settings["ac_type"] = "wide-body"
|
|
267
|
+
elif ac in regional_jets():
|
|
268
|
+
p_settings["ac_type"] = "regional"
|
|
269
|
+
else:
|
|
270
|
+
p_settings["ac_type"] = "single-aisle"
|
|
271
|
+
else:
|
|
272
|
+
p_settings["ac_type"] = "wide-body"
|
|
273
|
+
|
|
274
|
+
clim_imp = GeTaCCFs(self)
|
|
275
|
+
clim_imp.get_accfs(**p_settings)
|
|
276
|
+
aCCFs, _ = clim_imp.get_xarray()
|
|
277
|
+
|
|
278
|
+
# assign ACCF outputs to source
|
|
279
|
+
skip = {
|
|
280
|
+
v[0].short_name if isinstance(v, tuple) else v.short_name
|
|
281
|
+
for v in (*self.met_variables, *self.sur_variables)
|
|
282
|
+
}
|
|
283
|
+
maCCFs = MetDataset(aCCFs)
|
|
284
|
+
for key, arr in maCCFs.data.items():
|
|
285
|
+
# skip met variables
|
|
286
|
+
if key in skip:
|
|
287
|
+
continue
|
|
288
|
+
|
|
289
|
+
assert isinstance(key, str)
|
|
290
|
+
if isinstance(self.source, GeoVectorDataset):
|
|
291
|
+
self.source[key] = self.source.intersect_met(maCCFs[key])
|
|
292
|
+
else:
|
|
293
|
+
self.source[key] = arr
|
|
294
|
+
|
|
295
|
+
self.transfer_met_source_attrs()
|
|
296
|
+
self.source.attrs["pycontrails_version"] = pycontrails.__version__
|
|
297
|
+
|
|
298
|
+
return self.source
|
|
299
|
+
|
|
300
|
+
def _generate_weather_store(self, p_settings: dict[str, Any]) -> None:
|
|
301
|
+
from climaccf.weather_store import WeatherStore
|
|
302
|
+
|
|
303
|
+
# The library does not call the coordinates by name, it just slices the
|
|
304
|
+
# underlying data array, so we need to put them in the expected order.
|
|
305
|
+
# It also needs variables to have the ECMWF short name
|
|
306
|
+
if isinstance(self.met, MetDataset):
|
|
307
|
+
ds_met = self.met.data.transpose("time", "level", "latitude", "longitude")
|
|
308
|
+
name_dict = {
|
|
309
|
+
v[0].standard_name if isinstance(v, tuple) else v.standard_name: v[0].short_name
|
|
310
|
+
if isinstance(v, tuple)
|
|
311
|
+
else v.short_name
|
|
312
|
+
for v in self.met_variables
|
|
313
|
+
}
|
|
314
|
+
ds_met = ds_met.rename(name_dict)
|
|
315
|
+
else:
|
|
316
|
+
ds_met = None
|
|
317
|
+
|
|
318
|
+
if hasattr(self, "surface"):
|
|
319
|
+
ds_sur = self.surface.data.squeeze().transpose("time", "latitude", "longitude")
|
|
320
|
+
name_dict = {v.standard_name: v.short_name for v in self.sur_variables}
|
|
321
|
+
ds_sur = ds_sur.rename(name_dict)
|
|
322
|
+
else:
|
|
323
|
+
ds_sur = None
|
|
324
|
+
|
|
325
|
+
ws = WeatherStore(
|
|
326
|
+
ds_met,
|
|
327
|
+
ds_sur,
|
|
328
|
+
ll_resolution=p_settings["horizontal_resolution"],
|
|
329
|
+
forecast_step=p_settings["forecast_step"],
|
|
330
|
+
)
|
|
331
|
+
|
|
332
|
+
if p_settings["lat_bound"] and p_settings["lon_bound"]:
|
|
333
|
+
ws.reduce_domain(
|
|
334
|
+
{
|
|
335
|
+
"latitude": p_settings["lat_bound"],
|
|
336
|
+
"longitude": p_settings["lon_bound"],
|
|
337
|
+
}
|
|
338
|
+
)
|
|
339
|
+
|
|
340
|
+
self.ds = ws.get_xarray()
|
|
341
|
+
self.variable_names = ws.variable_names
|
|
342
|
+
self.pre_variable_names = ws.pre_variable_names
|
|
343
|
+
self.coordinate_names = ws.coordinate_names
|
|
344
|
+
self.pre_coordinate_names = ws.pre_coordinate_names
|
|
345
|
+
self.coordinates_bool = ws.coordinates_bool
|
|
346
|
+
self.aCCF_bool = ws.aCCF_bool
|
|
347
|
+
self.axes = ws.axes
|
|
348
|
+
self.var_xr = ws.var_xr
|
|
349
|
+
|
|
350
|
+
|
|
351
|
+
def _get_accf_config(params: dict[str, Any]) -> dict[str, Any]:
|
|
352
|
+
# a good portion of these will get ignored since we are not producing an
|
|
353
|
+
# output file, but the library will complain if they aren't defined
|
|
354
|
+
return {
|
|
355
|
+
"lat_bound": params["lat_bound"],
|
|
356
|
+
"lon_bound": params["lon_bound"],
|
|
357
|
+
"time_bound": None,
|
|
358
|
+
"horizontal_resolution": params["horizontal_resolution"],
|
|
359
|
+
"forecast_step": params["forecast_step"],
|
|
360
|
+
"NOx_aCCF": True,
|
|
361
|
+
"NOx_EI&F_km": params["nox_ei"],
|
|
362
|
+
"output_format": "netCDF",
|
|
363
|
+
"mean": False,
|
|
364
|
+
"std": False,
|
|
365
|
+
"merged": params["merged"],
|
|
366
|
+
"aCCF-V": params["accf_v"],
|
|
367
|
+
"efficacy": params["efficacy"],
|
|
368
|
+
"efficacy-option": params["efficacy_option"],
|
|
369
|
+
"emission_scenario": params["emission_scenario"],
|
|
370
|
+
"climate_indicator": params["climate_indicator"],
|
|
371
|
+
"TimeHorizon": params["time_horizon"],
|
|
372
|
+
"ac_type": "wide-body",
|
|
373
|
+
"sep_ri_rw": params["sep_ri_rw"],
|
|
374
|
+
"PMO": params["PMO"],
|
|
375
|
+
"aCCF-scalingF": {
|
|
376
|
+
"CH4": params["ch4_scaling"],
|
|
377
|
+
"CO2": params["co2_scaling"],
|
|
378
|
+
"Cont.": params["cont_scaling"],
|
|
379
|
+
"H2O": params["h2o_scaling"],
|
|
380
|
+
"O3": params["o3_scaling"],
|
|
381
|
+
},
|
|
382
|
+
"unit_K/kg(fuel)": params["unit_K_per_kg_fuel"],
|
|
383
|
+
"PCFA": params["pfca"],
|
|
384
|
+
"PCFA-ISSR": {
|
|
385
|
+
"rhi_threshold": params["issr_rhi_threshold"],
|
|
386
|
+
"temp_threshold": params["issr_temp_threshold"],
|
|
387
|
+
},
|
|
388
|
+
"PCFA-SAC": {
|
|
389
|
+
"EI_H2O": params["sac_ei_h2o"],
|
|
390
|
+
"Q": params["sac_q"],
|
|
391
|
+
"eta": params["sac_eta"],
|
|
392
|
+
},
|
|
393
|
+
"Chotspots": False,
|
|
394
|
+
"hotspots_binary": True,
|
|
395
|
+
"color": "Reds",
|
|
396
|
+
"geojson": False,
|
|
397
|
+
"save_path": "./",
|
|
398
|
+
"save_format": "netCDF",
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
|
|
402
|
+
def _rad_instantaneous_to_accumulated(ds: xr.Dataset) -> xr.Dataset:
|
|
403
|
+
"""Convert instantaneous radiation to accumulated radiation."""
|
|
404
|
+
|
|
405
|
+
for name, da in ds.items():
|
|
406
|
+
try:
|
|
407
|
+
unit = da.attrs["units"]
|
|
408
|
+
except KeyError as e:
|
|
409
|
+
msg = (
|
|
410
|
+
f"Radiation data contains '{name}' variable "
|
|
411
|
+
"but units are not specified. Provide units in the "
|
|
412
|
+
f"rad['{name}'].attrs passed into ACCF."
|
|
413
|
+
)
|
|
414
|
+
raise KeyError(msg) from e
|
|
415
|
+
|
|
416
|
+
if unit == "J m**-2":
|
|
417
|
+
continue
|
|
418
|
+
if unit != "W m**-2":
|
|
419
|
+
msg = f"Unexpected units '{unit}' for '{name}'. Expected 'J m**-2' or 'W m**-2'."
|
|
420
|
+
raise ValueError(msg)
|
|
421
|
+
|
|
422
|
+
# Convert from W m**-2 to J m**-2
|
|
423
|
+
ds[name] = da.assign_attrs(units="J m**-2") * 3600.0
|
|
424
|
+
|
|
425
|
+
return ds
|