Thermal Simulation — Technical Documentation

Pre-computed solar heating pipeline for paragliding thermal analysis  ·  Terrain: SRTM 30 m · Land cover: ESA WorldCover 2021 · Calibration: ERA5 / Open-Meteo

Overview

The Thermal simulation is a diagnostic pre-computation pipeline — it does not solve fluid dynamics equations in real time. Instead, it walks through a sequence of physical steps (solar geometry → surface heating → thermal inertia → orographic flow → trigger zones) and produces a set of 2D maps, one per time step, exported as JSON to the Three.js viewer. The pipeline runs in seconds to minutes on the server.

Elevation data comes from SRTM 30 m (physics) and AWS Terrarium ~7 m tiles (visual mesh). Land cover is from ESA WorldCover 2021 (10 m), resampled to the terrain grid. The peak ground–air temperature difference is calibrated from ERA5 reanalysis (Open-Meteo API).

Pipeline Overview

1
Terrain (dual-resolution) — SRTM 30 m DEM for all physics (slope normals, shadow casting, orographic flow); AWS Terrarium ~7 m tiles for the visual Three.js mesh.
2
Land cover — ESA WorldCover 2021 (10 m) resampled to the terrain grid, converted to per-cell thermal factor tf and time constant τ.
3
Sun positions — real astronomical ephemeris (pysolar) computed every 30 min during daylight for the chosen date and area centre.
4
Solar heating — direct beam + diffuse radiation per cell, with terrain shadow ray-casting. Slope–sun incidence angle computed from DEM surface normals.
5
Thermal inertia — ground temperature accumulates across time steps using a first-order relaxation (low-pass filter) with per-cell time constant τ derived from land cover.
6
Thermal release cooling — cells with high trigger factor lose heat when their temperature anomaly exceeds a release threshold, simulating bubble detachment and surface cooling.
7
Heat advection along orographic flow — temperature anomaly is advected upslope (anabatic wind) + toward trigger zones (collector suction), using a first-order upwind scheme with adaptive CFL substeps.
8
Trigger and collector maps — static trigger factor (terrain convexity + land-cover boundary gradient) and accumulated heat-weighted convergence field, normalised to [0, 1].
9
Export — all scenario maps plus static fields packed into a JSON file served to the Three.js viewer.

Physics Modelled

1. Sun position and slope–sun geometry

Exact solar azimuth and elevation are computed with pysolar (USNO astronomical algorithm) for each time step. The direct beam incidence on each terrain cell accounts for the slope and aspect:

cos(θ_inc) = n̂ · ŝ [dot product of surface normal and sun direction unit vector] n̂ = (−∂Z/∂x, −∂Z/∂y, 1) / |...| [derived from DEM gradient]

A south-facing 30° slope receives significantly more direct beam than a flat or north-facing surface at the same insolation. The incidence field is Gaussian-smoothed (σ = slope_smooth, default 1.5 cells) to suppress artefacts from noisy DEM normals on steep terrain.

2. Terrain shadow casting

For each time step, a ray is cast from every terrain cell toward the sun. If any terrain point along that ray rises above the sun ray, the cell is in shadow and receives no direct beam — only diffuse sky radiation. This produces realistic cold patches on north-facing slopes and valley floors during morning and evening.

3. Atmospheric transmittance

The solar beam weakens as it passes through the atmosphere, proportional to the airmass (path length). The Kasten-Young airmass formula and a Bird clear-sky transmittance model are used:

AM = 1 / [sin(el) + 0.50572 · (el_deg + 6.07995)^(−1.6364)] [airmass] T_atm = T_base ^ (AM ^ T_exp) [atmospheric transmittance] At el = 68° (midday): T_atm ≈ 0.70 At el = 14° (dawn): T_atm ≈ 0.38

A low sun delivers much less energy than geometry alone would suggest. Default: T_base = 0.70, T_exp = 0.678.

4. Diffuse sky radiation

Cells in terrain shadow still receive scattered sky radiation. Modelled with an isotropic sky assumption and a sky view factor:

I_diffuse = diffuse_frac · (1 − T_atm) · SVF SVF = (1 + cos(slope)) / 2 [sky view factor — flat = 1, vertical wall = 0.5]

More scattering at low sun angles (higher diffuse fraction at dawn/dusk). Default: diffuse_frac = 0.30.

5. Ground temperature anomaly (surface heat flux)

The instantaneous ground temperature anomaly is the product of total insolation, the thermal factor, and the peak heating amplitude:

dT_instant(x,y) = max_heat · tf(x,y) · (I_direct + I_diffuse) I_direct = cos(θ_inc) · T_atm · (1 − shadow) I_diffuse = diffuse_frac · (1 − T_atm) · SVF

max_heat is the peak ground–air temperature difference under full insolation on grassland (tf = 1). It is auto-calibrated from ERA5 data or set manually.

6. Thermal inertia — first-order relaxation

The ground does not instantly reach its equilibrium temperature. Heat accumulates using a per-cell low-pass filter with time constant τ (land-cover dependent):

decay = exp(−dt / τ) dT(t) = dT(t−1) · decay + dT_instant · (1 − decay)

This causes thermals to lag behind the sun — peaking in the early afternoon rather than at solar noon. Bare rock (τ ≈ 0.5 h) heats and cools rapidly; forest (τ ≈ 3.75 h) responds slowly; water (τ ≈ 22 h) barely changes within a day.

7. Land cover — thermal factors

Each grid cell has a thermal heating factor tf (scaling the temperature anomaly) and a time constant τ (controlling how quickly it heats and cools), derived from ESA WorldCover 2021:

Land cover tf (heating factor) τ (h) Effect
Bare rock / scree 1.80 0.50 Strongest heating, fastest response — spikes at noon
Built-up 1.30 0.75 Concrete/tarmac heat island
Grassland 1.00 1.50 Reference land cover
Cropland 0.85 1.30 Moderate inertia
Shrubland 0.70 2.00 Partial canopy — slower
Tree cover 0.45 3.75 Forest: weak and slow — canopy evaporation damps heating
Permanent water 0.03 22.5 Very high heat capacity — nearly constant temperature

8. Thermal top height

The ceiling at which a thermal parcel loses buoyancy is derived from the surface temperature anomaly and the difference between the Dry Adiabatic Lapse Rate and the Environmental Lapse Rate:

h_top = dT / (DALR − ELR) DALR = 9.8 K/km [fixed by thermodynamics] ELR = adjustable slider (standard: 6.5 K/km) stab = DALR − ELR = 3.3 K/km → h_top ≈ 300 m per K of surface anomaly

Higher ELR → less stable atmosphere → thermals reach greater altitude. At ELR = DALR the atmosphere is neutral and thermals rise indefinitely (convective instability).

9. Thermal release cooling

Cells with a high trigger factor (ridges, land-cover boundaries) lose excess heat each time step once the temperature anomaly exceeds a release threshold, simulating periodic bubble detachment and surface cooling:

dT ← dT − trigger_factor · max(0, dT − T_release) · K_release · dt K_release = 0.3 h⁻¹ (default) T_release = 1.5 K (default)

10. Heat advection along orographic flow

After the release step, the temperature anomaly field is advected using first-order upwind finite differences along a velocity field with two components:

u = (∇Z / |∇Z|) · dT [orographic term: upslope anabatic wind] − k_collect · ∇dT [collector suction: drains warm cells toward trigger zones] V_advect = V_max · √( clip(slope_75th / slope_ref) · clip(mean_dT / max_heat) ) V_max = 1000 m/h (default ceiling)

The advection speed is computed dynamically each step — nearly idle at dawn (weak heating), peaking around solar noon. The time step is subdivided adaptively to keep the CFL number ≤ CFL_safety (default 0.9).

11. Trigger and collector zone maps

Two static diagnostic maps are derived from the simulation:

12. Thermal plume profile (plume speed estimate)

Clicking a cell in the viewer shows an estimated vertical speed profile derived from simple parcel buoyancy theory (no entrainment, no turbulence):

w(z) = √(2 · g/T₀ · ΔT(z) · z_AGL) ΔT(z) = ΔT_eff · (1 − z/H) [linear temperature excess decaying to zero at ceiling H] w_max = √(0.5 · g/T₀ · ΔT_eff · H) [peak speed at z = H/2] g = 9.81 m/s², T₀ = 293 K

This is a rough order-of-magnitude estimate only. Real thermals are slower because this formula ignores entrainment of surrounding air (which dilutes buoyancy), turbulent drag, and the continuous-plume structure. Typical paragliding thermal speeds are 1–4 m/s; the formula may overestimate by a factor of 2–3 in strong conditions. Use it as a relative indicator, not an absolute number.

13. ERA5 calibration of max_heat

The scale factor max_heat is derived automatically from ERA5 reanalysis data (Open-Meteo archive API) for the specific simulation date and site. Clear-sky midday values of surface_temperature − temperature_2m from the past 5 years are averaged. This implicitly captures seasonal effects (soil moisture, vegetation state) without explicit modelling.

Numerical Methods

Shadow ray-casting

For each terrain cell (i, j) and each time step, a ray is marched from the cell toward the sun in the horizontal plane. The ray is stepped in increments of one cell width; at each step the expected ray height (based on the solar elevation angle and horizontal distance) is compared with the DEM elevation. If any DEM point is higher than the ray, the cell is flagged as shadowed. To avoid self-shadowing artefacts, the ray starts one cell away from the source.

For step s = 1, 2, ..., N along the ray: x_s = i + s · cos(az), y_s = j + s · sin(az) z_ray = Z(i,j) + s · dx · tan(el) if Z(x_s, y_s) > z_ray: shadow = True (and stop)

The ray direction is bilinearly interpolated on the DEM, so fractional-cell positions are handled correctly. Complexity is O(NX · NY · ray_length) per time step.

Slope normals and incidence angle

Surface normals are computed from the DEM using central finite differences:

∂Z/∂x ≈ (Z[i+1,j] − Z[i−1,j]) / (2·dx) ∂Z/∂y ≈ (Z[i,j+1] − Z[i,j−1]) / (2·dy) n̂ = (−∂Z/∂x, −∂Z/∂y, 1) / |...| [outward surface normal] cos(θ_inc) = max(0, n̂ · ŝ) [clamp to avoid negative illumination]

One-sided differences are used at the domain boundary. The resulting cos(incidence) field is then Gaussian-smoothed with kernel radius σ = slope_smooth (default 1.5 cells) to suppress high-frequency artefacts from noisy DEM gradients on steep terrain.

Gaussian smoothing

Several fields (slope incidence, land-cover factor, trigger maps, advection flow) are spatially smoothed with a 2D Gaussian kernel of standard deviation σ (in grid cells). The kernel is applied separably as two 1D convolutions (x then y) for efficiency:

G(k) = exp(−k² / (2σ²)) for k = −3σ, ..., +3σ [truncated at 3σ] f_smooth = f ★ G_x ★ G_y [separable 2D convolution]

Boundary pixels are handled by reflection padding to avoid edge darkening. The kernel is re-normalised after truncation so the total weight sums to 1.

Thermal inertia — first-order IIR filter

The ground temperature response to solar forcing is modelled as a first-order Infinite Impulse Response (low-pass) filter applied per cell at each time step:

decay = exp(−dt / τ) dT(t) = dT(t−1) · decay + dT_instant(t) · (1 − decay) dt = 0.5 h (time step between scenarios) τ = per-cell time constant from land cover (in hours)

This is the exact discrete solution to the ODE d(dT)/dt = (dT_instant − dT) / τ, so it is exact regardless of step size and unconditionally stable for any τ and dt.

Trigger zone computation — terrain Laplacian

Terrain convexity (ridges and peaks have negative curvature) is computed from the DEM Laplacian using a 5-point finite-difference stencil, after Gaussian pre-smoothing (σ = convexity_smooth, default 4 cells):

∇²Z ≈ (Z[i+1,j] + Z[i−1,j] + Z[i,j+1] + Z[i,j−1] − 4·Z[i,j]) / dx² convexity = max(0, −∇²Z) [positive on ridges and peaks, zero on concave terrain]

The land-cover boundary gradient is computed as the magnitude of the gradient of the thermal factor field: |∇tf| via central differences. Both maps are individually Gaussian-blurred (σ = trigger_blur), normalised to [0, 1], then blended: trigger = w·convexity + (1−w)·|∇tf|.

First-order upwind advection

The temperature anomaly field is advected along the flow field (u, v) using first-order upwind finite differences — the spatial difference is taken from the upwind side to ensure stability:

if u[i,j] > 0: ∂dT/∂x ≈ (dT[i,j] − dT[i−1,j]) / dx (backward difference) if u[i,j] < 0: ∂dT/∂x ≈ (dT[i+1,j] − dT[i,j]) / dx (forward difference) (same for v and y direction) dT_new[i,j]=dT[i,j] − dt_sub · (u·∂dT/∂x + v·∂dT/∂y)

Upwind differencing is monotone (no spurious oscillations) but first-order accurate, introducing numerical diffusion proportional to u·dx/2. This artificial diffusion slightly spreads thermal plumes spatially, which is acceptable at typical grid spacings of 100–300 m.

Adaptive CFL substep splitting

The upwind scheme is stable only when the CFL condition is satisfied per cell. The advection step is subdivided into substeps of size dt_sub such that the maximum CFL across the domain is below the safety threshold:

CFL_max = max(|u|/dx + |v|/dy) · dt_sub ≤ CFL_safety (default 0.9) n_substeps = ceil(CFL_actual / CFL_safety) dt_sub = dt / n_substeps

At low wind speeds or coarse grids a single substep suffices. Near solar noon with strong heating and steep slopes (fast anabatic flow), 3–5 substeps may be used. The total advection displacement per full time step is therefore always V_advect · dt regardless of the substep count.

Collecting zone accumulation

The horizontal divergence of the flow field is computed at each scenario step using central differences:

div(u,v)[i,j] = (u[i+1,j] − u[i−1,j]) / (2·dx) + (v[i,j+1] − v[i,j−1]) / (2·dy) conv[i,j,t] = max(0, −div) [convergence: positive where flow accumulates] collector(i,j) = Σ_t conv[i,j,t] · max(0, dT[i,j,t] − threshold) collector → normalised to [0, 1]

The weighting by dT means that warm-but-divergent cells and cold-but-convergent cells both score low — only cells that are simultaneously warm and in a convergent zone accumulate a high collector score.

Parameters and Their Effects

Heating

Parameter Default Effect
max_heat 15 K Peak ground–air temperature anomaly under full insolation on grassland (tf = 1). Directly scales all surface heat fluxes. Auto-calibrated from ERA5. Primary knob for overall thermal strength.
diffuse_frac 0.30 Fraction of solar energy arriving as scattered sky radiation. Higher → more uniform heating across shadowed and north-facing slopes.
ELR (K/km) 6.5 Environmental Lapse Rate. Controls how high thermals rise: h_top = dT / (DALR − ELR). Higher ELR → thermals reach greater altitude. At 9.8 K/km (= DALR) the atmosphere is neutral.

Solar radiation model

Parameter Default Effect
T_atm base 0.70 Zenith atmospheric transmittance: T_atm = T_base^(AM^T_exp). Higher → more solar energy reaches the surface. Use 0.80–0.85 for high-altitude clear-sky days; lower for haze or humidity.
T_atm exponent 0.678 Controls how sharply transmittance drops at low sun angles (high airmass). Higher → stronger penalty near dawn/dusk; lower → more uniform insolation through the day.
σ slope (incidence smoothing) 1.5 cells Gaussian blur on the cos(incidence) field. Suppresses artefacts on cliff edges and noisy DEM normals. Set to 0 to disable.
Clear-sky threshold 600 W/m² Minimum direct radiation for an ERA5 hour to count as "clear-sky" in the max_heat calibration. Lower includes more marginal hours; higher keeps only sunniest midday observations.

Thermal inertia

Parameter Default Effect
τ hours (reference) 1.5 h Reference thermal time constant for grassland (tf = 1). Other land covers scale from this: rock × 0.35, forest × 2.5, water × 15. Smaller τ → faster, more peaked heating.
σ land cover (lc smoothing) 4 cells Gaussian blur on the land-cover thermal factor map. Blends abrupt forest/grassland boundaries. Set to 0 for sharp LC boundaries.

Trigger and collector zones

Parameter Default Effect
Convexity smooth σ 4 cells Gaussian blur on the DEM before computing curvature (Laplacian). Larger σ → peaks and ridges blend; smaller → sharper but noisier curvature map.
Trigger blur σ 3 cells Post-processing blur on convexity and LC boundary maps before blending. Controls spatial extent of trigger zones around ridges and forest edges.
Convexity weight 0.60 Blend: trigger = w·convexity + (1−w)·LC_boundary. Higher w favours terrain peaks/ridges; lower favours forest/grassland transitions.

Heat advection

Parameter Default Effect
V_advect_max 1000 m/h Ceiling for anabatic flow speed. Scales the entire advection velocity field. Higher → heat moves faster upslope.
Ref slope 0.30 Characteristic slope (tan) normalising the terrain gradient in the advection scaling. Lower → advection activates on gentler terrain.
k_collect 1.0 Strength of collector-suction term that pulls heat down the ΔT gradient toward trigger zones. 0 = pure orographic flow.
Flow smooth σ 5 cells Gaussian blur on the advection vector field before applying it. Reduces checkerboard artefacts from the upwind scheme.
CFL safety 0.9 Target CFL for adaptive substep splitting. Lower → more substeps, more stable. Values above 1.0 are unstable.

Thermal release

Parameter Default Effect
K_release 0.3 h⁻¹ Rate at which excess heat is removed from trigger-zone cells once the threshold is exceeded. Higher → thermals release more aggressively each step.
Release threshold 1.5 K Minimum temperature anomaly above which release cooling activates. Acts as a noise floor: only cells that have genuinely accumulated heat release.

Diurnal model (ERA5 gap-fill)

Parameter Default Effect
Diurnal amplitude 5 K Half the daily temperature swing for gap-filling missing ERA5 hourly values: T(h) = T_mean + A·cos(2π(h − h_max)/24). Used only when ERA5 data has gaps.
Peak hour 14 h (local) Hour of the daily temperature maximum in the cosine gap-fill model.

Output Fields

Field Symbol Description
Thermal heat dT(x,y,t) Ground temperature anomaly at each time step (filtered by display threshold). Positive = warm surface. Primary indicator of thermal strength and location.
Thermal top h_top(x,y,t) Estimated ceiling altitude (m AGL) where a parcel from this cell loses buoyancy. Derived from dT and atmospheric stability.
Daily heat max_t dT(x,y) Maximum temperature anomaly over all time steps — the single hottest moment of the day per cell.
Trigger zones trigger(x,y) Static terrain convexity + land-cover boundary score. High values (bright) identify where thermals preferentially detach from the surface.
Collecting zones conv(x,y) Accumulated heat-weighted horizontal convergence. High values mark areas that funnel warm air into trigger points.
Flow arrows u, v (x,y,t) Advection velocity field (orographic + collector suction). Shows the direction thermal heat moves across the terrain at each time step.

Data Sources

Dataset Used for Source Resolution
Elevation — simulation Shadow casting, slope normals, thermal tops, orographic flow SRTM via AWS elevation tiles (Mapzen/Tilezen) ~30 m
Elevation — visual mesh 3D terrain surface rendered in the browser AWS Terrain Tiles, Terrarium RGB encoding (IGN RGE Alti for France) ~7 m
Land cover Thermal factor and time constant per cell ESA WorldCover 2021 10 m
Sun position Azimuth + elevation at each time step pysolar (USNO astronomical algorithm)
Calibration temperature max_heat scale factor ERA5 reanalysis via Open-Meteo archive API ~28 km

Dual-resolution elevation strategy

The SRTM 30 m grid is used for all physics computations: slope normals, shadow ray-casting, thermal tops, and orographic flow. A coarser grid keeps the computation and exported JSON compact.

The AWS Terrarium ~7 m tiles are fetched once and used only to build the visual terrain mesh in Three.js. At zoom level 14 the underlying source for France is IGN RGE Alti (LiDAR-derived), giving ~4–5× higher surface detail than SRTM. The RGB elevation encoding is: elevation = R×256 + G + B/256 − 32768, delivered in Web Mercator (EPSG:3857) and reprojected to WGS84 before use.

Physics overlays (heatmap, flow arrows, etc.) are rendered on the coarser 30 m grid but remain visually correct because they cover the same geographic extent and use depthWrite: false, floating just above the terrain surface.

Simplifications

Limitations