Beta 9
More 'Home' Physics More FAQs More Text
This commit is contained in:
+32
-46
@@ -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) {
|
||||
// 35–45 °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));
|
||||
|
||||
Reference in New Issue
Block a user