Skip to content

Derive canopy fuel from a tree inventory

You are viewing in-progress documentation for v2 (Beta). Switch to the stable version for the current production release.

This guide starts with a completed tree inventory and ends with the four 2D canopy fuel bands a landscape file needs, cbd, cbh, chm, and cc. The endpoint estimates each tree’s available canopy fuel, distributes it through the crown, attributes it to output cells, and reduces each cell’s vertical profile to those four bands. It does this directly from the inventory, without first building a 3D voxel grid.

  1. An API key. my-api-key.

  2. A domain. your-domain-id.

  3. A completed tree inventory. your-inventory-id. The inventory may come from any supported inventory workflow, but it must carry x, y, height, dbh, crown_ratio, and fia_species_code.

The live responses and figures on this page use the completed inventory from From lidar to a tree inventory. That point-cloud lineage makes the fixture reproducible. It is not a requirement of the canopy endpoint.

The complete minimal flow of create, poll, export, and download is available as one script.

Tree inventory to canopy_fuel.tif
import fastfuels_sdk.v2 as ff
ff.set_api_key("my-api-key")
# Any completed tree inventory with x, y, height, dbh, crown_ratio, fia_species_code.
inventory = ff.get_inventory("your-domain-id", "your-inventory-id")
grid = ff.grids.create_canopy_fuel_grid_from_inventory(
inventory,
name="Canopy fuel from inventory",
)
grid.wait()
# Export every band to one GeoTIFF, then download it.
export = grid.export(format="geotiff")
export.wait()
export.to_file("canopy_fuel.tif")

The only required request field is the completed inventory id. The endpoint defaults to four bands on a 30 m, domain-anchored lattice.

POST grids/canopy/inventory
curl -X 'POST' \
'https://api-v2-prod-782971006568.us-west1.run.app/domains/your-domain-id/grids/canopy/inventory' \
-H 'accept: application/json' \
-H 'api-key: my-api-key' \
-H 'Content-Type: application/json' \
-d '{
"source_inventory_id": "your-inventory-id",
"name": "Canopy fuel from inventory"
}'

Record the id as your-canopy-grid-id. The response’s source records every omitted choice. The defaults are NSVB allometry, all foliage plus 7.5 percent of total branchwood, all species, Reinhardt vertical profiles, crown-projected cell attribution, a 3 m CBD running mean, threshold-derived CBH and CHM, and geometric crown-union cover. The source inventory id, and its checksum when the inventory supplies one, preserve the input lineage. The inputs that build the profile explanation covers what each default means.

Poll until the job completes.

GET grid status
curl -X 'GET' \
'https://api-v2-prod-782971006568.us-west1.run.app/domains/your-domain-id/grids/your-canopy-grid-id' \
-H 'accept: application/json' \
-H 'api-key: my-api-key'

The completed grid contains these bands.

BandUnitRole
cbdkg/m**3canopy bulk density
cbhmbase of the canopy fuel layer
chmmcanopy-top height
cc%canopy cover
Four maps of the same inventory-derived canopy grid arranged in a two-by-two panel: canopy bulk density, canopy base height, canopy height, and canopy cover. Forested cells form the same broad spatial pattern, while each metric emphasizes a different property and uses its own labeled color scale.

The four default bands from the live fixture inventory, calculated on the same 30 m lattice. Non-forest cells are zero. Each band has its own physical unit and color scale. Matching spatial outlines do not make their values interchangeable.

To also retain total available canopy fuel per ground area, add cfl to bands. It is reported in kg/m**2, but it is not a landscape-file role.

Create a GeoTIFF export after the grid reaches completed, poll the returned export id, then download its signed_url.

Export and download canopy_fuel.tif
curl -X 'POST' \
'https://api-v2-prod-782971006568.us-west1.run.app/domains/your-domain-id/grids/your-canopy-grid-id/exports/geotiff' \
-H 'accept: application/json' \
-H 'api-key: my-api-key' \
-H 'Content-Type: application/json' \
-d '{}'
# Record the response id as your-export-id, then poll it:
curl \
'https://api-v2-prod-782971006568.us-west1.run.app/exports/your-export-id' \
-H 'accept: application/json' \
-H 'api-key: my-api-key'
# When status is completed, download from the response's signed_url:
curl 'paste-signed-download-url' --output canopy_fuel.tif

Use a preset when your result must be comparable to an existing tool. Each curl body below is complete and copyable; the Python SDK blocks continue from the one-script flow above, reusing the imported ff module and the inventory object, and show only the parameters and typed method objects that change. Keep the inventory and cell lattice fixed when comparing presets so the method is the only changing input. For why matching a convention keeps a downstream model valid, see reproducing a convention or deviating from it.

This is the endpoint’s fuelcalc_comparison OpenAPI example. It switches from the FastFuels national defaults to Brown (1978) biomass, FuelCalc species and crown-class handling, stem-cell attribution, 5 ft running means, Crookston-Stage crown widths, and random-overlap cover.

FuelCalc 1.7-comparable request
curl -X 'POST' \
'https://api-v2-prod-782971006568.us-west1.run.app/domains/your-domain-id/grids/canopy/inventory' \
-H 'api-key: my-api-key' \
-H 'Content-Type: application/json' \
-d '{
"source_inventory_id": "your-inventory-id",
"name": "FuelCalc 1.7-comparable canopy fuel",
"biomass_source": {"type": "allometry", "equations": "brown_1978"},
"species_inclusion": "fuelcalc_default",
"crown_class_adjustment": {
"method": "fuelcalc_table",
"missing_crown_class": "other_none"
},
"horizontal_distribution": "stem",
"max_crown_radius_source": {
"type": "allometry",
"equations": "crookston_stage"
},
"cbd": {
"method": "maximum_running_mean",
"window": 1.524,
"edge": "ground_clamped"
},
"cbh": {
"method": "bulk_density_threshold",
"smoothing_window": 1.524,
"smoothing_edge": "ground_clamped"
},
"chm": {
"method": "bulk_density_threshold",
"smoothing_window": 1.524,
"smoothing_edge": "ground_clamped"
},
"cc": {"method": "crown_overlap"}
}'

If the inventory does not carry fia_crown_class_code, missing_crown_class: "other_none" applies FuelCalc’s documented fallback to those trees.

Starting from the FuelCalc comparison body, use a 15 ft (4.572 m) window for CBD and threshold crossings and exclude trees below 6 ft (1.83 m).

Original FuelCalc request
curl -X 'POST' \
'https://api-v2-prod-782971006568.us-west1.run.app/domains/your-domain-id/grids/canopy/inventory' \
-H 'api-key: my-api-key' \
-H 'Content-Type: application/json' \
-d '{
"source_inventory_id": "your-inventory-id",
"name": "Original FuelCalc method (RMRS-P-41)",
"biomass_source": {"type": "allometry", "equations": "brown_1978"},
"species_inclusion": "fuelcalc_default",
"crown_class_adjustment": {
"method": "fuelcalc_table",
"missing_crown_class": "other_none"
},
"min_tree_height": 1.83,
"horizontal_distribution": "stem",
"max_crown_radius_source": {
"type": "allometry",
"equations": "crookston_stage"
},
"cbd": {
"method": "maximum_running_mean",
"window": 4.572,
"edge": "ground_clamped"
},
"cbh": {
"method": "bulk_density_threshold",
"smoothing_window": 4.572,
"smoothing_edge": "ground_clamped"
},
"chm": {
"method": "bulk_density_threshold",
"smoothing_window": 4.572,
"smoothing_edge": "ground_clamped"
},
"cc": {"method": "crown_overlap"}
}'

Use uniform fuel through each crown, ignore trees below 6 ft, reduce CBD over a 13 ft window, and find CBH/CHM by crossing a 3 ft smoothed profile at a flat 0.011 kg/m**3. truncated reproduces FFE-FVS’s treatment of running means at both profile ends.

FFE-FVS-comparable request
curl -X 'POST' \
'https://api-v2-prod-782971006568.us-west1.run.app/domains/your-domain-id/grids/canopy/inventory' \
-H 'api-key: my-api-key' \
-H 'Content-Type: application/json' \
-d '{
"source_inventory_id": "your-inventory-id",
"name": "FFE-FVS-comparable canopy fuel",
"biomass_source": {"type": "allometry", "equations": "brown_1978"},
"vertical_distribution": "uniform",
"min_tree_height": 1.83,
"cbd": {
"method": "maximum_running_mean",
"window": 3.9624,
"edge": "truncated"
},
"cbh": {
"method": "bulk_density_threshold",
"threshold": 0.011,
"relative_threshold_fraction": null,
"smoothing_window": 0.9144,
"smoothing_edge": "truncated"
},
"chm": {
"method": "bulk_density_threshold",
"threshold": 0.011,
"relative_threshold_fraction": null,
"smoothing_window": 0.9144,
"smoothing_edge": "truncated"
}
}'

Brown & Johnston (1976), the FFE-FVS biomass family, is not available. Brown (1978) is the closest offered family, so expect comparable rather than identical values.

For an inventory-derived counterpart built on the canopy allometries behind the FuelCalc/LANDFIRE method, select Brown (1978) biomass and Crookston-Stage crown widths while leaving the FastFuels spatial defaults in place.

LANDFIRE-comparison request
curl -X 'POST' \
'https://api-v2-prod-782971006568.us-west1.run.app/domains/your-domain-id/grids/canopy/inventory' \
-H 'api-key: my-api-key' \
-H 'Content-Type: application/json' \
-d '{
"source_inventory_id": "your-inventory-id",
"name": "LANDFIRE-comparison canopy fuel",
"biomass_source": {"type": "allometry", "equations": "brown_1978"},
"max_crown_radius_source": {
"type": "allometry",
"equations": "crookston_stage"
}
}'

This does not recreate a published LANDFIRE raster. LANDFIRE also includes mapping, imputation, and product-level adjustments that are outside this per-cell inventory calculation.

Two paired map comparisons over the same inventory and 30 metre lattice. The top pair compares canopy base height from the default bulk-density threshold with the 20th percentile of per-tree crown bases. The bottom pair compares canopy bulk density from the default maximum running mean with foliage-only load divided by mean crown length. Each pair shares one color scale.

Method choice changes the reported value, even though the inventory and lattice stay fixed. Each row shares one color scale. The top row is default threshold CBH versus 20th-percentile CBH, and the bottom row is default effective CBD versus Cruz-style load-over-depth CBD. Zero-valued non-forest cells are shown in light grey.

Use the 20th percentile of per-tree crown bases when low ladder fuel should control the result rather than the average tree. It can land higher or lower than a threshold-crossing CBH, so compare the two on the same inventory before choosing. The canopy base height explanation covers when the lower tail is the right choice.

20th-percentile CBH
curl -X 'POST' \
'https://api-v2-prod-782971006568.us-west1.run.app/domains/your-domain-id/grids/canopy/inventory' \
-H 'api-key: my-api-key' \
-H 'Content-Type: application/json' \
-d '{
"source_inventory_id": "your-inventory-id",
"name": "Conservative CBH screening",
"cbh": {"method": "percentile", "percentile": 20}
}'

For the most risk-averse screen, replace the cbh object with {"method": "minimum"} so any low crown base can set the cell’s CBH.

Use foliage only and divide canopy fuel load by mean crown length. This reports an average density over the depth, which reads lower than the default effective CBD. The canopy bulk density explanation covers why.

Cruz-style CBD
curl -X 'POST' \
'https://api-v2-prod-782971006568.us-west1.run.app/domains/your-domain-id/grids/canopy/inventory' \
-H 'api-key: my-api-key' \
-H 'Content-Type: application/json' \
-d '{
"source_inventory_id": "your-inventory-id",
"name": "Cruz-style load-over-depth CBD",
"available_fuel": {
"foliage_fraction": 1.0,
"branchwood": {"size_partition": "none", "fraction": 0.0}
},
"cbd": {"method": "load_over_depth", "depth": "mean_crown_length"}
}'

Feed the canopy grid into a landscape export

Section titled “Feed the canopy grid into a landscape export”

Point all four canopy_* roles at the completed inventory canopy grid. The terrain and FBFM grids may be different resources, but every role must already share one CRS, origin, cell size, and coverage.

POST grids/exports/landscape
curl -X 'POST' \
'https://api-v2-prod-782971006568.us-west1.run.app/domains/your-domain-id/grids/exports/landscape' \
-H 'api-key: my-api-key' \
-H 'Content-Type: application/json' \
-d '{
"name": "Landscape with inventory-derived canopy fuel",
"alignment": {"target": "grid", "grid_id": "your-canopy-grid-id"},
"fire_behavior_fuel_model": "fbfm40",
"elevation": {"grid_id": "your-topography-grid-id", "band": "elevation"},
"slope": {"grid_id": "your-topography-grid-id", "band": "slope"},
"aspect": {"grid_id": "your-topography-grid-id", "band": "aspect"},
"fuel_model": {"grid_id": "your-fbfm40-grid-id", "band": "fbfm"},
"canopy_cover": {"grid_id": "your-canopy-grid-id", "band": "cc"},
"canopy_height": {"grid_id": "your-canopy-grid-id", "band": "chm"},
"canopy_base_height": {"grid_id": "your-canopy-grid-id", "band": "cbh"},
"canopy_bulk_density": {"grid_id": "your-canopy-grid-id", "band": "cbd"}
}'

The example anchors the export to your-canopy-grid-id. Build or resample the topography and FBFM grids onto that exact lattice first. Alternatively, choose an existing export grid up front and create the canopy grid with {"alignment": {"target": "grid", "grid_id": "..."}}. The landscape export crops aligned inputs but never resamples or reprojects them.

  • Passing a thin inventory. Complete dbh, crown_ratio, and fia_species_code with GDAM before deriving canopy fuel.
  • Using a pending inventory. Poll the GDAM inventory to completed before this request.
  • Changing resolution during a comparison. A smaller cell changes which crowns contribute and what scale each value describes. Keep the lattice fixed when comparing methods.
  • Calling a preset an exact reproduction. Match its method body and the input inventory, including crown class, before interpreting differences as implementation differences.
  • Building export roles at native resolution. Native source cells often do not match a domain-anchored 30 m lattice. Use an explicit resolution or align every role to one existing grid.