Correct 'Shade' temps for night
This commit is contained in:
fraxle
2026-08-02 17:38:59 +01:00
parent ff86b6d5ca
commit ffb5ed1d7d
8 changed files with 91 additions and 30 deletions
+49 -2
View File
@@ -14,6 +14,7 @@
// calcVehicleInteriorTemp(Ta, globalRad, solElev, vehicleType, ventilated, speedMph, windMps) equilibrium cabin temp
// calcVehicleInteriorTempPass(TaArr, globArr, elevArr, vaArr, vehicleType, ventilated, speedMph) lagged cabin temp
// calcShadeAirTemp(TaEnv, effRadEnv, va, elev, shade) per-env shade air temp
// calcShadeFeltTemp(TaShade, difEnv, va, eh, elev, shade) felt temp in that shade
// vaporPressureHpa(Ta, RH) Magnus formula - hPa
// solarElevationDeg(lat, lon, dateUTC) NOAA simplified solar position (-)
// calcTmrt(Ta, dirRad, diffRad, globalRad, solElev) Mean radiant temperature
@@ -601,9 +602,55 @@ export function calcShadeAirTemp(Ta, effectiveRad, va, elev, shade) {
const daytime = (elev != null && elev > 0) ? 1 : 0;
const sunTerm = SHADE_K_SUN * (shade.sun ?? 0) * rad * daytime;
const windTerm = SHADE_K_WIND * (1 - (shade.shelter ?? 0)) * Math.max(0, va || 0);
const adj = sunTerm - windTerm;
// Wind can only cancel the sun-trap warming, never push the pocket BELOW
// ambient: mixing drags trapped air back toward the open-air reading, it
// does not manufacture cold. Without the floor a breezy night read a
// degree under Air for no physical reason (and Pet Shade with it).
// Genuinely cool environments still sit below Air via env.taOffset.
const adj = Math.max(0, sunTerm - windTerm);
// Local sun-trap nudge only - the env air-temp shift is already in Ta.
return Ta + Math.max(-4, Math.min(5, adj));
return Ta + Math.min(5, adj);
}
// -------------------------------------------------------------------
// SHADE FELT TEMPERATURE - "SunSoak, but out of the sun"
// -------------------------------------------------------------------
// calcShadeAirTemp() above answers "what would a thermometer in the shade
// read". That is NOT what someone standing in the shade feels: they are
// still outdoors, so wind still strips heat off them and humidity still
// blunts their sweating, exactly as in the open. Reading a shade AIR temp
// beside SunSoak's FELT temp made Shade look hotter than full sun on a
// breezy night - both numbers were right, but they were different
// quantities sitting in adjacent columns.
//
// So Shade runs the same UTCI machinery as SunSoak, changing one input:
// the radiant load. Out of the beam a person sees no direct sun, only the
// part of the sky their shade does not block, so:
//
// direct -> 0 (that is what shade IS)
// diffuse -> difEnv * skyView
// global -> the same diffuse (no beam reaching the ground to reflect)
//
// skyView derives from shade.shelter, reusing it as an enclosure proxy: a
// forest canopy or building canyon that stops the wind also hides most of
// the sky dome, while an umbrella on open sand blocks little besides the
// sun itself. It is a proxy, not a measurement - like the rest of the
// shade block, this is an estimate of a typical spot in that environment.
//
// Consequences worth knowing: at night there is no beam to remove, so
// Shade converges on SunSoak (they differ only by the sun-trap nudge in
// the air temp). In daytime sun Shade sits several degrees under SunSoak,
// which is the whole point of standing in it. On a windy hour BOTH fall
// together, because both now carry the same wind term.
// -------------------------------------------------------------------
export const SHADE_SKY_BLOCK = 0.6; // fraction of the sky an enclosed shade hides, per unit shelter
export function calcShadeFeltTemp(TaShade, difRad, va, eh, elev, shade) {
if (TaShade == null) return null;
const skyView = 1 - SHADE_SKY_BLOCK * (shade?.shelter ?? 0);
const difShade = Math.max(0, (difRad || 0) * skyView);
const Tmrt = calcTmrt(TaShade, 0, difShade, difShade, elev);
return utciApprox(TaShade, Tmrt, va, eh);
}
// -- Pets Profile - Fur Surface Temperature -----------------------------------