Windfield

see how wind moves over real terrain

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.

The Windfield pipeline Your browser sends the area you drew to the server. The server downloads elevation tiles, builds a projected terrain mesh, burns in structures, runs WindNinja, and sends back a speed image, arrows, and a data grid. Your browser only draws the result. your browser our server your browser You draw an area (and options) Download elevation SRTM / 3DEP / GMTED Build terrain mesh projected, your resolution Burn in structures if you drew any Run WindNinja mass-conserving or momentum Speed image + arrows, legend Data grid for probing, downloads
Your browser only ever draws the area you pick and the results that come back. Every heavy step — terrain, meshing, structures, the wind solve — runs on the server.
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:

Technical note
Simulate
initialization_method = domainAverageInitialization
Points
pointInitialization, one station CSV per point (WindNinja rejects multiple stations in one file), date_time is 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_type one of NOMADS-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.

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.

AGL follows the terrain; MSL is a flat altitude A cross-section of a mountain. The 10-metre AGL line follows the ground up and over the peak and back down. A fixed-altitude MSL line stays flat and ends up underground on the peak but high above the low ground on either side. 10 m AGL — follows the ground a fixed MSL altitude — stays flat underground on the peak… …high above the low ground here peak sea level / MSL datum below
The 10 m AGL wind (teal) tracks the ridge up and down. A flat MSL altitude slice (red) cuts through the peak and floats far above the valleys — the two are not the same thing.
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.

Fast mass-conserving flow vs. momentum-solver flow around a block Two side-by-side panels showing wind approaching a raised block from the left. In the fast mass-conserving panel, streamlines smoothly bulge up and over the block with no wake. In the momentum solver panel, streamlines separate at the block's edges and curl into eddies and a recirculating wake behind it. Fast (mass-conserving) smooth over the top — no wake Momentum (OpenFOAM) separation, eddies, recirculating wake recirculation
Same block, two solvers. The fast solver treats it as a smooth bump in the terrain; the momentum solver resolves the separated, recirculating wake actually forms behind a sharp-edged obstacle.
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

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:

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.