Atmosphere & ERA5#
Every simulation needs a vertical profile of pressure, temperature and trace gases. pyRadtran gives you three sources, which combine rather than compete:
Background profile (
atmosphere_file) — one of the AFGL climatological standard atmospheres shipped with libRadtran. Always present.Measured / reanalysis profile (
radiosonde) — a real p/T/H2O (and optionally O3) column, from an ERA5 dataset or an IGRA2 radiosonde. Overlays the background.Scalar column scalings (
ozone_du,h2o_mm/mol_modify …) — rescale a gas column to a total value.
How the layers combine#
libRadtran always starts from the atmosphere_file. When a
radiosonde profile is given, it replaces pressure, temperature and
the provided gas profiles from the surface up to the profile top; above
that, and for every gas the profile does not contain, the background
profile fills in (shifted so the two match at the transition). A -1
value at any profile level also falls back to the background.
That means the background still matters when you use ERA5 or a radiosonde: pick one that matches the season and latitude so the stratosphere and the unmeasured gases stay consistent.
import pyradtran
pyradtran.recommend_atmosphere(latitude=78.0, time="2022-03-15")
# 'afglsw' (sub-arctic winter)
Set it in your YAML config (paths.atmosphere_profile: afglsw) or per
run via params={"atmosphere_file": ...}.
When to use which source#
Situation |
Use |
|---|---|
Idealised / sensitivity study |
background profile only |
You know the total ozone / water column |
background + |
Case study with reanalysis data |
|
Co-located sounding available |
radiosonde file |
ERA5 datasets are accepted as-is#
era5_atmosphere= takes a raw ERA5 dataset — CDS NetCDF short names
(t, q, o3, pressure_level, …) or ARCO-ERA5 Zarr long names
(temperature, specific_humidity, level, …). Names, coordinate
conventions and pressure units are normalised automatically
(pyradtran.normalize_era5), so this works directly:
import gcsfs
import xarray as xr
gcs = gcsfs.GCSFileSystem(token="anon")
era5 = xr.open_zarr(
gcs.get_mapper(
"gs://gcp-public-data-arco-era5/ar/full_37-1h-0p25deg-chunk-1.zarr-v3"
),
chunks="auto",
consolidated=True,
)
# Subset (and .load()) your region/time first — per-point access into
# the full global archive is slow.
era5 = era5.sel(
time=slice("2022-03-01", "2022-03-31"),
latitude=slice(80, 75),
longitude=slice(10, 20),
).load()
result = ds.pyradtran.run(
config_path="config/thermal.yaml",
era5_atmosphere=era5,
)
One atmosphere file is written per unique (time, lat, lon) in your input dataset, selected nearest-neighbour from the ERA5 data.
Ozone#
If the ERA5 dataset contains an ozone field (o3 /
ozone_mass_mixing_ratio), the ozone profile is written into the
atmosphere file alongside humidity. In that case the config
ozone_du scaling is not applied — the profile wins, and rescaling
it with a scalar would silently discard the reanalysis information.
Without an ozone field, ozone_du scales the background ozone column
as usual. The same rule applies to water vapour: a radiosonde/ERA5
profile disables the h2o_mm scaling.
To vary ozone as an experiment axis, drop o3 from the ERA5 dataset
and use params={"mol_modify O3": Var("ozone_du")} instead.
Clouds from ERA5#
The same dataset can drive clouds. era5_clouds=True converts the
clwc / ciwc fields (kg/kg → g/m³ via the ideal-gas air density,
altitudes from geopotential) into per-point wc_file / ic_file
profiles:
result = ds.pyradtran.run(
config_path="config/thermal.yaml",
era5_atmosphere=era5,
era5_clouds=True, # or {"reff_water_um": 8.0}
)
ERA5 carries no droplet sizes; effective radii default to 10 µm (liquid) and 20 µm (ice) and can be set via the dict form.
Anything more explicit wins over ERA5 clouds for the points where both
exist: a wc_file/ic_file in params, or the cloud_*_var
variation-axis kwargs (see below).
Warning
For cloudy runs use rte_solver: disort. The fast twostr solver is
numerically unstable for some optically thick cloud layers and returns
NaN at individual wavelengths, which poisons integrated (broadband)
results. pyRadtran logs a warning when it detects this.
Keeping a variation axis (Shupe & Intrieri-style studies)#
Parameter-space studies — e.g. mapping cloud radiative effect over LWP
× albedo × SZA — keep working exactly as before: put the cloud
parameters on their own dataset dimensions and reference them with the
cloud_*_var kwargs (or params + Var):
ds = xr.Dataset(
coords={"time": times, "latitude": ("time", lats),
"longitude": ("time", lons)},
data_vars={
"lwc": (("lwc",), lwcs), # variation axis
"reff": (("reff",), reffs), # variation axis
"cth": (("lwc",), cth),
"cbh": (("lwc",), cbh),
},
)
result = ds.pyradtran.run(
config_path="config/arctic_cloud_experiment_thermal.yaml",
cloud_wc_var="lwc", cloud_reff_var="reff",
cloud_top_var="cth", cloud_bottom_var="cbh",
)
You can combine both: era5_atmosphere= for a realistic atmosphere
plus a synthetic cloud grid for the experiment — the explicit cloud
kwargs override the ERA5 clouds, and with era5_clouds unset the ERA5
data only supplies the gas/temperature profiles.
Radiosondes (IGRA2)#
RadiosondeAtmosphereGenerator finds the closest active IGRA2 station
and sounding in time and writes the same file format
(H2O RH columns). Files produced by either path carry a
machine-readable # columns: header, so the input builder always emits
a matching radiosonde option line — no unit mismatch possible.