HITRAN look-up tables¶
HITRAN line-by-line absorption cross-section LUTs and a Beer–Lambert forward model. hitran-api is imported lazily. RTM stack.
hapi_lut
¶
HAPI-driven absorption cross-section look-up tables.
Submodules
config—GasConfig/LUTGridConfig/ATMOSPHERIC_GASES.generator— single-gas fetch / compute / wrap / save pipeline.multi— multi-gas orchestration (separate-per-gas + combined).beers— LUT interpolation + Beer-Lambert transmittance + the differential-ratio form used for plume-enhancement retrievals.
HAPI (hitran-api on PyPI) is imported lazily inside the generator +
multi pipelines — importing this sub-package does not require HAPI, only
building / fetching a LUT does.
GasConfig
dataclass
¶
GasConfig(name: str, molecule_id: int, isotopologue_id: int, nu_min: float, nu_max: float, description: str = '')
Configuration for a single HITRAN gas species.
Attributes:
| Name | Type | Description |
|---|---|---|
name |
str
|
Short name used as HAPI SourceTable id (e.g. 'CH4'). |
molecule_id |
int
|
HITRAN molecule id (see |
isotopologue_id |
int
|
HITRAN isotopologue id (1 = most abundant). |
nu_min |
float
|
Lower wavenumber bound [cm⁻¹]. |
nu_max |
float
|
Upper wavenumber bound [cm⁻¹]. |
description |
str
|
Free-text description for dataset metadata. |
LUTGridConfig
dataclass
¶
LUTGridConfig(T_grid: ndarray = (lambda: np.array([200, 220, 240, 260, 280, 300, 320], dtype=float))(), P_grid: ndarray = (lambda: np.array([0.1, 0.2, 0.4, 0.6, 0.8, 1.0], dtype=float))(), nu_step: float = 0.01, diluent_composition: dict[str, float] | None = None)
Grid configuration shared across gases for a single LUT build.
Attributes:
| Name | Type | Description |
|---|---|---|
T_grid |
ndarray
|
Temperature knots [K]. Should bracket the atmospheric range of interest (upper-troposphere ~200 K to surface ~320 K). |
P_grid |
ndarray
|
Pressure knots [atm]. Should bracket the altitude range of interest (upper-tropo ~0.1 atm to surface ~1.0 atm). |
nu_step |
float
|
Spectral step [cm⁻¹] on the output wavenumber grid. HITRAN lines have HWHMs down to ~0.005 cm⁻¹ at low pressure — step ≤ 0.01 cm⁻¹ for hyperspectral work, 0.05 cm⁻¹ for tutorials. |
diluent_composition |
dict[str, float] | None
|
Optional override for the air diluent fractions. If None, each gas gets {'air': 1-VMR, 'self': VMR}. |
get_diluent_for_gas
¶
Return the HAPI Diluent dict for a given gas.
If an explicit diluent_composition override was supplied at
construction time it is returned verbatim; otherwise a standard
{'air': 1 - VMR, 'self': VMR} split is built from
DEFAULT_VMR_NOMINAL[gas_name] (falling back to 1e-6).
Source code in src/plumax/hapi_lut/config.py
absorption_coefficient
¶
absorption_coefficient(sigma: DataArray | ndarray, *, vmr: float, p_atm: float, T_K: float) -> xr.DataArray | np.ndarray
α(ν) = σ(ν) · N_gas [1/cm].
Source code in src/plumax/hapi_lut/beers.py
air_mass_factor
¶
Plane-parallel two-way AMF for sun → surface → sensor.
Breaks down past ~75° zenith; fine for the tutorial regime.
Source code in src/plumax/hapi_lut/beers.py
beers_law_from_lut
¶
beers_law_from_lut(ds: Dataset, *, vmr: float, T_K: float, p_atm: float, l_vert_cm: float, sza_deg: float, vza_deg: float, var: str = 'absorption_cross_section') -> xr.DataArray
End-to-end: σ-from-LUT → α → τ(ν) for a single homogeneous layer.
Returns an xarray.DataArray carrying the wavenumber + wavelength
coords so callers can plot τ(λ) without re-introducing units.
Source code in src/plumax/hapi_lut/beers.py
interpolate_cross_section
¶
interpolate_cross_section(ds: Dataset, T_K: float, p_atm: float, *, var: str = 'absorption_cross_section') -> xr.DataArray
Return σ(ν) at a given (T, p) via bilinear LUT interpolation.
The LUT dataset must carry temperature [K] and pressure [atm]
coords as in build_lut_dataset. No extrapolation — callers should
clip to the LUT range before interpolating.
Source code in src/plumax/hapi_lut/beers.py
number_density
¶
Return total number density [molecules / cm³] from the ideal-gas law.
plume_ratio_spectrum
¶
plume_ratio_spectrum(ds: Dataset, *, vmr_background: float, vmr_total: float, T_K: float, p_atm: float, l_vert_cm: float, sza_deg: float, vza_deg: float, var: str = 'absorption_cross_section') -> xr.DataArray
Differential-Beer-Lambert ratio τ_total(ν) / τ_bg(ν).
Callers typically pass vmr_total = vmr_background * (1 + enhancement)
where enhancement is the fitting parameter in the plume retrieval.
Source code in src/plumax/hapi_lut/beers.py
transmittance
¶
transmittance(alpha: DataArray | ndarray, *, l_vert_cm: float, sza_deg: float, vza_deg: float) -> xr.DataArray | np.ndarray
τ(ν) = exp(-α · L_vert · AMF).
Source code in src/plumax/hapi_lut/beers.py
build_lut_dataset
¶
build_lut_dataset(gas_config: GasConfig, grid_config: LUTGridConfig, nu_grid: ndarray, sigma_lut: ndarray, wavelength_nm: ndarray) -> xr.Dataset
Wrap the raw LUT arrays in a CF-conformant xarray.Dataset.
Source code in src/plumax/hapi_lut/generator.py
283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 | |
compute_absorption_lut
¶
compute_absorption_lut(gas_config: GasConfig, grid_config: LUTGridConfig, *, cache_dir: Path | str | None = None, progress: bool = True) -> tuple[np.ndarray, np.ndarray, np.ndarray]
Evaluate σ(ν, T, P) on the full LUT grid.
Assumes fetch_hitran_data has already populated the cache. Fails
fast — a HAPI failure at any (T, P) knot, or any non-finite
cross-section in the result, raises rather than leaving silent NaN
holes in the LUT.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
gas_config
|
GasConfig
|
Target gas (molecule + isotopologue + ν range). |
required |
grid_config
|
LUTGridConfig
|
Common T/P/ν-step grid. |
required |
cache_dir
|
Path | str | None
|
Where the fetched HITRAN |
None
|
progress
|
bool
|
Emit per-(T, P) log lines at INFO. |
True
|
Raises:
| Type | Description |
|---|---|
RuntimeError
|
if |
Returns:
| Type | Description |
|---|---|
ndarray
|
|
ndarray
|
|
ndarray
|
|
tuple[ndarray, ndarray, ndarray]
|
|
Source code in src/plumax/hapi_lut/generator.py
200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 | |
default_cache_dir
¶
Return the HITRAN line-data cache directory.
Resolution order
$HAPI_CACHE_DIRif set.~/.cache/plumax/hitran— a per-user cache, so that invoking the generator from anywhere on disk does not leave unignored artefacts in the repository tree. Theprojects/plumax/data/hitran_cachepath used by the notebooks is always passed explicitly.
Source code in src/plumax/hapi_lut/generator.py
fetch_hitran_data
¶
Fetch HITRAN line parameters for gas_config into cache_dir.
HAPI writes a <GasConfig.name>.data + .header pair under
cache_dir, overwriting any existing pair (HAPI's fetch does not
no-op on a cache hit). A successful fetch also records the fetched ν range
in a <name>.range.json sidecar.
When fetch() fails (no network, API error) the previously-cached pair is
only reused if its recorded range covers the requested
[nu_min, nu_max] — otherwise a narrowed cache (e.g. left by
:func:plumax.hapi_lut.multi.create_combined_lut, which fetches over the
gas intersection range) would silently serve a truncated line list, and
absorptionCoefficient_Voigt would return σ ≈ 0 outside it.
Raises:
| Type | Description |
|---|---|
RuntimeError
|
if |
Returns:
| Type | Description |
|---|---|
Path
|
The resolved cache directory. |
Source code in src/plumax/hapi_lut/generator.py
96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 | |
generate_single_gas_lut
¶
generate_single_gas_lut(gas_config: GasConfig, grid_config: LUTGridConfig | None = None, *, cache_dir: Path | str | None = None, output_dir: Path | str | None = None, save: bool = False) -> xr.Dataset
End-to-end single-gas pipeline: fetch → compute → wrap → (optionally) save.
Source code in src/plumax/hapi_lut/generator.py
save_lut
¶
Persist ds to <output_dir>/<gas_name>_absorption_lut.nc (zlib).
Source code in src/plumax/hapi_lut/generator.py
create_combined_lut
¶
create_combined_lut(gases: list[str], grid_config: LUTGridConfig | None = None, *, output_dir: Path | str, cache_dir: Path | str | None = None, filename: str = 'combined_atmospheric_lut.nc') -> Path
Build a single NetCDF with one cross-section variable per gas.
The shared wavenumber axis is the intersection of the per-gas ranges:
[max(nu_min), min(nu_max)]. All variables therefore live on one
(wavenumber, temperature, pressure) coordinate system — convenient
for matched-filter retrievals where CH₄ and CO₂ share the same SWIR window.
output_dir has no default for the same reason as
:func:create_multi_gas_luts — forcing an explicit path keeps
generated artefacts out of the current working directory.
Source code in src/plumax/hapi_lut/multi.py
87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 | |
create_multi_gas_luts
¶
create_multi_gas_luts(gases: list[str], grid_config: LUTGridConfig | None = None, *, output_dir: Path | str, cache_dir: Path | str | None = None, force_recompute: bool = False) -> dict[str, Path]
Build one separate <gas>_absorption_lut.nc per gas.
Gases are looked up in ATMOSPHERIC_GASES. Unknown names are skipped
with a warning rather than raising — lets users pass a superset list.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
gases
|
list[str]
|
List of gas names (e.g. |
required |
grid_config
|
LUTGridConfig | None
|
Common grid; defaults to |
None
|
output_dir
|
Path | str
|
Required. Where to write NetCDF LUTs. No default
is provided to avoid dropping unignored artefacts
into the current working directory; callers should
point this at the project-local git-ignored
|
required |
cache_dir
|
Path | str | None
|
HITRAN cache dir; defaults to |
None
|
force_recompute
|
bool
|
If False, skip gases whose NetCDF already exists. |
False
|
Returns:
| Type | Description |
|---|---|
dict[str, Path]
|
Mapping |