How Windfield works
This page walks through what actually happens between you drawing a box on a map and seeing wind flow over the ground. Plain language first; open any Technical note for the mechanics. See also About & Methodology for the short version, and Help for how to use the app and the full public API.
1. The big picture
You draw a rectangle on the map — that's the ground you want modelled. Everything after that happens on our server, not in your browser: it downloads real elevation data for that exact area, builds a 3D terrain model at the resolution you asked for, burns in any structures you drew, runs the WindNinja wind model on that terrain, and sends back pictures (a speed map, arrows) and numbers (a grid of values you can probe). Your browser's whole job is to draw what comes back and let you interact with it — it never runs the wind model itself.
Technical note
A drawn bbox (and your options) becomes a POST /api/v1/jobs
request. The server fetches AWS Terrain Tiles (Terrarium PNG encoding:
elev = R×256 + G + B/256 − 32768) at a zoom chosen
from your requested resolution, mosaics and decodes them, writes a
GeoTIFF in WGS84, then gdal.Warps it to the local UTM zone
in metres — WindNinja needs a projected DEM, not degrees. Any
structures you drew are rasterized into that same grid
(gdal.RasterizeLayer, ALL_TOUCHED) and their
height added before the solve. WindNinja_cli runs against
a generated .cfg file as the unprivileged
windninja user, niced, with a 15-minute timeout.
Post-processing turns its *_vel/*_ang
output into the speed PNG, arrows GeoJSON and the decimated
grid.json your browser renders. See the
public API for the exact shapes.
2. The engine: WindNinja 4.0
Windfield runs WindNinja 4.0, built by the USDA Forest Service's Rocky Mountain Research Station at the Missoula Fire Sciences Lab, originally for wildfire behaviour prediction. It's a diagnostic wind model: it solves one physically consistent snapshot of the wind for the terrain and inputs you give it, rather than forecasting how the atmosphere will evolve over time. Give it the same terrain and the same starting wind twice and you get the same answer — it's a calculation, not a weather forecast.
The fast solver: mass-conserving
By default Windfield uses WindNinja's fast mass-conserving solver. It starts from an initial wind (one you set, a station, or a model) and adjusts it, cell by cell over a 3D terrain-following mesh, so that air doesn't pile up or vanish anywhere — the amount of air flowing into a cell has to equal the amount flowing out. Enforcing that single rule over real terrain already produces the effects you'd expect to see outdoors: wind speeds up over ridgelines and passes, slows and backs up on the windward side of a hill, gets deflected sideways around high ground, channels down valleys, and runs faster downslope at night than during the day. It does all of this in seconds, which is why it's the default.
What it can't do: it has no concept of flow separating off a sharp edge, no wakes, no eddies, no recirculation behind a ridge, cliff or building. It smooths the wind over the terrain's shape; it doesn't simulate the turbulence that shape would actually cause.
The high-fidelity solver: momentum (OpenFOAM)
For cases where that separated flow matters — wakes behind buildings or cliffs, eddies, recirculation zones, gully flows merging — Windfield can opt in to a momentum solver built on OpenFOAM. Instead of only conserving mass, it solves the full Navier–Stokes momentum equations with a turbulence model, which is what actually produces wakes and recirculation in the first place. The cost is time: minutes rather than seconds, and a much smaller cell budget, because resolving that level of detail is far more expensive per cell.
The log wind profile, roughness, and diurnal slope winds
Near the ground, wind speed doesn't change linearly with height — it follows a logarithmic profile: close to the surface friction slows it down a lot, and speed increases quickly at first, then more gradually higher up. How quickly depends on vegetation roughness: short grass lets wind run fast close to the ground, while a forest canopy drags on the air much more, flattening the near-surface profile and effectively pushing the "free-flowing" wind higher up. That's why vegetation is one of the inputs you set.
When you turn on diurnal winds, Windfield also models the daily heating/cooling cycle: during the day, sun-warmed slopes heat the air next to them, which rises and pulls air upslope; at night, slopes cool faster than the free air above them, and that denser cold air sinks and drains downslope and down-valley. Pair it with non-neutral stability and the model also accounts for how stable or unstable the atmosphere is, which changes how strongly it resists vertical motion — stable air suppresses turbulence and mixing near the ground, unstable air encourages it.
Technical note
This is the real WindNinja_cli 4.0.0 binary
(firelab/windninja tag 4.0.0), built with
NINJAFOAM=OFF for the mass-conserving path — so the
fast solver is a separate, deliberately lighter build from the
momentum path, which runs as its own OpenFOAM worker behind a job
queue (solver: "momentum" in the API, capped at 60,000
cells, one job at a time). The mass solver iterates the wind field on
a 3D terrain-following mesh to minimize divergence (net flow in/out
of each cell) subject to the terrain boundary, which is a far cheaper
problem than solving momentum transport and turbulence closure.
Diurnal/stability add diurnal_winds,
non_neutral_stability, uni_air_temp,
uni_cloud_cover and a date/time/timezone to the cfg.
3. Ways to start the wind
WindNinja always needs a starting wind to adjust — Windfield gives you five ways to provide one:
- One wind for the whole area — you set a single speed, direction and height yourself (Simulate). The simplest option, and a good way to see how terrain alone bends a given wind.
- Observation points — you place one or more points on the map and give each its own speed/direction/height, letting the model start from several different readings across the area rather than one uniform value.
- Live stations — real current observations: NOAA's National Weather Service stations, airport METAR reports, NDBC ocean buoys, and RAWS/mesonet stations (via Synoptic Data) feed the model with what's actually blowing right now, nearest to your area.
- A vertical profile — wind speed and direction at several heights at once, rather than one height. This comes from model analysis pressure levels (Open-Meteo, which assimilates balloon, aircraft and satellite data into its weather models) and from radiosonde (weather balloon) soundings at nearby launch sites. Windfield interpolates between the levels you give it to drive each output height.
- A forecast model — GFS, HRRR or NAM (via NOAA NOMADS) drives the wind over several hours at a time, one WindNinja run per forecast hour, with a time player to step through them.
Technical note
- Simulate
initialization_method = domainAverageInitialization- Points
pointInitialization, one station CSV per point (WindNinja rejects multiple stations in one file),date_timeis UTC with a required throwaway trailing character.- Live stations
GET /weather/stations?sources=nws,metar,ndbc,raws, merged and fed in as points using each station's own anemometer height.- Profile
GET /weather/profile?sources=model,sounding,surface; direction is interpolated as a unit vector (not the raw angle) so e.g. 350°→10° doesn't average to 180°.- Forecast
wxModelInitialization, live fetch from NOMADS;wx_model_typeone ofNOMADS-GFS-GLOBAL-0.25-DEG(worldwide),NOMADS-HRRR-CONUS-3-KM,NOMADS-NAM-CONUS-12-KM(continental US only).
4. Heights: above the ground, not above sea level
Every wind value Windfield shows you is a height above the ground directly beneath it (AGL — above ground level), following the terrain up and down, not a fixed altitude above sea level (MSL). "10 m" always means ten metres above whatever the ground is doing right there — on a ridge, in a valley, on a slope.
- 2 m — roughly head height; dominated by local surface friction and the most sheltered by vegetation and terrain roughness.
- 10 m — the standard meteorological reference height, above most ground-level friction effects; what most station wind reports use.
- 100 m — well above the surface boundary layer on flat ground, closer to the wind that's steering the larger flow rather than being dragged by the ground.
The 3D view works the same way: it's a volume of AGL wind following the terrain surface, but when you slice through it at a fixed altitude (MSL), that slice can clip through the ground on high terrain and float above it over low terrain — a 500 m MSL slice is inside the mountain on one side of your area and still well above the valley floor on the other. Where the model shows air rising, it's being pushed up a windward slope; where it shows air sinking, it's descending a lee slope or draining downhill at night.
Technical note
WindNinja's mesh is terrain-following: each output height
(output_wind_height/units_output_wind_height)
is a surface offset from the DEM at every grid cell, not a constant
MSL elevation. Windfield can run several heights in one job
(output_heights, 1–6 values, sharing the same DEM
and solve) — results.heights[] carries one result
per height. grid.json additionally reports a terrain-forced
vertical wind component w = u·dz/dx + v·dz/dy
(central differences on the UTM grid, in m/s) so rising/sinking air
can be read directly from the data, not just inferred from slope.
5. Structures
You can draw a building, wall, barn, or any polygon/rectangle/circle shape on the map, give it a height (or mark it infinitely tall), and Windfield raises the terrain under it before solving.
- With the fast solver, a structure is modelled as raised terrain — the solver doesn't know it's a building, only that the ground there is suddenly taller. That means your mesh has to be clearly finer than the object: a structure smaller than one grid cell barely registers at all, and a coarse 100 m mesh will simply erase a 10 m shed. Pick a mesh resolution well under the structure's own size to see it at all.
- With the momentum solver, structures behave as real obstacles the flow has to go around — this is what actually produces a wake, separation and recirculation behind them, which the fast solver can't represent regardless of mesh size.
- Infinite height marks a structure as effectively unbounded (burned far above any real terrain in the area) — useful for seeing a wall-like barrier effect without picking a real height.
Technical note
Structures are rasterized into the UTM DEM
(gdal.RasterizeLayer, ALL_TOUCHED) and
their height (or +1000 m for infinite) is added
before either solver runs — the fast solver never sees them as
anything but taller terrain. The momentum path instead hands the
same DEM/structure geometry to a separate OpenFOAM worker
(windninja-foam.service) which meshes around them as
real boundaries; it's capped to 60,000 cells and one job at a time,
since that meshing is far more expensive per cell. Validation: up to
50 structures per job, each ring up to 2,000 vertices, height
0.1–5000 m, clipped silently to the drawn area.
6. The pictures
- Speed drape — a colour-mapped raster of wind speed over the terrain, opacity adjustable so the map underneath stays visible. Colours follow the legend shown with the result.
- Streamlines — traced by stepping along the local wind direction from a starting point in small increments, again and again, so the resulting curve follows the path air would actually take through the field; good for seeing flow wrap around a ridge or a structure at a glance.
- Particles — small animated points carried by local speed and direction in real time, the most intuitive read of where flow is fast, slow, or turning, at the cost of giving exact values at a glance.
- Arrows — a decimated grid of direction/speed arrows, useful when you want a value at a specific spot rather than an animation.
- Contours, relief and slope — drawn from the same model terrain WindNinja actually solved on (after any structures were burned in), not a generic basemap layer, so what you see lines up exactly with what was modelled.
- Cross-sections — draw a line and see a side-on profile of the terrain, and where available, wind speed, along it.
- Probe values — click any point on a finished result for the exact speed, direction and elevation there.
- Eddies, recirculation and convergence overlays — highlight where the flow curls back on itself or where air from different directions meets; most meaningful with the momentum solver, which is the one that actually resolves that behaviour.
Technical note
The speed/direction rasters are warped to EPSG:3857 so they drape
correctly as a Leaflet image overlay; arrows are a GeoJSON point
layer decimated to roughly 2,500 features. grid.json
(up to 300×300 cells) carries speed, dir,
u, v, the terrain-forced vertical component
w, and elev/slope/aspect
sampled onto the exact same grid — everything a client needs to
draw streamlines, particles, probes and a terrain profile from one
response. Terrain layers (terrain_hillshade.png,
terrain_slope.png, contours.geojson) are
built once per job from the same post-structure, post-water-clamp
DEM WindNinja solved on.
7. Accuracy and limits
Windfield's results are a model, not a forecast. It computes a physically plausible wind field for the terrain and inputs it's given — it doesn't know what the wind is actually doing right now unless you chose Current conditions or Forecast, and even then it's only as good as the weather source behind it. Treat it as a planning and situational-awareness tool, not a substitute for an official forecast, especially for life-safety decisions.
There's a direct trade-off between resolution and run time: a finer mesh resolves smaller terrain features and structures but costs more cells and more solver time, so the app shows a live estimate (cells · seconds · terrain resolution) before you run, and offers the coarsest resolution that would fit if a custom setting is too fine for the area.
Budgets. Every run is capped, not just the area:
- Domain up to about 400 km on a side.
- Up to 1.5 million mesh cells.
- Up to 15 minutes of run time.
- Up to 2 runs processing at once (per visitor, up to 2 in flight).
Results aren't kept forever: a finished job's files expire roughly 5 minutes after you stop watching it (close the page or move on). A share link doesn't point at a file kept on disk indefinitely — it re-POSTs the original request and rebuilds the run from scratch, so it still works after the original job has expired, just with a fresh wait. Everything above is also reachable as a plain public JSON API — see Help › API for every endpoint and example request.
Technical note
Limits live in GET /api/v1/capabilities /
GET /api/v1/health.limits and are enforced server-side
(the UI only mirrors them): max_domain_km 400,
max_cells 1,500,000, max_run_seconds 900,
momentum solver capped separately at 60,000 cells. A custom
mesh_resolution that would exceed the cell budget gets a
400 response naming the coarsest resolution that would
fit. Job lifetime is presence-based, not a flat calendar TTL: every
view of a job bumps its last_seen, and a cleanup thread
deletes a job's directory once nobody has looked at it for
WN_JOB_IDLE_TTL_S (default 300s) — a job that's
queued/running is never deleted mid-flight.
GET /api/v1/jobs/{id}/recipe returns the exact original
request, re-postable to /api/v1/jobs to recreate a run.
8. Data sources and credits
Windfield runs on, and is grateful to: the USDA Forest Service Rocky Mountain Research Station / Missoula Fire Sciences Lab's WindNinja; AWS Terrain Tiles (SRTM, USGS 3DEP, GMTED) for elevation; Open-Meteo and the NOAA National Weather Service for current conditions; NOAA NOMADS (GFS, HRRR, NAM) for forecasts; NDBC and Synoptic Data for additional live stations; OpenStreetMap Nominatim for place search; and OpenStreetMap, OpenTopoMap and Esri for basemaps. Full attribution, licences and links: Credits.
Windfield has no login and no accounts. Its privacy policy and terms are SummitFlux's own: summitflux.com/privacy-policy.