ModulesBands

Band convolution — to_platform()

BioSNICAR outputs spectral albedo at 480 wavelengths (0.205–4.995 µm, 10 nm resolution). Satellites and climate models use coarser spectral bands. The to_platform() function maps the high-resolution spectrum onto platform-specific bands via SRF convolution (satellites) or flux-weighted interval averaging (GCMs).

Quick start

from biosnicar import run_model
 
# Single run → Sentinel-2 bands
s2 = run_model(solzen=50, rds=1000).to_platform("sentinel2")
print(s2.B3, s2.NDSI)
 
# Single run → CESM 2-band
cesm = run_model(solzen=50).to_platform("cesm2band")
print(cesm.vis, cesm.nir)
from biosnicar.drivers.sweep import parameter_sweep
 
# Parameter sweep → band columns appended to DataFrame
df = parameter_sweep(
    params={"rds": [500, 1000], "solzen": [50, 60]},
).to_platform("sentinel2")
 
print(df[["rds", "solzen", "BBA", "B3", "NDSI"]])

Three entry points

There are three ways to call to_platform(), depending on context:

1. Direct function call

from biosnicar.bands import to_platform
 
o = run_model(solzen=50, rds=1000)
result = to_platform(o.albedo, "sentinel2", flx_slr=o.flx_slr)

2. On Outputs (chaining from run_model)

result = run_model(solzen=50, rds=1000).to_platform("sentinel2")

3. On SweepResult (chaining from parameter_sweep)

df = parameter_sweep(params={"rds": [500, 1000]}).to_platform("sentinel2")

When chaining from a sweep, band and index columns are appended to the DataFrame. Pass multiple platforms to get prefixed columns:

df = parameter_sweep(
    params={"rds": [500, 1000]},
).to_platform("sentinel2", "modis")
 
# Columns: rds, BBA, ..., sentinel2_B3, sentinel2_NDSI, ..., modis_B1, modis_NDSI, ...

BandResult container

All single-run calls return a BandResult object with:

  • band_names — list of band attribute names (e.g. ["B1", "B2", ..., "B12"])
  • index_names — list of spectral index names (e.g. ["NDSI", "NDVI", "II"])
  • as_dict() — flat dict of all bands and indices
  • Attribute accessresult.B3, result.NDSI, etc.
r = run_model(solzen=50).to_platform("sentinel2")
print(r)             # pretty-printed summary
print(r.as_dict())   # {"B1": 0.82, "B2": 0.81, ..., "NDSI": 0.73, ...}

Supported platforms

Satellite platforms

Satellite bands use spectral response function (SRF) convolution. The band-averaged albedo for band $k$ is:

$$\bar{\alpha}_k = \frac{\sum_i \alpha_i \cdot \text{SRF}_k(\lambda_i) \cdot F_i}{\sum_i \text{SRF}_k(\lambda_i) \cdot F_i}$$

where $\alpha_i$ is spectral albedo, $\text{SRF}_k(\lambda_i)$ is the spectral response, and $F_i$ is the solar flux at wavelength $i$.

PlatformKeyBandsIndicesSource
Sentinel-2 MSI"sentinel2"B1–B12 (13 bands)NDSI, NDVI, IIESA S2 MSI User Guide
Sentinel-3 OLCI"sentinel3"Oa01–Oa21 (21 bands)NDCI, NDSI, NDVIESA S3 OLCI User Guide
Landsat 8 OLI"landsat8"B1–B7 (7 bands)NDSI, NDVIUSGS Landsat 8 handbook
MODIS (Terra/Aqua)"modis"B1–B7, VIS, NIR, SW (10 bands)NDSIMODIS L1 calibration docs

Sentinel-2 band definitions

BandCentre (nm)Width (nm)Typical use
B144320Coastal aerosol
B249065Blue
B356035Green
B466530Red
B570515Vegetation red edge
B674015Vegetation red edge
B778320Vegetation red edge
B8842115NIR
B8A86520Narrow NIR
B994520Water vapour
B10137530SWIR — cirrus
B11161090SWIR
B122190180SWIR

Spectral indices

  • NDSI (Normalised Difference Snow Index): (B3 − B11) / (B3 + B11) — discriminates snow/ice from other surfaces
  • NDVI (Normalised Difference Vegetation Index): (B8 − B4) / (B8 + B4) — detects vegetation and algal growth
  • II (Impurity Index): B3 / B8A — sensitive to light-absorbing impurities on ice

GCM / climate model platforms

GCM bands use flux-weighted interval averaging over wavelength ranges:

$$\bar{\alpha} = \frac{\sum_{i \in [lo, hi)} \alpha_i \cdot F_i}{\sum_{i \in [lo, hi)} F_i}$$
PlatformKeyBandsDescription
CESM 2-band"cesm2band"vis, nirVIS (0.2–0.7 µm), NIR (0.7–5.0 µm)
CESM RRTMG"cesmrrtmg"sw1–sw1414 shortwave bands (Iacono et al. 2008)
MAR"mar"4 bandsRegional climate model band scheme
HadCM3"hadcm3"6 bandsUK Met Office GCM band scheme

Architecture

biosnicar/bands/
    __init__.py      # to_platform(), BandResult, platform registry
    _core.py         # WVL grid, load_srf(), srf_convolve(), interval_average()
    platforms/       # Satellite platform modules
        sentinel2.py
        sentinel3.py
        landsat8.py
        modis.py
    gcm/             # GCM platform modules
        cesm.py
        mar.py
        hadcm3.py

Each platform module registers itself with _register(name, func) on import. Platform modules are lazy-loaded — they are only imported the first time to_platform() is called, keeping startup fast.

SRFs for satellite platforms are stored as CSV files in data/band_srfs/ with columns wavelength_um,B1,B2,... on the 480-point wavelength grid. GCM platforms define their wavelength intervals directly in code.

Improving the tophat SRFs

The current satellite SRFs use a tophat (boxcar) approximation: each band is represented as a rectangular function that is 1.0 within the published band centre ± half-width and 0.0 outside. This is a reasonable starting point because:

  1. At BioSNICAR’s 10 nm spectral resolution, the number of wavelength bins inside each band is small (typically 2–10), so the shape of the SRF within the band has limited influence.
  2. Tophat approximations are standard practice in the snow/ice remote sensing literature.

However, real instrument SRFs have non-rectangular shapes with gradual roll-off at the edges, asymmetries, and sometimes secondary lobes. For applications requiring high radiometric accuracy (e.g., validating satellite-retrieved albedo against model predictions), replacing the tophats with manufacturer-provided SRFs would improve fidelity.

How to replace tophats with real SRFs

The system is designed to make SRF upgrades straightforward. The only thing you need to change is the CSV file — no code modifications are required:

  1. Download the manufacturer SRF for your sensor (e.g., Sentinel-2 Spectral Responses from ESA, Landsat 8 RSR from USGS).

  2. Resample the SRF onto the BioSNICAR 480-point wavelength grid (0.205–4.995 µm, 10 nm step). Manufacturer SRFs are typically provided at 1 nm resolution, so this is a downsampling step. Use nearest-neighbour, linear interpolation, or band-average the 1 nm values within each 10 nm bin.

  3. Replace the CSV at data/band_srfs/<sensor_name>.csv with the resampled values. The file format is: first column wavelength_um, subsequent columns are band names (B1, B2, …). Values should be normalised so the peak is approximately 1.0.

  4. No code changes neededload_srf() reads the CSV at runtime and the convolution engine uses whatever SRF shape is in the file. If the CSV wavelength grid doesn’t exactly match the model grid, values are linearly interpolated automatically.

Practical considerations

  • Sentinel-2: The ESA-provided SRFs vary per detector module across the focal plane. For albedo work, average across modules or use the mean SRF.
  • MODIS: MODIS bands are relatively wide (20–50 nm), so the tophat approximation introduces less error than for narrow bands.
  • Sentinel-3 OLCI: OLCI bands are narrow (3.75–10 nm) but at 10 nm model resolution each band spans only 1–2 bins, making the SRF shape nearly irrelevant.
  • Landsat 8 OLI: OLI bands have well-characterised SRFs available from USGS. Bands 1–5 (VNIR) would benefit most from real SRFs.

Contributing improved SRFs

If you produce resampled manufacturer SRFs and would like to contribute them to the project, please submit a pull request replacing the relevant CSV in data/band_srfs/. Include a brief note on the source and resampling method used.