Export weather grids to contrail engines
baro.met.weather.WeatherCube and RadiationCube validate already retrieved
weather and export it without introducing a mandatory contrail-engine dependency.
They do not fetch weather or compute optical depth. A provider offering wind and
temperature alone does not thereby become a complete CoCiP weather source.
Construct a pressure cube with keyword arguments lon, lat, level_hpa,
time_ns, fields, units, and provenance. Coordinates must be strictly
ascending; time is signed integer Unix nanoseconds in UTC. Every field has shape
(longitude, latitude, pressure, time). Descending source coordinates must be
reordered together with the data before construction. Pressure is in hPa.
| Field | Unit |
|---|---|
u, v |
m s-1 |
t |
K |
q, ciwc |
kg kg-1 |
omega |
Pa s-1 |
tau_cirrus |
1 |
geopotential |
m2 s-2 |
CoCiP exports require u, v, t, q, omega, and tau_cirrus. The latter
must be calculated from an appropriate atmospheric column before cropping to
flight levels. Missing or nonfinite values are rejected; missing weather must
never silently become physical contrail extinction.
A radiation cube has the same arguments except level_hpa, with field shape
(longitude, latitude, time). It requires an explicit time_semantics:
valid for instantaneous flux, or interval_midpoint for flux averaged over
an interval and assigned to that interval's midpoint. Fields are
top_net_solar_radiation and top_net_thermal_radiation, both in W m-2.
Accumulated J m-2 and interval-end timestamps must be resolved upstream.
Neither export applies a time shift. For example, an ERA5 hourly flux for the
interval ending at 12:00 belongs at 11:30 after conversion to a mean flux.
met_arrays = weather.to_diffcocip()
rad_arrays = radiation.to_diffcocip()
# Optional dependencies: pycontrails and xarray
met = weather.to_pycontrails()
rad = radiation.to_pycontrails()
The diffcocip export creates plain NumPy dictionaries, including seconds since
2000 and the solar time bridge. It requires at least two points on every axis.
It does not import JAX. The pycontrails export maps units to engine conventions
and marks radiation timestamps as already resolved to prevent a second shift.
to_pycontrails(require_cocip=False) permits partial grids for preprocessing.
coverage describes inclusive coordinate bounds; it is not permission to
extrapolate. provenance is a caller-supplied string mapping retained by the
container and pycontrails export. Keep the container alongside diffcocip arrays
when provenance is needed, since the engine dict contains numerical arrays only.
For example, distinguish ERA5 reanalysis from assembled forecast archives whose
original run times and forecast leads are unavailable.
Retrieve a native window
materialize_weather and materialize_radiation connect these containers to
the targeted ARCO readers. This path supports pressure-level ERA5; it does not
implicitly convert model levels or invent missing fields for other providers.
import numpy as np
from baro.met import (
ArcoRadiationSource, Domain, RangeCache, WeatherRequest,
arco_pressure_levels, materialize_radiation, materialize_weather,
)
source = arco_pressure_levels(fields={
"u": "u_component_of_wind", "v": "v_component_of_wind",
"t": "temperature", "q": "specific_humidity",
"omega": "vertical_velocity", "ciwc": "specific_cloud_ice_water_content",
})
radiation_source = ArcoRadiationSource()
cache = RangeCache(".weather-cache")
source.use_cache(cache)
radiation_source.use_cache(cache)
request = WeatherRequest(
Domain(48, 49, 1, 2),
np.datetime64("2017-05-28T09:30"), np.datetime64("2017-05-28T10:00"),
(25000, 30000), # Pa
)
weather = materialize_weather(source, request, tau_cirrus=True)
radiation = materialize_radiation(radiation_source, request)
Without tau_cirrus=True, only the requested local variables and pressure
neighbours are read. Deriving cirrus optical depth additionally reads temperature
and cloud ice from the top of the native archive down to a lower guard level.
It uses optional pycontrails for the grid calculation, before cropping. If
geopotential is configured it supplies the height field; otherwise pycontrails'
ISA height approximation is used and recorded in provenance. Keep the full
native pressure axis on the source for this calculation. Native variable units
are checked when present; with missing unit metadata, the caller is responsible
for supplying fields in the declared units.
Radiation reads are independent of pressure levels. ERA5 hourly J m**-2
values are divided by 3600 and placed 30 minutes before their archive timestamp.
The negative sign of net thermal flux is retained. These are top-of-atmosphere
fluxes, not irradiance at ground level; no finite height above ground applies.
SurfaceEnricher(ArcoRadiationSource(), wind_components=None) can also sample
them directly at geographic points without altitude.
Windows reject requests outside their source's coverage. They include the
neighbours required by interpolation, but provide no guarantee about future
advection. A pressure margin must account for lower probes and sedimentation.
Materialization holds the requested window in memory; for repeated point
sampling use WeatherSession with its bounded decoded-cube cache instead.