Set up a QUIC-Fire simulation with quicfire-tools
You are viewing in-progress documentation for v2 (Beta). Switch to the stable version for the current production release.
The QUIC-Fire export
hands you the fuel and terrain arrays — treesrhof.dat, treesmoist.dat,
treesfueldepth.dat, treesss.dat, topo.dat. QUIC-Fire itself needs about
fifteen more files before it will run: the .inp input deck that sets winds,
ignition, timing, and outputs, plus a gridlist and rasterorigin.txt
describing the fire grid. Writing that deck by hand is error-prone;
quicfire-tools writes all of
it from a few Python calls. This guide builds the deck around a FastFuels
export, runs the fire, and reads the outputs back as numpy arrays.
Prerequisites
Section titled “Prerequisites”-
A completed QUIC-Fire export, unzipped. The Create QUIC-Fire simulation inputs tutorial produces exactly this bundle — the
.datfiles plus ametadata.jsondescribing the fire grid. This guide assumes it’s unzipped into aquicfire_inputs/directory. -
QUIC-Fire 6.1 or later. QUIC-Fire is developed at Los Alamos National Laboratory and distributed on request — it is not on PyPI or GitHub. The distribution ships source plus build scripts; on macOS or Linux, compile with gfortran following the distribution’s Running QUIC-Fire under Mac or Linux guide (the result lands in
exe/, e.g.quicfire_MACI.exe). -
quicfire-tools:
pip install quicfire-tools(Python ≥ 3.8), plusnumpyfor reading outputs.
Step 1 — Build the input deck around the export
Section titled “Step 1 — Build the input deck around the export”The export’s metadata.json records the fire-grid cell counts (nx, ny,
nz) and cell sizes (dx, dy, dz). The script copies the horizontal
resolutions into the deck and verifies that dz is 1 m, as required by
QUIC-Fire 6.1.1’s FastFuels reader. It then points QUIC-Fire at the custom
.dat files, adds an ignition and outputs, and writes the deck:
"""Build a QUIC-Fire input deck around a FastFuels export with quicfire-tools."""
import jsonimport shutilfrom pathlib import Path
from quicfire_tools.inputs import SimulationInputs
bundle = Path("quicfire_inputs") # the unzipped FastFuels exportrun_dir = Path("qf_run")run_dir.mkdir(exist_ok=True)
# 1. The export's metadata.json records the fire grid the .dat files sit on.fire_grid = json.loads((bundle / "metadata.json").read_text())["fire_grid"]
# QUIC-Fire 6.1.1's FastFuels reader requires a uniform 1 m vertical grid.# Horizontal resolutions can vary, but they must be copied into the deck.if fire_grid["dz"] != 1.0: raise ValueError( "QUIC-Fire 6.1.1 FastFuels inputs require fire_grid.dz == 1.0 m; " f"this export uses {fire_grid['dz']} m" )
sim = SimulationInputs.create_simulation( nx=fire_grid["nx"], ny=fire_grid["ny"], fire_nz=fire_grid["nz"], wind_speed=6.0, # m/s, measured at 6.1 m wind_direction=270, # degrees the wind blows FROM (270 = out of the west) simulation_time=1800, # s)sim.qu_simparams.dx = fire_grid["dx"]sim.qu_simparams.dy = fire_grid["dy"]sim.quic_fire.dz = fire_grid["dz"]
# 2. Point the deck at the FastFuels .dat files.sim.set_custom_simulation( fuel_density=True, fuel_moisture=True, fuel_height=True, size_scale=True, # use treesss.dat too topo=True, # use topo.dat ignition=False, # keep a simple rectangle ignition (step 3) interpolate=False, # the files are already on the fire grid)
# FastFuels .dat files use QUIC-Fire's FastFuels convention (flag 5).# quicfire-tools writes the Firetec matching-grid convention (flag 3), which# is not interchangeable and fails for this FastFuels export, so override it.sim.quic_fire.fuel_density_flag = 5sim.quic_fire.fuel_moisture_flag = 5sim.quic_fire.size_scale_flag = 5
# QUIC-Fire requires the wind-solver domain to be at least 3x the terrain# relief. This export spans 957-1172 m (215 m of relief), so raise the# default 300 m QUIC domain to 700 m.sim.qu_simparams.quic_domain_height = 700
# 3. A north-south ignition line near the west (upwind) edge, spanning the# middle 80% of the domain. Coordinates are meters from the SW corner.sim.set_rectangle_ignition( x_min=0.1 * fire_grid["nx"] * fire_grid["dx"], y_min=0.1 * fire_grid["ny"] * fire_grid["dy"], x_length=10, y_length=0.8 * fire_grid["ny"] * fire_grid["dy"],)
# 4. Outputs: 3-D fuel density and fire energy, and the 2-D burnt-mass map,# every 300 s of fire time.sim.set_output_files(fuel_dens=True, mass_burnt=True, eng_to_atm=True)sim.set_output_interval(300)
# 5. Write the deck and put the FastFuels files next to it.sim.write_inputs(run_dir)for name in ("treesrhof.dat", "treesmoist.dat", "treesfueldepth.dat", "treesss.dat", "topo.dat"): shutil.copy(bundle / name, run_dir / name)Five decisions in that script are worth pausing on:
fuel_*_flag = 5(FastFuels convention).set_custom_simulationwrites flag3, the Firetec matching-grid convention. It is not interchangeable with the FastFuels convention: this export usestreesfueldepth.datfor surface fuel-bed depth in the ground layer and requires flag5with a uniform 1 mdz. On QUIC-Fire 6.1.1, this case fails at the first fire step under flag3, so the override matters.quic_domain_height. QUIC-Fire requires its wind-solver domain to be at least 3× the terrain relief. This export’s terrain spans 957 m to 1,172 m — 215 m of relief — so the default 300 m domain fails at startup; 700 m clears it.interpolate=False. The export wrote the.datfiles on the fire grid itself, so no re-gridding is needed. (For QUIC-Fire ≤ 6.0 the interpolation flag was also a workaround for a custom-fuels bug; on 6.1+ keep it off when the grids already match.)- Wind direction is meteorological — the direction the wind blows
from, in degrees clockwise from north.
270is a west wind pushing the fire east. - Ignition coordinates are meters, not cells, measured from the domain’s southwest corner.
write_inputs also writes the gridlist and rasterorigin.txt that
QUIC-Fire’s custom-fuel reader requires — you don’t create those yourself.
Step 2 — Run QUIC-Fire
Section titled “Step 2 — Run QUIC-Fire”Copy the compiled executable into the run directory and start it there. Two environment details matter for gfortran builds:
# From the run directory, next to the .inp deck and .dat filescd qf_runcp /path/to/QUIC-Fire/exe/quicfire_MACI.exe .
# gfortran builds keep large automatic arrays on the stack — give the main# thread and each OpenMP thread room, or the run dies at "Fire time: 1"ulimit -s 65520OMP_NUM_THREADS=8 OMP_STACKSIZE=1G ./quicfire_MACI.exe | tee qf_console.logQUIC-Fire first solves the initial wind field over the terrain, then steps
the fire one second at a time, writing outputs into Output/ on the
interval you set:
Allocating 1 threads. Small cell physics turned on WARNING: Trees files dz assumed equal to 1 m Plume time step [s]: 1.0 Initializing fire variables Percentage of cells with fuel (fire domain): 4.85 % Percentage of cells with fuel (QU domain): 10.43 % Writing fire-related output files Total vegetation mass burnt: 0.00% Calculating winds for 08/13/2026 15:13:21 Finished interpolating winds Fire time [s]: 1 Update buoyant plumesPlume time step 1.00; Plume tot time 1.00... Update buoyant plumesPlume time step 1.00; Plume tot time 1.00 Fire time [s]: 1800 Writing fire-related output files Total vegetation mass burnt: 33.09% Update buoyant plumesPlume time step 1.00; Plume tot time 1.00 Simulation time = 2444 sBy t = 1,800 s this burn has consumed 33.09% of the simulation’s initial
modeled fuel mass. This percentage describes the fuel represented in the
input arrays, not all vegetation biomass or ecological effects in the real
domain. Wall-clock cost scales with the grid and how much is burning — this
1,800 s burn on the 654 × 442 × 33 grid took about 40 minutes on a laptop;
scale expectations (and simulation_time) to your case.
Step 3 — Read the outputs
Section titled “Step 3 — Read the outputs”The Output/ directory holds compressed binary arrays keyed to the fire
grid. SimulationOutputs reads any of them back as numpy arrays:
"""Read QUIC-Fire outputs back as numpy arrays with quicfire-tools."""
import jsonfrom pathlib import Path
import numpy as npfrom quicfire_tools.outputs import SimulationOutputs
bundle = Path("quicfire_inputs")fire_grid = json.loads((bundle / "metadata.json").read_text())["fire_grid"]nz, ny, nx = fire_grid["nz"], fire_grid["ny"], fire_grid["nx"]dx, dy = fire_grid["dx"], fire_grid["dy"]
outputs = SimulationOutputs("qf_run/Output", nz, ny, nx, dy=dy, dx=dx)print(outputs.list_outputs())
# 3-D fuel density at each output timedens = outputs.get_output("fuels-dens")print(dens.times) # [0, 300, 600, ..., 1800] seconds of fire time
first = dens.to_numpy(timestep=0)[0] # shape (nz, ny, nx)last = dens.to_numpy(timestep=len(dens.times) - 1)[0]
# Total modeled fuel-mass fraction. Cell volume is uniform, so it cancels.modeled_mass_consumed = (first.sum() - last.sum()) / first.sum()print(f"modeled fuel mass consumed: {modeled_mass_consumed:.1%}")
# Column totals -> fraction of modeled fuel consumed in each map cellinitial_by_column = first.sum(axis=0)final_by_column = last.sum(axis=0)consumed_by_column = np.where( initial_by_column > 0.01, 1 - final_by_column / initial_by_column, np.nan,)
# 2-D vertically-integrated % of mass burnt, for perimeter mapsmburnt = outputs.get_output("mburnt_integ")burned_area_m2 = ( (mburnt.to_numpy(timestep=len(mburnt.times) - 1)[0] > 1).sum() * dx * dy)print(f"burned area: {burned_area_m2 / 10_000:.1f} ha")For an explanation of what the consumption and fire-energy arrays show — and what conclusions a single run cannot support — see Interpreting a FastFuels export in QUIC-Fire.
Common pitfalls
Section titled “Common pitfalls”Domain is not tall enough. Must be 3. times the maximum terrain elevation.Raisesim.qu_simparams.quic_domain_heightto at least three times the relief reported in the message.- Segfault right at
Fire time [s]: 1(gfortran builds). The fire step keeps large automatic arrays on the stack. Raise the limits shown in Step 2 (ulimit -s 65520,OMP_STACKSIZE=1G) before blaming the deck. - Crash at the first fire step with canopy fuels. You’re running
FastFuels
.datfiles under fuel flag 3. Set the density, moisture, and size-scale flags to 5 as in Step 1. Output directory already exists. QUIC-Fire refuses to overwrite a previous run — removeOutput/before re-running.- The fire never grows. Check the ignition rectangle actually overlaps fuel (roads and other masked cells hold zero), and remember the coordinates are meters from the southwest corner, not cell indices.
Next steps
Section titled “Next steps”- Tune the weather.
wind_speed,wind_direction, and the moisture grids in the export are the main levers on fire behavior — rebuild the export with a drier uniform moisture grid or add wind sensors withsim.add_wind_sensor(...)for time-varying winds. - Visualize in 3-D. The QUIC-Fire distribution ships post-processing
scripts (
drawfire.py,PyVistaQF.py) that render the same outputs as images and VTK files for ParaView. The Blue Mountain case study explains the figures produced for this run. - Automate the whole loop. The export’s
signed_urldownload, the deck build, and the run are all scriptable — see the tutorial’s single-script pipeline for the FastFuels half.