Profile memory
Comment cleanup
This commit is contained in:
fraxle
2026-05-18 23:02:41 +01:00
parent dded468bec
commit 85c415d1f5
16 changed files with 504 additions and 581 deletions
+121 -121
View File
@@ -1,5 +1,5 @@
// ════════════════════════════════════════════════════════════════════════
// physics.js Physical constants and meteorological calculations.
// ------------------------------------------------------------------------
// physics.js - Physical constants and meteorological calculations.
//
// All numbers are peer-reviewed constants or coefficients. Nothing in
// here should need editing unless the underlying science changes.
@@ -10,63 +10,63 @@
// calcIndoorTempPass(TaArr, globArr, elevArr, buildingType) passive indoor temp
// calcManagedIndoorTempPass(TaArr, globArr, elevArr, buildingType) managed indoor temp
// calcVehicleInteriorTemp(Ta, globalRad, solElev, vehicleType, ventilated)
// vaporPressureHpa(Ta, RH) Magnus formula hPa
// solarElevationDeg(lat, lon, dateUTC) NOAA simplified solar position (°)
// vaporPressureHpa(Ta, RH) Magnus formula - hPa
// solarElevationDeg(lat, lon, dateUTC) NOAA simplified solar position (-)
// calcTmrt(Ta, dirRad, diffRad, globalRad, solElev) Mean radiant temperature
// utciApprox(Ta, Tmrt, va10, ehPa) Bröde et al. 2012 UTCI polynomial
// ════════════════════════════════════════════════════════════════════════
// utciApprox(Ta, Tmrt, va10, ehPa) Br-de et al. 2012 UTCI polynomial
// ------------------------------------------------------------------------
import { VEHICLE_TYPES, BUILDING_TYPES } from './utils.js';
// ═══════════════════════════════════════════════════════════════════
// -------------------------------------------------------------------
// PHYSICAL CONSTANTS
// ───────────────────────────────────────────────────────────────────
// These are real-world physics values don't change them unless you
// -------------------------------------------------------------------
// These are real-world physics values - don't change them unless you
// have a peer-reviewed reason. They're used by calcTmrt() below to
// work out how much heat your skin actually absorbs from the sun.
// SIGMA StefanBoltzmann constant (radiates heat)
// EPSILON_P emissivity of human skin (~0.97)
// A_K short-wave absorption coefficient for clothing
// ALBEDO_GRASS how much sun grass reflects back at you (23%)
// ALBEDO_CONCRETE how much sun concrete reflects back (30%)
// SIGMA - Stefan-Boltzmann constant (radiates heat)
// EPSILON_P - emissivity of human skin (~0.97)
// A_K - short-wave absorption coefficient for clothing
// ALBEDO_GRASS - how much sun grass reflects back at you (23%)
// ALBEDO_CONCRETE - how much sun concrete reflects back (30%)
// Concrete absorbs more net solar than grass and has
// no evaporative cooling, so its surface runs hot.
// ═══════════════════════════════════════════════════════════════════
// -------------------------------------------------------------------
export const SIGMA = 5.670374419e-8;
export const EPSILON_P = 0.97;
export const A_K = 0.7;
export const ALBEDO_GRASS = 0.23;
export const ALBEDO_CONCRETE = 0.30;
// ═══════════════════════════════════════════════════════════════════
// -------------------------------------------------------------------
// CONCRETE SURFACE TEMPERATURE (Urban profile)
// ───────────────────────────────────────────────────────────────────
// -------------------------------------------------------------------
// Estimates the surface temperature of exposed concrete using a
// simplified energy-balance approach:
// Absorbed solar = globalRad × (1 albedo)
// No latent heat (no evaporation concrete is dry)
// Convective loss to air proportional to wind speed
// Result is clamped to a physically plausible range
// - Absorbed solar = globalRad - (1 - albedo)
// - No latent heat (no evaporation - concrete is dry)
// - Convective loss to air proportional to wind speed
// - Result is clamped to a physically plausible range
//
// This is what matters for contact heat stress in cities the UTCI
// standard uses grass, which runs ~515 °C cooler than urban concrete
// This is what matters for contact heat stress in cities - the UTCI
// standard uses grass, which runs ~5-15 -C cooler than urban concrete
// on a sunny day because grass sweats (transpires).
// ═══════════════════════════════════════════════════════════════════
// -------------------------------------------------------------------
export function calcConcreteTemp(Ta, globalRad, windSpeed) {
if (globalRad == null || Ta == null) return null;
const absorbed = globalRad * (1 - ALBEDO_CONCRETE); // W/m²
// Convective heat transfer coefficient: ~10 W/m²K still air, rises with wind
const absorbed = globalRad * (1 - ALBEDO_CONCRETE); // W/m-
// Convective heat transfer coefficient: ~10 W/m-K still air, rises with wind
// (10 reflects realistic natural convection; 5 was too low and ran too hot)
const hc = 10 + 4.5 * Math.sqrt(Math.max(windSpeed || 0, 0));
// Surface temp: Ta + solar gain / convective loss
const Ts = Ta + absorbed / hc;
// Clamp: can't be cooler than air, cap at 85 °C (melting asphalt territory)
// Clamp: can't be cooler than air, cap at 85 -C (melting asphalt territory)
return Math.max(Ta, Math.min(Ts, 85));
}
// ═══════════════════════════════════════════════════════════════════
// -------------------------------------------------------------------
// UK HOUSE INDOOR TEMPERATURE (windows closed, no active cooling)
// ───────────────────────────────────────────────────────────────────
// -------------------------------------------------------------------
// Estimates the ambient indoor air temperature of a typical UK brick
// house with windows closed and no air conditioning.
//
@@ -74,37 +74,37 @@ export function calcConcreteTemp(Ta, globalRad, windSpeed) {
//
// 1. WALL CONDUCTION
// Heat conducts through brick cavity walls and roof. UK Part L
// compliant walls have a U-value around 0.280.45 W/m²K; older
// compliant walls have a U-value around 0.28-0.45 W/m-K; older
// solid-brick stock runs higher. A representative mid-stock value
// is used. This drives a slow, steady heat transfer proportional
// to the difference between outdoor and indoor air temperature.
//
// 2. WINDOW SOLAR GAIN
// A typical UK semi has ~1518% glazing ratio. Solar energy
// A typical UK semi has ~15-18% glazing ratio. Solar energy
// transmits through glass, is absorbed by floors and furniture,
// and heats the indoor air. Gain is averaged across orientations
// (not all windows face south). Diffuse radiation contributes
// regardless of sun angle.
//
// THERMAL LAG
// Brick and concrete have high thermal mass the house responds
// Brick and concrete have high thermal mass - the house responds
// slowly to outdoor temperature swings. Each hour builds a realistic
// target temperature from outdoor air, window solar gain, retained
// warmth, and internal gains, then the room temperature lags toward
// that target. This avoids runaway accumulation while still giving
// the characteristic late-day indoor peak.
// Call calcIndoorTempPass() on the full hourly arrays after
// building rows it returns a per-hour indoor temp array.
// building rows - it returns a per-hour indoor temp array.
//
// No mechanical cooling. Minimal infiltration (windows closed).
// Internal heat gains (people, appliances) are not modelled.
//
// Colour thresholds:
// < 20 °C cool, may need heating
// 2026 °C comfortable
// 2632 °C warm; WHO heatwave advisory threshold for sleeping
// > 32 °C hot; risk for elderly and vulnerable occupants
// ═══════════════════════════════════════════════════════════════════
// < 20 -C - cool, may need heating
// 20-26 -C - comfortable
// 26-32 -C - warm; WHO heatwave advisory threshold for sleeping
// > 32 -C - hot; risk for elderly and vulnerable occupants
// -------------------------------------------------------------------
// Two-pass function: call with the full arrays of hourly Ta and globalRad.
// Returns an array of indoor temperatures, one per hour.
@@ -124,7 +124,7 @@ export function calcIndoorTempPass(TaArr, globArr, elevArr, buildingType = 'bric
const Ta = TaArr[i] ?? Ti;
const glob = globArr[i] ?? 0;
// Solar gain through windows (W/m² effective)
// Solar gain through windows (W/m- effective)
const solarGain = glob * glazingRatio * gValue * orientFactor;
const retainedWarmth = Math.max(0, (baseTemp ?? 16) - Ta) * (retainedScale ?? 0.35);
@@ -141,9 +141,9 @@ export function calcIndoorTempPass(TaArr, globArr, elevArr, buildingType = 'bric
return result;
}
// ═══════════════════════════════════════════════════════════════════
// UK HOUSE INDOOR TEMPERATURE MANAGED (curtains closed, windows open)
// ───────────────────────────────────────────────────────────────────
// -------------------------------------------------------------------
// UK HOUSE INDOOR TEMPERATURE - MANAGED (curtains closed, windows open)
// -------------------------------------------------------------------
// Models the same UK brick house as calcIndoorTempPass but with two
// behavioural interventions that reflect standard heatwave advice:
//
@@ -156,11 +156,11 @@ export function calcIndoorTempPass(TaArr, globArr, elevArr, buildingType = 'bric
// When outdoor air is cooler than the indoor air, windows are open
// and a ventilation heat exchange pulls the indoor temp toward
// outdoor. When outdoor is hotter than indoor, windows are kept
// shut so this strategy never makes things worse, only better.
// shut - so this strategy never makes things worse, only better.
// Ventilation rate ~2 air changes/hour for a well-opened house.
//
// Same thermal lag model as calcIndoorTempPass (4 h brick time constant).
// ═══════════════════════════════════════════════════════════════════
// -------------------------------------------------------------------
// buildingType must be a key of BUILDING_TYPES; defaults to 'brick'.
export function calcManagedIndoorTempPass(TaArr, globArr, elevArr, buildingType = 'brick') {
const preset = BUILDING_TYPES[buildingType] || BUILDING_TYPES.brick;
@@ -176,7 +176,7 @@ export function calcManagedIndoorTempPass(TaArr, globArr, elevArr, buildingType
const Ta = TaArr[i] ?? Ti;
const glob = globArr[i] ?? 0;
// Solar gain curtains block curtainBlock fraction
// Solar gain - curtains block curtainBlock fraction
const solarGain = glob * glazingRatio * gValue * orientFactor * (1 - curtainBlock);
const retainedWarmth = Math.max(0, (baseTemp ?? 16) - Ta) * (retainedScale ?? 0.35);
@@ -196,9 +196,9 @@ export function calcManagedIndoorTempPass(TaArr, globArr, elevArr, buildingType
return result;
}
// ═══════════════════════════════════════════════════════════════════
// -------------------------------------------------------------------
// VEHICLE INTERIOR CABIN TEMPERATURE (seated occupant, not in sunbeam)
// ───────────────────────────────────────────────────────────────────
// -------------------------------------------------------------------
// Models the ambient cabin air temperature experienced by an occupant
// seated out of direct sunlight inside a sealed, parked vehicle.
// Two heat sources are combined:
@@ -207,72 +207,72 @@ export function calcManagedIndoorTempPass(TaArr, globArr, elevArr, buildingType
// Aluminium body panels absorb solar radiation and conduct heat
// into the cabin. Albedo ~0.25 (mid-point for typical mixed-colour
// fleet; dark paint ~0.10, silver/white ~0.40).
// Panel surface temp conductive gain into cabin air.
// Panel surface temp - conductive gain into cabin air.
//
// 2. SIDE-WINDOW GLAZING GAIN (sun-angle dependent)
// When solar elevation is between ~10° and ~60°, the sun's rays
// When solar elevation is between ~10- and ~60-, the sun's rays
// cut through the side glass at an angle that allows significant
// transmission into the cabin (rather than hitting the roof or
// reflecting off at a shallow angle). This warms the cabin air
// but the occupant is modelled as NOT sitting in the beam
// but the occupant is modelled as NOT sitting in the beam -
// so it adds to ambient cabin temp, not direct radiant load.
// Above 60° the sun mostly hits the roof; below 10° it reflects.
// Above 60- the sun mostly hits the roof; below 10- it reflects.
//
// Wind is ignored unless ventilation is enabled. No evaporative cooling.
// Cars warm quickly; motorhomes and caravans are treated as insulated living
// spaces with 2535 mm sandwich panels, so panel heat gain is much smaller
// spaces with 25-35 mm sandwich panels, so panel heat gain is much smaller
// and the interior response is slower than a car cabin. Because they are
// occupied living spaces, they also retain warmth from previous hours, people,
// appliances, and background heating; without that, cool sunny days are
// under-estimated badly.
//
// Colour thresholds in the table:
// < 35 °C warm but tolerable for short periods
// 3545 °C dangerous for children/pets (hyperthermia risk)
// > 45 °C potentially fatal within minutes
// ═══════════════════════════════════════════════════════════════════
// < 35 -C - warm but tolerable for short periods
// 35-45 -C - dangerous for children/pets (hyperthermia risk)
// > 45 -C - potentially fatal within minutes
// -------------------------------------------------------------------
export function calcVehicleInteriorTemp(Ta, globalRad, solElev, vehicleType = 'car', ventilated = false) {
if (globalRad == null || Ta == null) return null;
// Look up vehicle preset; fall back to a standard car if key unknown.
const preset = VEHICLE_TYPES[vehicleType] || VEHICLE_TYPES.car;
// ── 1. Panel conduction ──────────────────────────────────────────
const panelAbsorbed = globalRad * (1 - preset.albedo); // W/m² absorbed by bodywork
// -- 1. Panel conduction ------------------------------------------
const panelAbsorbed = globalRad * (1 - preset.albedo); // W/m- absorbed by bodywork
// Panel surface temp: absorbed solar / convective loss to outside air
// hOut ~10 W/m²K (light breeze over panel surface even when parked)
// hOut ~10 W/m-K (light breeze over panel surface even when parked)
const hOut = 10;
const panelSurfaceTemp = Ta + panelAbsorbed / hOut;
// Conductive gain into cabin. Cars are thin metal + trim; motorhomes and
// caravans use insulated sandwich panels, so their bodyU is much lower.
const hCabin = preset.bodyU ?? 4;
const conductionGain = hCabin * (panelSurfaceTemp - Ta); // W/m²
const conductionGain = hCabin * (panelSurfaceTemp - Ta); // W/m-
// ── 2. Side-window glazing gain (angle-dependent) ─────────────────
// -- 2. Side-window glazing gain (angle-dependent) -----------------
// Glazing transmission for auto glass ~0.70; scaled by vehicle glazing area.
const tau = 0.70 * preset.glazingArea;
let glazingGain = 0;
if (solElev != null && solElev > 10 && solElev < 60) {
// Scale factor: peaks around 3040° elevation (sun cuts squarely
// through side glass), tapers off toward 10° (shallow/reflected)
// and 60° (sun increasingly hitting roof not side glass).
// Use a simple tent function peaking at 35°.
// Scale factor: peaks around 30-40- elevation (sun cuts squarely
// through side glass), tapers off toward 10- (shallow/reflected)
// and 60- (sun increasingly hitting roof not side glass).
// Use a simple tent function peaking at 35-.
const peak = 35;
const halfWidth = 25; // degrees either side
const factor = Math.max(0, 1 - Math.abs(solElev - peak) / halfWidth);
// Diffuse radiation also enters through glass regardless of angle
glazingGain = tau * globalRad * factor * 0.5; // occupant not in beam 50% ambient
glazingGain = tau * globalRad * factor * 0.5; // occupant not in beam - 50% ambient
} else {
// Outside the side-window zone: diffuse only (scattered sky light)
glazingGain = tau * (globalRad * 0.15); // ~15% diffuse fraction
}
// ── Combine into cabin air temperature ───────────────────────────
// Total heat input per m² of cabin surface
// -- Combine into cabin air temperature ---------------------------
// Total heat input per m- of cabin surface
const totalGain = conductionGain + glazingGain;
// Cabin heat rejection: an effective blend of leakage, internal air volume,
// and surfaces exchanging heat with the outside. With windows open it is
// roughly 5× higher air moves freely
// roughly 5- higher - air moves freely
// through the cabin, flushing heat out and capping interior temperature much
// closer to ambient. Cabin temp still rises a little due to panel/roof solar gain.
const effectiveHLoss = ventilated ? preset.hCabinLoss * 5 : preset.hCabinLoss;
@@ -289,16 +289,16 @@ export function calcVehicleInteriorTemp(Ta, globalRad, solElev, vehicleType = 'c
const Ti = Ta + solarRise + retainedWarmth + internalGain;
// Clamp: can't be cooler than outside air; physical cap at 90 °C
// Clamp: can't be cooler than outside air; physical cap at 90 -C
return Math.max(Ta, Math.min(Ti, 90));
}
// ── FUTURE FEATURE v2: Pets Profile Fur Temperature & Heat Stress ──────────
// -- FUTURE FEATURE v2: Pets Profile - Fur Temperature & Heat Stress ----------
// Dogs and cats experience heat very differently from humans:
//
// FUR SURFACE TEMPERATURE
// Dark/thick fur absorbs solar radiation and can run significantly hotter
// than air temperature similar in principle to calcConcreteTemp() but
// than air temperature - similar in principle to calcConcreteTemp() but
// with fur-specific albedo values:
// Black fur: albedo ~0.05 (almost all radiation absorbed)
// Brown fur: albedo ~0.15
@@ -306,44 +306,44 @@ export function calcVehicleInteriorTemp(Ta, globalRad, solElev, vehicleType = 'c
// White fur: albedo ~0.40
// Add a calcFurSurfaceTemp(Ta, globalRad, windSpeed, furAlbedo) function
// mirroring calcConcreteTemp but with appropriate convective coefficients
// for fur (lower hc than concrete fur insulates).
// for fur (lower hc than concrete - fur insulates).
//
// PAW BURN RISK
// Paw pads are highly sensitive to surface temperature. Use calcConcreteTemp()
// output directly the existing concrete surface temp is already the right
// output directly - the existing concrete surface temp is already the right
// number. Threshold guidance:
// < 40 °C safe
// 4052 °C discomfort / possible burn (hold-your-hand-for-7-seconds test)
// > 52 °C burns within 60 seconds
// < 40 -C - safe
// 40-52 -C - discomfort / possible burn (hold-your-hand-for-7-seconds test)
// > 52 -C - burns within 60 seconds
// Display as a simple traffic-light column in the Pets profile.
//
// BREED-SPECIFIC HEAT STRESS
// Brachycephalic breeds (Bulldogs, Pugs, French Bulldogs, Persians) have
// severely impaired thermoregulation their safe UTCI ceiling is much lower.
// severely impaired thermoregulation - their safe UTCI ceiling is much lower.
// A breed selector (Normal / Brachycephalic / Senior) applies a risk multiplier
// to the UTCI thresholds, similar to the planned activity modifier.
//
// SUGGESTED COLUMNS FOR PETS PROFILE
// Hour | Air Temp | UTCI+P | Fur Surface Temp | Paw Burn Risk | Shade Advised
//
// ─────────────────────────────────────────────────────────────────────────────
// -----------------------------------------------------------------------------
// ── FUTURE FEATURE: Vehicle-at-speed thermal model ────────────────────────────
// -- FUTURE FEATURE: Vehicle-at-speed thermal model ----------------------------
// Idea: extend calcVehicleInteriorTemp (or add a companion function) to model
// cabin temperature for a vehicle travelling at speed, not just parked.
//
// Key physics differences from the static model:
// Forced convection over the shell scales with vehicle speed (v²), so
// hOut rises significantly shell cools much faster than when parked.
// Above ~30 mph the vehicle's own forward motion dominates airflow, so
// - Forced convection over the shell scales with vehicle speed (v-), so
// hOut rises significantly - shell cools much faster than when parked.
// - Above ~30 mph the vehicle's own forward motion dominates airflow, so
// ambient wind direction becomes largely irrelevant (simplifies the model).
// Speed classes to model: urban (~20 mph), dual carriageway (~50 mph),
// motorway (~70 mph) each with a derived hOut multiplier.
// Windows-open behaviour changes completely at speed: at 70 mph open
// - Speed classes to model: urban (~20 mph), dual carriageway (~50 mph),
// motorway (~70 mph) - each with a derived hOut multiplier.
// - Windows-open behaviour changes completely at speed: at 70 mph open
// windows create high-velocity through-flow, dramatically cutting cabin
// temp vs. the sealed-car case (but much less pleasant than AC!).
// Roof and bonnet solar gain stays the same; side-glass gain is unchanged.
// AC-off vs AC-on would be the primary user toggle alongside speed class.
// - Roof and bonnet solar gain stays the same; side-glass gain is unchanged.
// - AC-off vs AC-on would be the primary user toggle alongside speed class.
//
// Suggested signature:
// calcVehicleInteriorTempAtSpeed(Ta, globalRad, solElev, vehicleType, speedMph, windowsOpen)
@@ -351,42 +351,42 @@ export function calcVehicleInteriorTemp(Ta, globalRad, solElev, vehicleType = 'c
// This would be a useful companion output column (e.g. "Vehicle (moving)")
// for road-trip planning, dog-in-car safety at a rest stop vs. motorway, etc.
//
// ── CYCLIST AT SPEED (companion to the above) ────────────────────────────────
// -- CYCLIST AT SPEED (companion to the above) --------------------------------
// A cyclist generates their own headwind, so the felt temperature (UTCI) is
// very different from a stationary person even without ambient wind.
// very different from a stationary person - even without ambient wind.
// Could share the same speed-class approach as the vehicle model:
// Slow (10 mph) / Moderate (15 mph) / Fast (25 mph)
//
// Key differences from the vehicle model:
// The cyclist IS the exposed person use the UTCI polynomial directly
// - The cyclist IS the exposed person - use the UTCI polynomial directly
// with va = max(ambientWind, cyclingSpeed * conversionFactor)
// No cabin heating effect the cyclist gets wind chill, not solar entrapment
// High metabolic heat generation raises core temp (links to the activity
// modifier planned in compute.js cycling at speed is a combined effect)
// UVA/UVB exposure is unchanged still fully exposed to the sun
// - No cabin heating effect - the cyclist gets wind chill, not solar entrapment
// - High metabolic heat generation raises core temp (links to the activity
// modifier planned in compute.js - cycling at speed is a combined effect)
// - UVA/UVB exposure is unchanged - still fully exposed to the sun
//
// Could be a sub-option within the existing Cycling activity variant rather
// than a separate column e.g. a speed picker in the variant controls.
// ─────────────────────────────────────────────────────────────────────────────
// than a separate column - e.g. a speed picker in the variant controls.
// -----------------------------------------------------------------------------
// ═══════════════════════════════════════════════════════════════════
// VAPOUR PRESSURE Magnus formula.
// ───────────────────────────────────────────────────────────────────
// Converts air temperature (°C) and relative humidity (%) to vapour
// -------------------------------------------------------------------
// VAPOUR PRESSURE - Magnus formula.
// -------------------------------------------------------------------
// Converts air temperature (-C) and relative humidity (%) to vapour
// pressure in hPa. Used as the humidity input to utciApprox().
// ═══════════════════════════════════════════════════════════════════
// -------------------------------------------------------------------
export function vaporPressureHpa(Ta, RH) {
const es = 6.105 * Math.exp((17.27 * Ta) / (237.7 + Ta));
return es * (RH / 100);
}
// ═══════════════════════════════════════════════════════════════════
// SOLAR ELEVATION NOAA simplified algorithm (degrees above horizon).
// ───────────────────────────────────────────────────────────────────
// Accurate to within ~0.01° for most practical purposes. Returns a
// -------------------------------------------------------------------
// SOLAR ELEVATION - NOAA simplified algorithm (degrees above horizon).
// -------------------------------------------------------------------
// Accurate to within ~0.01- for most practical purposes. Returns a
// negative value when the sun is below the horizon (civil twilight
// starts at , nautical at 12°, astronomical at 18°).
// ═══════════════════════════════════════════════════════════════════
// starts at -6-, nautical at -12-, astronomical at -18-).
// -------------------------------------------------------------------
export function solarElevationDeg(lat, lon, dateUTC) {
const start = Date.UTC(dateUTC.getUTCFullYear(), 0, 0);
const diff = dateUTC - start;
@@ -422,21 +422,21 @@ export function solarElevationDeg(lat, lon, dateUTC) {
return (Math.PI / 2 - zenith) * (180 / Math.PI);
}
// ═══════════════════════════════════════════════════════════════════
// -------------------------------------------------------------------
// MEAN RADIANT TEMPERATURE (Tmrt)
// ───────────────────────────────────────────────────────────────────
// -------------------------------------------------------------------
// Tmrt is the uniform temperature of an imaginary enclosure that would
// cause the same net radiation exchange as the actual environment.
// It accounts for:
// Direct solar beam (DNI), scaled by the projected-area factor fp
// Diffuse sky radiation (scattered and cloud-reflected)
// Ground-reflected shortwave (albedo × global radiation)
// Longwave thermal emission from surrounding surfaces ( blackbody at Ta)
// - Direct solar beam (DNI), scaled by the projected-area factor fp
// - Diffuse sky radiation (scattered and cloud-reflected)
// - Ground-reflected shortwave (albedo - global radiation)
// - Longwave thermal emission from surrounding surfaces (- blackbody at Ta)
//
// The fabric index 0.308 (fp formula from ISO 7933) projects the sun
// onto a standing person's silhouette as a function of solar elevation.
// Output feeds directly into utciApprox() as the Tmrt argument.
// ═══════════════════════════════════════════════════════════════════
// -------------------------------------------------------------------
export function calcTmrt(Ta, dirRad, diffRad, globalRad, solElev) {
const TaK = Ta + 273.15;
let fp = 0;
@@ -455,14 +455,14 @@ export function calcTmrt(Ta, dirRad, diffRad, globalRad, solElev) {
return TmrtK - 273.15;
}
// ═══════════════════════════════════════════════════════════════════
// UTCI POLYNOMIAL DO NOT EDIT (or be VERY careful if you do)
// ───────────────────────────────────────────────────────────────────
// This is the official 210-term Bröde et al. (2012) approximation.
// -------------------------------------------------------------------
// UTCI POLYNOMIAL - DO NOT EDIT (or be VERY careful if you do)
// -------------------------------------------------------------------
// This is the official 210-term Br-de et al. (2012) approximation.
// It takes air temp, mean radiant temp, wind, and humidity and returns
// the "felt" temperature. Every number is a peer-reviewed coefficient.
// Scroll past it there's nothing here you'll want to change.
// ═══════════════════════════════════════════════════════════════════
// Scroll past it - there's nothing here you'll want to change.
// -------------------------------------------------------------------
export function utciApprox(Ta, Tmrt, va10, ehPa) {
const va = Math.max(0.5, Math.min(17, va10));
const D_Tmrt = Tmrt - Ta;