Changelog#
Version 0.2.0 (Upcoming)#
ERA5 Integration (pyradtran.era5)#
era5_atmosphere=accepts raw ERA5 datasets as-is: CDS NetCDF short names and ARCO-ERA5 Zarr long names (e.g. straight from gcsfs) are normalised automatically (normalize_era5) — no manual renaming or unit attributes needed;millibars/Papressure coordinates handledOzone from ERA5: an
o3field is written into the radiosonde-style atmosphere file (radiosonde … H2O MMR O3 MMR); atmosphere files carry a machine-readable# columns:header that the input builder reads backA gas profile provided by the radiosonde file now disables the corresponding
mol_modifyscalar (ozone_du,h2o_mm) — the measured/reanalysis column is no longer silently rescaledera5_clouds=Trueonrun(): per-pointwc_file/ic_filecloud profiles from ERA5clwc/ciwc(kg/kg → g/m³, altitudes from geopotential, correct 1D cloud-file layer semantics); pass a dict to set effective radii. Explicitparamsclouds orcloud_*_varkwargs take precedence per point, so variation-axis studies compose with ERA5 atmospheresrecommend_atmosphere(lat, time): AFGL background profile matching latitude band and seasonNaN detection for the known twostr instability with optically thick clouds, with a warning that points to
rte_solver disortNew user-guide page: Atmosphere & ERA5 (when to use radiosonde vs atmosphere profiles, ozone interplay, gcsfs workflow)
Unified Parameter Handling#
New
paramsmapping onrun()/execute_simulation_batch(): one place for registry parameters (validated), raw uvspec keywords (escape hatch), and dotted config overrides (simulation_defaults.wavelength_nm)Var("name")marker resolves a parameter per point from a dataset variable; NaN values skip the parameter for that pointParameter registry (
pyradtran.params.REGISTRY) with type, unit, and range validation before any simulation runsds.pyradtran.explain()/Simulation.dry_run(): preview the exact uvspec input file with per-line provenance annotations (no simulation run)Deprecated (still working, with
DeprecationWarning):parameter_overrides=,albedo_var=,surface_temperature_var=,surface_type_var=,altitude_var=, and theSimulation.run_simulation(override_*=...)kwargs
Schema-Backed Validation (from your libRadtran install)#
pyRadtran extracts libRadtran’s own machine-readable option schema (~244 options: argument types, valid ranges, choices, dependencies, documentation) from the local installation and caches it under
~/.pyradtran/— validation always matches the exact binary in useUnknown option names are rejected before any simulation runs, with did-you-mean suggestions; enumerated choices and numeric ranges are enforced (
ic_properties,wc_modify,cloudcover, …)Repeatable options accept list values (one input line per entry); flag options accept
True;Raw(value)bypasses validationpyradtran.describe(name)/pyradtran.search_options(text): the libRadtran manual — signatures, choices, docs — at the Python prompt
Instrument Channels & Jacobians#
convolve_channels()/run(channels=srf): SRF-average spectral results onto achanneldimension (trapezoidal, pure numpy)brightness_temperature(): inverse-Planck conversion of thermal radiancesds.pyradtran.jacobian(param, delta): finite-difference sensitivity kernels via paired batch runs
Provenance#
Result datasets (and saved NetCDF files) record what produced them: package version + history line, the
paramsmapping as JSON, the full merged config as YAML, the uvspec binary path, and the annotated input file of the first point (pyradtran_input_example)wavelength/altitudecoordinates carry units attrs; channel runs record the channel names
Robustness & Failure Reporting#
Per-point
statusvariable (0 ok / 1 failed / 2 skipped) in every resultfailures_<timestamp>.logwith captured stderr; failed runs keep their.inpfile for post-mortemOutput columns are now self-consistent: the
output_userline and the output parser derive from the same code path;lambda(spectral) andzout(multi-altitude) columns are injected automatically. Previously, a spectral config without an explicitlambdacolumn produced silently all-NaN resultsPer-run
output_userandzoutoverrides are honoured by the output parserOutputParserhonours per-run altitudes over config altitudesERA5 humidity unit (RH vs MMR) detected from the atmosphere file header
Documentation & Notebooks#
API reference actually renders:
only_build_toc_files: truehad excluded every autosummary-generated page, leaving all API links dead; the book now builds the full reference (and the build is warning-free)save_master_config()documented as the primary setup path; catalogue short names (afglms,NewGuey2003, …) documented foratmosphere_profile/solar_spectrumRestructured the book: slim landing page with a “where to go next” map, a four-page User Guide (configuration / parameters / results / debugging), tutorials separated from campaign case studies
New executed deep-dive notebook covering the whole parameter system: layers and provenance, validation,
describe(), repeatable options,Raw, status codes, channels, and jacobiansOverhauled all 21 Jupyter Book notebooks: standardized titles, structure, and API usage
Fixed broken TOC references and added 11 previously orphaned notebooks to the book
Filled the “Check your input file!” section with detailed debugging guidance
Expanded installation guide with
~/.pyradtran/config.yamlmaster config documentationExpanded usage guide with sections on clouds, ERA5, batch processing, and
parameter_overridesOverhauled README with configuration section, CI badge, and corrected examples
API Standardization#
Standardized all examples to use
ds.pyradtran.run(config_path=...)as the canonical APIMade libRadtran paths version-agnostic in all YAML configs
CI/CD#
Added GitHub Actions workflow for unit tests (Python 3.9, 3.10, 3.11)
Added Jupyter Book build checks
Bug Fixes (deep audit)#
CloudGenerator.from_era5_datasetarray misalignment: with ascending ERA5 pressure levels, altitudes/pressure were reversed but the cloud-content, cloud-cover, and temperature arrays were not — a surface cloud could come out at stratospheric altitude with a wrong density conversion. All per-level arrays are now reordered togetherGeopotential unit detection: profiles topping out below ~10 km were misread as height-in-metres (10× altitude error). The
zunits attribute now decides the conversion; the magnitude heuristic is only a fallback (era5.cloud_profilesandclouds.py)OutputToXarray.convertcrashed for any two-dimensional result (spectral single-altitude, integrated multi-altitude); reshape now follows the present axes.convert_batchresults are transposed so input dims come first:(time, ..., wavelength, altitude)zoutordering: uvspec hard-errors on unsorted output altitudes. Dataset-provided altitude coordinates, per-runzoutoverrides, and the config line are now sorted and de-duplicated on every pathNaT / missing coordinates: a NaT time crashed the whole batch (including inside xarray’s stacked-index handling); such points are now skipped with
status=2like NaN coordinates, and missing time/latitude/longitude variables raise one clearValueErrorup frontConfig-level
parameter_overridesreach the output parser: anoutput_user/zout/output_quantityset through the config escape hatch shaped the input file but not the parser, silently scrambling column namesBrightness runs: the
albedooutput column is now filtered in the shared column function, so builder and parser stay aligned whereveralbedosits in the column listinspect_cloud_file()shares the batch driver’s cloud construction: NaN inputs return a message instead of crashing, and mixed-phase points preview both profilesjacobian()no longer differentiates thestatusflag (worst status of the two runs is kept);max_workers: nullno longer breaks saving
Error Safety#
run(config=...)copies the config — caller objects are never mutated by per-run adjustments or dotted overrides (SimulationConfig.copy())altitudeas a data variable no longer emits a spuriousDeprecationWarningcleanup_temp_files: truenow also removes parsed.outfiles; failed runs keep their generated cloud files for post-mortemera5_clouds={}means “enabled with defaults” instead of silently offconvolve_channels()raises a clear error for SRFs that do not overlap the result wavelength grid (previously silent inf/NaN)PathsConfigaccepts plain strings;wavelength_nmaccepts the{start:, end:}YAML spellingLibrary no longer calls
logging.basicConfig()(host logging config untouched); exceptions chain their cause (raise ... from e) and pyRadtran errors are no longer double-wrappedStale libRadtran schema caches under
~/.pyradtran/are pruned when a new one is written
Bug Fixes#
Fixed various typos in documentation and notebooks
Unified Python version requirement to
>= 3.9across all documentation
Version 0.1.0 (Development)#
Initial release
Basic pyRadtran functionality with
uvspecwrapperxarray integration via
.pyradtranaccessorYAML configuration system with layered defaults
Parallel simulation execution
Jupyter notebook examples