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 access —
result.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$.
| Platform | Key | Bands | Indices | Source |
|---|---|---|---|---|
| Sentinel-2 MSI | "sentinel2" | B1–B12 (13 bands) | NDSI, NDVI, II | ESA S2 MSI User Guide |
| Sentinel-3 OLCI | "sentinel3" | Oa01–Oa21 (21 bands) | NDCI, NDSI, NDVI | ESA S3 OLCI User Guide |
| Landsat 8 OLI | "landsat8" | B1–B7 (7 bands) | NDSI, NDVI | USGS Landsat 8 handbook |
| MODIS (Terra/Aqua) | "modis" | B1–B7, VIS, NIR, SW (10 bands) | NDSI | MODIS L1 calibration docs |
Sentinel-2 band definitions
| Band | Centre (nm) | Width (nm) | Typical use |
|---|---|---|---|
| B1 | 443 | 20 | Coastal aerosol |
| B2 | 490 | 65 | Blue |
| B3 | 560 | 35 | Green |
| B4 | 665 | 30 | Red |
| B5 | 705 | 15 | Vegetation red edge |
| B6 | 740 | 15 | Vegetation red edge |
| B7 | 783 | 20 | Vegetation red edge |
| B8 | 842 | 115 | NIR |
| B8A | 865 | 20 | Narrow NIR |
| B9 | 945 | 20 | Water vapour |
| B10 | 1375 | 30 | SWIR — cirrus |
| B11 | 1610 | 90 | SWIR |
| B12 | 2190 | 180 | SWIR |
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}$$| Platform | Key | Bands | Description |
|---|---|---|---|
| CESM 2-band | "cesm2band" | vis, nir | VIS (0.2–0.7 µm), NIR (0.7–5.0 µm) |
| CESM RRTMG | "cesmrrtmg" | sw1–sw14 | 14 shortwave bands (Iacono et al. 2008) |
| MAR | "mar" | 4 bands | Regional climate model band scheme |
| HadCM3 | "hadcm3" | 6 bands | UK 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.pyEach 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:
- 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.
- 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:
-
Download the manufacturer SRF for your sensor (e.g., Sentinel-2 Spectral Responses from ESA, Landsat 8 RSR from USGS).
-
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.
-
Replace the CSV at
data/band_srfs/<sensor_name>.csvwith the resampled values. The file format is: first columnwavelength_um, subsequent columns are band names (B1,B2, …). Values should be normalised so the peak is approximately 1.0. -
No code changes needed —
load_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.