More 'Home' Physics
More FAQs
More Text
This commit is contained in:
fraxle
2026-05-15 10:39:09 +01:00
parent 5b0e62d1b5
commit 01d939933a
7 changed files with 605 additions and 176 deletions
+32 -46
View File
@@ -7,13 +7,15 @@
// solarElevationDeg(lat, lon, dateUTC) NOAA simplified solar position
// calcTmrt(Ta, dirRad, diffRad, globalRad, solElev) Mean radiant temp
// utciApprox(Ta, Tmrt, va10, ehPa) Bröde et al. 2012 polynomial
// calcVehicleInteriorTemp(Ta, globalRad, solElev, vehicleType)
// calcVehicleInteriorTemp(Ta, globalRad, solElev, vehicleType, ventilated)
// calcIndoorTempPass(TaArr, globArr, elevArr, buildingType)
// calcManagedIndoorTempPass(TaArr, globArr, elevArr, buildingType)
//
// Nothing in here should need editing unless the underlying science
// changes. All numbers are peer-reviewed constants or coefficients.
// ════════════════════════════════════════════════════════════════════════
import { VEHICLE_TYPES } from './utils.js';
import { VEHICLE_TYPES, BUILDING_TYPES } from './utils.js';
// ═══════════════════════════════════════════════════════════════════
// PHYSICAL CONSTANTS
@@ -120,48 +122,37 @@ function _houseHeatLoad(Ta, globalRad, solElev) {
// Two-pass function: call with the full arrays of hourly Ta and globalRad.
// Returns an array of indoor temperatures, one per hour.
export function calcIndoorTempPass(TaArr, globArr, elevArr) {
// buildingType must be a key of BUILDING_TYPES; defaults to 'brick'.
export function calcIndoorTempPass(TaArr, globArr, elevArr, buildingType = 'brick') {
const preset = BUILDING_TYPES[buildingType] || BUILDING_TYPES.brick;
const { uWall, lagHours, glazingRatio, gValue, orientFactor } = preset;
const n = TaArr.length;
const result = new Array(n);
// Thermal resistance of the building envelope (°C per W/m² of heat load)
// Lower = faster response to outdoor changes. UK brick mid-stock ~0.45.
const uWall = 0.35; // W/m²K effective wall U-value
// Thermal mass time constant: heavier = longer lag.
// ~4 h lag for typical UK brick semi (expressed as exponential decay weight).
const lagHours = 4;
const alpha = 1 - Math.exp(-1 / lagHours); // per-hour blending weight
const alpha = 1 - Math.exp(-1 / lagHours); // per-hour blending weight
// Seed indoor temp to first outdoor temp
let Ti = TaArr[0] ?? 15;
for (let i = 0; i < n; i++) {
const Ta = TaArr[i] ?? Ti;
const glob = globArr[i] ?? 0;
const solElev = elevArr[i] ?? 0;
const Ta = TaArr[i] ?? Ti;
const glob = globArr[i] ?? 0;
// Solar gain through windows (W/m² effective)
const glazingRatio = 0.16;
const gValue = 0.63;
const orientFactor = 0.50;
const solarGain = glob * glazingRatio * gValue * orientFactor;
const solarGain = glob * glazingRatio * gValue * orientFactor;
// Conductive heat flow through walls: proportional to (Ta - Ti)
// uWall drives how quickly the indoor temp chases outdoor temp.
const conductionGain = uWall * (Ta - Ti);
// Target indoor temp this hour if there were no thermal mass:
// Ti_instant = Ti + conduction + solar load / heat capacity proxy
// heatCap proxy: how many degrees does 1 W/m² raise the indoor air?
// For a typical 90 m² house, ~0.15 °C per W/m² effective.
// Heat capacity proxy: how many °C does 1 W/m² raise the indoor air?
// Typical 90 m² house ≈ 0.15; conservatory much lower (less thermal mass).
const heatCapProxy = 0.15;
const Ti_instant = Ti + (conductionGain + solarGain) * heatCapProxy;
// Apply thermal lag: blend toward Ti_instant slowly
// Apply thermal lag: blend toward Ti_instant at the building's time constant
Ti = Ti + alpha * (Ti_instant - Ti);
// Can't be colder than outdoor (house doesn't actively cool)
// Cap at 55 °C (physically implausible above this for a house interior)
result[i] = Math.max(Math.min(Ti, 55), Math.min(Ta, Ti));
}
@@ -188,20 +179,14 @@ export function calcIndoorTempPass(TaArr, globArr, elevArr) {
//
// Same thermal lag model as calcIndoorTempPass (4 h brick time constant).
// ═══════════════════════════════════════════════════════════════════
export function calcManagedIndoorTempPass(TaArr, globArr, elevArr) {
// 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;
const { uWall, lagHours, glazingRatio, gValue, orientFactor, curtainBlock, ventAlpha } = preset;
const n = TaArr.length;
const result = new Array(n);
const uWall = 0.35;
const lagHours = 4;
const alpha = 1 - Math.exp(-1 / lagHours);
// Curtain factor: blocks 80% of solar gain
const curtainBlock = 0.80;
// Ventilation blending weight per hour when windows are open
// ~2 ACH for a well-ventilated house → meaningful but not instant pull
const ventAlpha = 0.25;
const alpha = 1 - Math.exp(-1 / lagHours);
let Ti = TaArr[0] ?? 15;
@@ -209,13 +194,10 @@ export function calcManagedIndoorTempPass(TaArr, globArr, elevArr) {
const Ta = TaArr[i] ?? Ti;
const glob = globArr[i] ?? 0;
// Solar gain — curtains block 80%
const glazingRatio = 0.16;
const gValue = 0.63;
const orientFactor = 0.50;
const solarGain = glob * glazingRatio * gValue * orientFactor * (1 - curtainBlock);
// Solar gain — curtains block curtainBlock fraction
const solarGain = glob * glazingRatio * gValue * orientFactor * (1 - curtainBlock);
// Wall conduction (unchanged)
// Wall conduction (unchanged from unmanaged model)
const conductionGain = uWall * (Ta - Ti);
const heatCapProxy = 0.15;
@@ -265,7 +247,7 @@ export function calcManagedIndoorTempPass(TaArr, globArr, elevArr) {
// 3545 °C — dangerous for children/pets (hyperthermia risk)
// > 45 °C — potentially fatal within minutes
// ═══════════════════════════════════════════════════════════════════
export function calcVehicleInteriorTemp(Ta, globalRad, solElev, vehicleType = 'car') {
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.
@@ -303,8 +285,12 @@ export function calcVehicleInteriorTemp(Ta, globalRad, solElev, vehicleType = 'c
// ── Combine into cabin air temperature ───────────────────────────
// Total heat input per m² of cabin surface
const totalGain = conductionGain + glazingGain;
// Cabin heat loss: varies by vehicle type (motorhomes insulated, cars not)
const Ti = Ta + totalGain / preset.hCabinLoss;
// Cabin heat loss: varies by vehicle type (motorhomes insulated, cars not).
// With windows open, convective loss is 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;
const Ti = Ta + totalGain / effectiveHLoss;
// Clamp: can't be cooler than outside air; physical cap at 90 °C
return Math.max(Ta, Math.min(Ti, 90));