Windgram derivations
Each weather model publishes atmospheric fields; one shared derivation turns an hourly vertical column into stability, cloud base, thermal velocity, boundary-layer top, and usable lift. Its constants and fallback rules define the product.
Inputs and dependencies across the document boundary
The pipeline publishes four derived quantities; the package turns published levels into stability and cloud fields.
A dependency graph from model inputs through retained-column, parcel, and heat-flux intermediates to profile-derived quantities and package-rendered fields.
Required surface and pressure-level fields
Section titled “Required surface and pressure-level fields”Every deterministic builder must supply the same source shape for each site and forecast hour:
| Part of the column | Fields |
|---|---|
| Surface | temperature, 10 m wind, cloud cover, dew point depression, sensible and latent heat flux, sea-level pressure, precipitation |
| Pressure levels | geopotential height, temperature, dew point depression, wind speed and direction |
| Terrain | model elevation at the launch grid cell |
The pressure levels are curated per model and declared in the model catalogue (models.json) —
the ECCC 2.5–15 km deterministic feeds publish a 14-level 1015–600 hPa band (the 1 km feed and the
ensembles carry fewer), the NOAA feeds the eight or nine levels their
files carry. The derivation is transport-independent and tolerates an absent level.
1. Discard levels below model terrain
Section titled “1. Discard levels below model terrain”Pressure levels are sorted by height and discarded unless they are finite and at least 20 m above model terrain. In mountains, 925 hPa—and sometimes higher levels—can be underground. Plotting them would invent an atmosphere beneath model terrain and corrupt every interpolation above it.
This filter makes model terrain data, not metadata. Change its elevation and boundary-layer depth, surface parcel temperature, cloud-base elevation, and the set of valid pressure levels all change.
2. Build the stability and cloud fields
Section titled “2. Build the stability and cloud fields”Between adjacent retained levels, local lapse rate is:
lapse = (T_next − T) / (z_next − z) × 304.8 # °C per 1000 ftNegative means temperature decreases with height. The renderer classifies the result into eight fixed stability bands. The renderer hatches a level as model cloud when its dew point depression — derived from the published dew point — falls below 0.5 °C; the pipeline publishes the moisture, not the flag.
3. Estimate cloud base
Section titled “3. Estimate cloud base”The surface parcel’s condensation level uses Bolton (1980, eq. 15), which gives the LCL temperature explicitly from surface temperature and dew point — no iteration, no lookup:
T_LCL = 1 / (1/(T_d − 56) + ln(T/T_d)/800) + 56 # kelvinparcelLclM = modelElevationM + (T − T_LCL) / 0.0098 # dry-adiabatic climbBolton’s fit is accurate to 0.1 K across the meteorological range; against Romps (2017)’s exact closed form the height stays within about 1%, most of that the shared dry-lapse constant. It retires the inherited 121 m per degree linear estimate, which sat 35–58 m below the parcel across the repository’s real columns — a fair estimate, now an unnecessary one.
The parcel is not the only evidence. When the published column itself saturates below the parcel
LCL — dew point depression down at the same 0.5 °C the renderer hatches as dense cloud, the crossing
interpolated between samples — the model has already put cloud beneath the parcel estimate, and
cloudBaseM publishes that lower height instead. A drier column publishes the parcel LCL alone; a
saturated or supersaturated surface puts cloud base at model terrain. The value never sits below
terrain.
4. Lift the surface parcel
Section titled “4. Lift the surface parcel”The surface parcel cools at 0.0098 °C/m as it rises dry adiabatically. The code walks upward until the
parcel is no longer warmer than the model environment, then intersects the parcel and environmental
temperature lines inside that layer. The crossing is boundaryLayerTopM.
If the parcel stays warmer than the entire available sounding, the top level is returned. That value is a column ceiling, not evidence that mixing stops there; the ensemble work records this kind of censoring explicitly.
How a parcel meets an inversion
Step through two controlled temperature columns: one cap yields to heating; the other persists.
An interactive windgram comparing an eroding morning inversion with a persistent inversion. The parcel-derived boundary-layer top is compared with launch altitude through six teaching hours.
Conclusion. Surface heating can deepen a parcel-derived mixed layer through launch altitude, but heating alone does not guarantee that outcome when the warm cap remains.
5. Turn surface heating into w*
Section titled “5. Turn surface heating into w*”Deardorff’s thermal velocity scale combines virtual heat flux and boundary-layer depth:
Q_v = SHTFL + 0.000245268 × T_K × LHTFLθ = T_K × (1015 / p_first)^0.28482w* = ((0.0075516 / θ) × Q_v × D)⅓D is boundary-layer depth in metres. The latent term accounts for moist air’s buoyancy contribution;
the 0.0075516 factor folds gravity, air density, and heat capacity into compatible units. If virtual
heat flux or depth is non-positive, w* is zero. Night, rain, and heavily suppressed heating therefore
produce no thermal forecast by construction.
6. Find usable lift
Section titled “6. Find usable lift”canadarasp’s Hcrit logic evaluates the strongest-core profile described in Why usable lift can sit above the boundary layer:
- If
2.02 × w* < 1 m/s, even the profile maximum cannot beat the sink threshold; publish null. - Start at 0.25 of the boundary-layer depth and evaluate
w* × 4 × (z/D)⅓ × (1 − 0.8z/D)at each retained level. - Interpolate the first height where the core falls to 1 m/s.
- Stop at cloud base if it comes first.
- If the sounding ends before a crossing, fall back to boundary-layer top, still capped at cloud base.
The code publishes the result as usableLiftTopM; achieved altitude also depends on the air and pilot.
The windgram package exposes the same derivation with the sink rate as a parameter (usableLiftTopM
in windgram/derive, a pure function of the published document); the pipeline’s published value is
the 1 m/s case, and a package test asserts the two agree exactly on a real profile.
What stops usable lift first?
Change only sink rate while the atmospheric column, parcel top, and cloud base remain fixed: some hours answer 'cloud base', others answer 'your sink rate' — and the slider moves the boundary between them.
An interactive windgram applying the package's parameterized usable-lift derivation to a convective column whose hours trade between the cloud-base cap and the sink-rate limit.
Conclusion. The strongest midday hours are cloud-base-capped: no sink rate the slider offers moves them, because condensation stops the climb before the glider does. The shoulder hours are sink-limited: raising the sink rate pulls their usable top down and finally erases them. One derivation, two regimes — and the slider walks each hour across the boundary.
7. Publish unsmoothed; smooth in the renderer
Section titled “7. Publish unsmoothed; smooth in the renderer”The pipeline publishes derived series exactly as computed. The renderer may apply a 1–2–1 temporal
kernel to cloud base and usable-lift top (smooth121 in the windgram package, on by default to match
the historical look), and only across values exactly one hour apart — three-hourly models, missing
hours, boundary-layer top, and w* are never smoothed. Consumers who want the model’s values read the
JSON; the smoothing is undoable because it no longer happens upstream.
8. Publish every hour; window in the renderer
Section titled “8. Publish every hour; window in the renderer”Profiles carry every forecast hour, chronological. Day windowing (07:00–21:00 local, discard days with fewer than five samples, fall back to all hours when none qualifies) moved to the windgram package with the timezone and day bounds as parameters, so no consumer’s clock is baked into the open dataset. The published coordinates let a renderer derive local time for any site.
Five constants define cloud base, lift, and hatching
Section titled “Five constants define cloud base, lift, and hatching”| Constant | Meaning | Status |
|---|---|---|
| 56 K, 800 K | Bolton’s LCL-temperature fit | Bolton (1980) |
| 0.0098 °C/m | dry adiabatic lapse | physical approximation |
| 4.0 | strongest-core profile coefficient | canadarasp choice |
| 1 m/s | glider sink threshold | canadarasp choice |
| 0.5 °C | saturation threshold (cloud-base floor and renderer hatch) | display choice, now load-bearing |
Changing a constant creates a different metric. Name it separately and test it against the archive.
The 121 m/°C LCL approximation inherited from the original charts is retired; Bolton’s fit replaced it once the published column could confirm the parcel against the model’s own moisture.
Executable authority: pipeline/src/windgram/derive.py, with exact assertions in
tests/.