Beta 10
New thermal charts
This commit is contained in:
+19
-25
@@ -86,10 +86,11 @@ export function calcConcreteTemp(Ta, globalRad, windSpeed) {
|
||||
//
|
||||
// THERMAL LAG
|
||||
// Brick and concrete have high thermal mass — the house responds
|
||||
// slowly to outdoor temperature swings. This function takes a
|
||||
// weighted average of the current hour's heat load and the
|
||||
// previous few hours', giving the characteristic lag where indoor
|
||||
// temperature peaks 2–4 hours after the outdoor peak.
|
||||
// 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.
|
||||
//
|
||||
@@ -125,14 +126,14 @@ function _houseHeatLoad(Ta, globalRad, solElev) {
|
||||
// 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 { lagHours, glazingRatio, gValue, orientFactor, solarScale, baseTemp, internalGain, retainedScale } = preset;
|
||||
|
||||
const n = TaArr.length;
|
||||
const result = new Array(n);
|
||||
const alpha = 1 - Math.exp(-1 / lagHours); // per-hour blending weight
|
||||
|
||||
// Seed indoor temp to first outdoor temp
|
||||
let Ti = TaArr[0] ?? 15;
|
||||
// Seed to a plausible occupied indoor baseline rather than outdoor air.
|
||||
let Ti = Math.max(TaArr[0] ?? 15, baseTemp ?? 16);
|
||||
|
||||
for (let i = 0; i < n; i++) {
|
||||
const Ta = TaArr[i] ?? Ti;
|
||||
@@ -141,16 +142,12 @@ export function calcIndoorTempPass(TaArr, globArr, elevArr, buildingType = 'bric
|
||||
// Solar gain through windows (W/m² effective)
|
||||
const solarGain = glob * glazingRatio * gValue * orientFactor;
|
||||
|
||||
// Conductive heat flow through walls: proportional to (Ta - Ti)
|
||||
const conductionGain = uWall * (Ta - Ti);
|
||||
const retainedWarmth = Math.max(0, (baseTemp ?? 16) - Ta) * (retainedScale ?? 0.35);
|
||||
const target = Ta + solarGain * (solarScale ?? 0.1) + retainedWarmth + (internalGain ?? 0.7);
|
||||
|
||||
// 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 at the building's time constant
|
||||
Ti = Ti + alpha * (Ti_instant - Ti);
|
||||
// Apply thermal lag: blend toward the hour's target instead of adding
|
||||
// solar gain repeatedly onto the previous indoor temperature.
|
||||
Ti = Ti + alpha * (target - Ti);
|
||||
|
||||
// Can't be colder than outdoor (house doesn't actively cool)
|
||||
result[i] = Math.max(Math.min(Ti, 55), Math.min(Ta, Ti));
|
||||
@@ -182,13 +179,13 @@ export function calcIndoorTempPass(TaArr, globArr, elevArr, buildingType = 'bric
|
||||
// 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 { lagHours, glazingRatio, gValue, orientFactor, curtainBlock, ventAlpha, solarScale, baseTemp, internalGain, retainedScale } = preset;
|
||||
|
||||
const n = TaArr.length;
|
||||
const result = new Array(n);
|
||||
const alpha = 1 - Math.exp(-1 / lagHours);
|
||||
|
||||
let Ti = TaArr[0] ?? 15;
|
||||
let Ti = Math.max(TaArr[0] ?? 15, baseTemp ?? 16);
|
||||
|
||||
for (let i = 0; i < n; i++) {
|
||||
const Ta = TaArr[i] ?? Ti;
|
||||
@@ -197,14 +194,11 @@ export function calcManagedIndoorTempPass(TaArr, globArr, elevArr, buildingType
|
||||
// Solar gain — curtains block curtainBlock fraction
|
||||
const solarGain = glob * glazingRatio * gValue * orientFactor * (1 - curtainBlock);
|
||||
|
||||
// Wall conduction (unchanged from unmanaged model)
|
||||
const conductionGain = uWall * (Ta - Ti);
|
||||
const retainedWarmth = Math.max(0, (baseTemp ?? 16) - Ta) * (retainedScale ?? 0.35);
|
||||
const target = Ta + solarGain * (solarScale ?? 0.1) + retainedWarmth + (internalGain ?? 0.7);
|
||||
|
||||
const heatCapProxy = 0.15;
|
||||
const Ti_instant = Ti + (conductionGain + solarGain) * heatCapProxy;
|
||||
|
||||
// Apply thermal lag
|
||||
Ti = Ti + alpha * (Ti_instant - Ti);
|
||||
// Apply thermal lag toward the managed target.
|
||||
Ti = Ti + alpha * (target - Ti);
|
||||
|
||||
// Smart ventilation: only open windows when outside is cooler
|
||||
if (Ta < Ti) {
|
||||
|
||||
Reference in New Issue
Block a user