# Methodology — Fuel Layer v1.0 Global FM40 Crosswalk ## Overview The Petrel Fuel Layer answers "what surface fuel is here?" as a Scott & Burgan 40 (FM40) fuel-model class, at native ~270 m (9 arcsec) globally. It is a **deterministic crosswalk** — a rule table, not a trained model — that assigns an FM40 class from exactly **two** inputs: 1. **ESA WorldCover 2021** land cover (10 m native), which distinguishes tree cover / shrubland / grassland / cropland / built / water / bare / snow. 2. **Beck et al. (2018) Köppen-Geiger** present-day climate (~1 km), which supplies the biome context (tropical / Mediterranean / temperate / boreal / polar / desert / steppe). ``` ESA WorldCover 2021 (10 m) ─┐ ├─> crosswalk_fm40(wc, koppen) ──> FM40 class (uint8) Beck Köppen-Geiger (~1 km) ─┘ deterministic rules 255 = nodata ``` The output is globally consistent by construction — the same rules apply everywhere, so there is no train-region bias (see *Why a crosswalk, not ML*). ## Working grid — ~270 m Everything is computed on a common 9 arcsec grid (144000 × 54000 cells, 60° S – 75° N, bounds −180..180 lon, EPSG:4326). This matches the grid of the Petrel Wildfire Hazard's Cell2Fire fuel input and of the prior ML fuel line, so the layer drops in without resampling. The finest driver (WorldCover) is 10 m and supports crisp land-cover edges; the coarser driver (Köppen, ~1 km) sets the biome, so the *effective* family-resolving scale is climate-limited. ~270 m is the deliberate product grid — fine enough to carry WorldCover's structure, coarse enough not to overstate the ~1 km climate context. ## The crosswalk rules FM40 is assigned family-first, then refined within family by Köppen band. The full rule table is `crosswalk_fm40()` in `petrel-global-fuel/pgflib/models/fuel_crosswalk.py`; the logic: ### Non-burnable (NB) — direct from land cover Built-up → NB1, snow/ice → NB2, water and mangrove → NB8, cropland → NB3, moss/lichen → NB9. Bare/sparse is treated as sparse burnable grass (GR1, e.g. the Sahel fringe) **except** in true desert (Köppen BW) or polar (E), where it becomes NB9 barren. ### Grassland → GR, load by climate Grassland load tracks climate: desert-fringe sparse (GR1), steppe and Mediterranean dry grass (GR2), humid-temperate and tropical-savanna grass (GR3), continental prairie (GR4), tundra (GR1). ### Shrubland → SH, incl. the Mediterranean fix Shrubland defaults to low-load SH2, rises to SH3 in humid/tropical climates, and to high-load **SH5 in Mediterranean (Köppen Cs)** climates. This is the flagship correction over the ML line, which read Mediterranean shrub as *grass*. ### Tree cover → TL (closed litter) or TU (open understory), by climate Because v1.0 carries no canopy-height input, open-vs-closed forest is proxied by climate: - tropical rainforest (Af/Am) → **TL9** tall-litter, - temperate humid (Cf/Cw/Cs) → **TL2** closed-canopy litter, - savanna / steppe woodland (Aw, BS) → **TU1** grass-understory woodland, - boreal / subpolar (D, E) → **TU5** needle-litter conifer with understory. A canopy-height refinement (real open-vs-closed discrimination) is the v1.1 item; v1.0's climate proxy is its main known soft spot (humid-C savanna can over-read as closed timber — see `validation_report.md`). ### No-WorldCover fallback (no spurious holes) ESA WorldCover has tile-shaped nodata gaps over persistent snow (notably the Greenland interior). Any cell that has valid Köppen but no WorldCover falls back by climate — polar (E) → NB2 snow-ice, otherwise NB9 barren — so land never returns a hole where a climate value exists. Ocean (no Köppen) is left for the coast clip to mask. ## Why a crosswalk, not ML The earlier fuel line (`FuelModelXGB`, `fuel_v0.1/v0.2_global_ml`) was a gradient-boosted model **trained on US LANDFIRE FM40 labels** and applied globally. Extrapolating a US-trained model abroad produced systematic, biome-scale errors: - Mediterranean shrub read as grass (missing the SH high-load fuels), - tropical and boreal forest families confused, - open water read as grass. It also depended on Meta canopy height (CC-BY-NC), which forced the whole product to CC-BY-NC. The deterministic crosswalk fixes both problems at once: it is **globally consistent by construction** (the rules encode biome fuel physics directly, not a fitted US relationship), and it uses **only CC-BY-4.0 inputs**, so the derived layer is fully CC-BY-4.0 / commercially redistributable. ## Post-processing (Petrel canonical promote) The single class raster goes through the standard Petrel Layer chain: 1. **Coast clip** to the GSHHG-f full-resolution shoreline (~100 m native, `all_touched` — any cell touching real land keeps its value). Ocean becomes NoData (255). Antarctica is excluded by standard (GSHHG L1 only). 2. **Categorical uint8 re-encode**: FM40 codes pass through unchanged — there is **no scale factor** (unlike the continuous Petrel layers). The NoData sentinel is `255`. Encoding provenance is recorded in the file tags (`PETREL_ENCODING = "uint8 categorical"`). 3. **Internal tiling + overviews** built with **nearest-neighbour** resampling (categorical — never averaged) for efficient zoomed-out reads. 4. **Audit** — automated checks on encoding tags, overviews, land test points (any query over real land must return a valid FM40 code, not NoData), and bundle completeness before any release. Because the source raster is already at native ~270 m on local scratch, promote uses the `source_global_raster` fast-path (no coastal-dilation / bilinear downscale — those stages exist to protect narrow coastal pixels when downscaling continuous fields, and are moot for a native-resolution categorical raster). ## Scope of v1.0 — class only v1.0 ships the FM40 **class** raster and nothing else. The four canopy attributes the ML line shipped (canopy cover, stand height, canopy base height, bulk density) are, in the crosswalk, per-FM40-family class-typical *constants*. Shipping constants dressed as per-pixel data would misrepresent the product, so they are deliberately withheld until v1.1 can populate them with real per-pixel values from the CC-BY ETH global canopy-height layer. ## Code references - `petrel-global-fuel/pgflib/models/fuel_crosswalk.py` — the FM40 rule table (`crosswalk_fm40`) and the no-WorldCover climate fallback - `petrel-global-fuel/scripts/predict_global_vintage.py` — the `--crosswalk` global build (per-tile predict + mosaic) - `petrel-global-fuel/scripts/crosswalk_prototype_validate.py` — the biome spot-checks + US LANDFIRE family-agreement check - `configs/fuel_layer.yaml` — the full promote configuration (source raster, coast clip, encoding, previews, audit) - `petrelml/postprocess/` — the canonical promote chain (coast clip, re-encode, overviews, bundle, STAC, preview, audit) ## References - Scott, J. H., & Burgan, R. E. (2005). Standard fire behavior fuel models: a comprehensive set for use with Rothermel's surface fire spread model. *USDA Forest Service, RMRS-GTR-153.* https://doi.org/10.2737/RMRS-GTR-153 - Zanaga, D., et al. (2022). ESA WorldCover 10 m 2021 v200. https://doi.org/10.5281/zenodo.7254221 - Beck, H. E., Zimmermann, N. E., McVicar, T. R., Vergopolan, N., Berg, A., & Wood, E. F. (2018). Present and future Köppen-Geiger climate classification maps at 1-km resolution. *Scientific Data*, 5, 180214. https://doi.org/10.1038/sdata.2018.214 - Wessel, P., & Smith, W. H. F. (1996). A global, self-consistent, hierarchical, high-resolution shoreline database. *JGR*, 101(B4), 8741–8743.