Meteogram derivations
Each weather model publishes atmospheric fields. One shared derivation turns an hourly vertical column into cloud base, thermal velocity, boundary-layer top, and usable lift, and every published profile carries the results. The renderer classifies stability from the same published column. The 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
Every deterministic builder must supply the same source fields 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 model catalogue (models.json) declares a curated set of pressure levels for each model. The
ECCC 2.5–15 km deterministic feeds publish a 14-level band from 1015 to 600 hPa, and the 1 km feed
and the ensembles carry fewer. The NOAA feeds publish the eight or nine levels their files carry.
The derivation does not depend on the transport, and it tolerates an absent level.
1. Discard levels below model terrain
Pressure levels are sorted by height. A level is kept only if it is finite and more than 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.
Because of this filter, model terrain elevation is an input to the derivation. Change it, and the boundary-layer depth, surface parcel temperature, cloud-base elevation, and set of valid pressure levels all change.
2. Build the stability and cloud fields
Between adjacent retained levels, the local lapse rate is:
lapse = (T_next − T) / (z_next − z) × 304.8 # °C per 1000 ftA negative value means temperature decreases with height. The renderer classifies the result into eight fixed stability bands. It hatches a level as model cloud when the level’s dew point depression, derived from the published dew point, falls below 0.5 °C. The forecast engine publishes the moisture and leaves the cloud flag to the renderer.
3. Estimate cloud base
The surface parcel’s condensation level uses Bolton (1980, eq. 15). It gives the LCL temperature explicitly from surface temperature and dew point, with no iteration and no lookup.
T_LCL = 1 / (1/(T_d − 56) + ln(T/T_d)/800) + 56 # kelvinparcelLclM = modelElevationM + (T − T_LCL) / 0.0098 # dry-adiabatic climb (intermediate — not a published field)Bolton’s fit is accurate to 0.1 K across the meteorological range. Against the exact closed form of Romps (2017), the height stays within about 1%, and most of that difference comes from the shared dry-lapse constant. The inherited 121 m/°C linear rule that Bolton replaced sat 35-58 m low (the calibrated mean is nearer 124 m/°C). The logbook records its retirement.
The derivation also checks the published column. When the column itself saturates below the parcel
LCL, the model has already put cloud beneath the parcel estimate, and cloudBaseM publishes that
lower height instead. Saturation here means dew point depression down at the same 0.5 °C the
renderer hatches as dense cloud, with the crossing interpolated between samples. A drier column
publishes the parcel LCL alone. A saturated or supersaturated surface puts cloud base at model
terrain. The value is always at or above terrain.
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 code returns the top level. That value is a column ceiling and is no 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 Meteogram comparing an eroding morning inversion with a persistent inversion. The parcel-derived boundary-layer top is compared with launch altitude through six 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*
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 the buoyancy that moisture adds.
The 0.0075516 factor folds gravity, air density, and heat capacity into compatible units. If virtual
heat flux or depth is zero or negative, w* is zero. Night, rain, and heavily suppressed heating
therefore produce no thermal forecast by construction.
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, so publish null. - Seed the scan at 0.2 of the boundary-layer depth with an updraft of
1.97 × w*(canadarasp’s entry point), then evaluatew* × 4 × (z/D)⅓ × (1 − 0.8z/D)at each retained level at or above 0.25 of the depth. - 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. The altitude a pilot achieves also depends on the
air and the pilot. The derivation itself lives in the read-side package as usableLiftTopM in
@azohra/meteo.briefing/derive, a pure function of the published document that takes the sink rate
as a parameter. The forecast engine imports that implementation and stores its 1 m/s evaluation. The
published value and a consumer’s re-derivation are therefore the same code, and an engine regression
test holds a real published profile to it.
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 Meteogram 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
The forecast engine 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 @azohra/meteo.briefing/meteogram,
on by default to match the historical look). It smooths only across values exactly one hour apart.
Three-hourly models, missing hours, boundary-layer top, and w* are left unsmoothed. Consumers who
want the model’s values read the JSON. The smoothing can be undone because it no longer happens
upstream.
8. Publish every hour; window in the renderer
Profiles carry every forecast hour in chronological order. Day windowing lives in
@azohra/meteo.briefing, with the timezone and day bounds as parameters, so the published dataset
carries no consumer’s clock. The window keeps 07:00–21:00 local, discards days with fewer than five
samples, and falls back to all hours when no day qualifies. The published coordinates let a renderer
derive local time for any site.
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 executable authority is forecast/src/derive.ts, with exact assertions in
forecast/test/. The renderer-side lapse-rate and stability-band definitions live in
briefing/src/derive/lapse.ts and
briefing/src/derive/stability.ts.