Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

Matched-filter retrieval of methane column enhancement

Matched-filter retrieval on a synthetic plume scene

The matched filter is the maximum-likelihood detector for a known signature tt in additive Gaussian noise with known covariance Σ\Sigma. For a pixel spectrum xx, background mean μ\mu, the abundance estimate is

ε^  =  (x−μ)⊤Σ−1tt⊤Σ−1t .\hat\varepsilon \;=\; \frac{(x - \mu)^\top \Sigma^{-1} t}{t^\top \Sigma^{-1} t}\,.

The target tt is the radiance (or normalised radiance) perturbation a reference plume enhancement ΔVMRref\Delta\mathrm{VMR}_\text{ref} would produce; scaling ε^\hat\varepsilon by ΔVMRref\Delta\mathrm{VMR}_\text{ref} recovers an estimate of each pixel’s true enhancement. This notebook runs the complete pipeline end-to-end on a hyperspectral synthetic scene built from the CH4_4 LUT plus a Gaussian-plume column map (with OU-turbulence meander enabled) — showing:

  1. Scene construction — plug a gauss_puff simulation (with new OU turbulence support) into the nB-LUT → dirty scene → add photon noise.

  2. Target spectrum construction via target_spectrum_normalized_linear (the classical MF assumption).

  3. Background statistics — build_lowrank_covariance_operator returns a structured gaussx.LowRankUpdate covariance plus its background mean in one trimmed pass. gaussx.solve applies the Woodbury identity internally so the matched filter never materialises an O(B3)O(B^3) inverse.

  4. Retrieval — matched_filter_image_op → ε^\hat\varepsilon → ΔX\Delta X map.

  5. Validation — scatter vs. truth, RMSE, per-pixel detection SNR.

The retrieval is hyperspectral here (~500 channels over the ν3\nu_3 Q-branch) because the matched filter’s SNR advantage scales as nchannels\sqrt{n_\text{channels}} — a 2-band multispectral retrieval would need much stronger plumes to be worth the ceremony.

LUT: 10000 channels over 4000-4500 cm⁻¹

1. Hyperspectral “instrument” and atmospheric state

We build a narrow-band SRF with one band per LUT wavenumber sample — effectively treating the LUT grid as the instrument’s native wavelength axis. This is the hyperspectral limit of the nB-LUT machinery and keeps the demo self-contained (no extra resampling).

Hyperspectral SRF: 200 channels (every 50-th LUT sample)

2. Synthetic scene construction

Use gauss_puff.simulate_puff with OU turbulence enabled — this is the new feature from this PR. The OU process adds realistic meander on top of the deterministic wind advection, giving a plume with the kind of off-axis wiggles a matched filter has to handle in production.

ΔX map: (61, 141), peak 10.58 mol/m², nonzero pixels 1275

Build the nB LUT once, inject the plume, and add a touch of photon noise so the retrieval has something to fight against.

<Figure size 1200x350 with 3 Axes>

3. Target spectrum

Use the linearised target (Maclaurin order 1) at a reference enhancement of 1 ppm. The matched filter will return abundance ε^\hat\varepsilon in units of “multiples of the reference target”, which we map back to ΔVMR\Delta\mathrm{VMR} and then to a column enhancement.

Target spectrum: 200 bands, RMS magnitude 6.853e-02
<Figure size 700x300 with 1 Axes>

4. Background statistics

Trimmed mean + low-rank covariance, both robust to the plume contaminating a small fraction of pixels. Default trim_frac=0.1 drops the top/bottom 10% of pixels per channel by brightness.

μ: (200,), cov: LowRankUpdate of rank 8 over 200 bands

5. Run the matched filter

<Figure size 1300x350 with 4 Axes>

6. Validation

Scatter retrieved vs. truth for the plume pixels, compute RMSE, and compare against the no-retrieval baseline (constant ΔX^=0\hat{\Delta X} = 0).

MF RMSE     = 0.903 mol/m²
zero RMSE   = 1.521 mol/m² (no-retrieval baseline)
improvement = 1.7×
<Figure size 500x500 with 1 Axes>

What this notebook demonstrates

  • The radtran stack — SRF → forward-model → nB-LUT → matched filter — cleanly composes to an end-to-end synthetic observation-plus-retrieval loop.

  • The matched filter tracks the plume column map with an RMSE of under 10% of the peak column (≈0.9 vs ≈10.6 mol/m² here, a 1.7× improvement over the no-retrieval baseline) at 0.3% fractional noise and a few hundred channels — even in the presence of OU-turbulence meander.

  • Target-spectrum construction, background estimation, and retrieval are all driven by a single HAPI-LUT dataset. Swapping the absorber (CO2_2, H2_2O, N2_2O, …) is a one-line change.

  • The covariance lives as a structured gaussx.LowRankUpdate throughout, and the matched filter calls gaussx.solve once to produce Σ−1 t\Sigma^{-1}\,t via Woodbury. No dense inverse is ever materialised; the hot path stays O(B⋅k)O(B\cdot k) even as the channel count grows.

Next natural extensions: replace the flat “clean scene” with a real Sentinel-2 or EMIT radiance cube, add the PSF / GSD observation operators from jej_vc_snippets/methane_retrieval/lut_obs_op.py, and swap the linearised target for target_spectrum_normalized_nonlinear when analysing strong plumes.