Calculation updates
This commit is contained in:
+66
-37
@@ -113,7 +113,7 @@ export function UTCIForecast() {
|
||||
// FLIP THE `false` BELOW TO `true` TO PREVIEW THE PRO EXPERIENCE.
|
||||
// When this is wired to real billing/auth, replace `useState(false)`
|
||||
// with a check against the logged-in user.
|
||||
const [isPro, setIsPro] = useState(false);
|
||||
const [isPro, setIsPro] = useState(true);
|
||||
|
||||
// How many days the free tier shows. Days beyond this get a 🔒.
|
||||
// Bump this number if you want to give free users more access.
|
||||
@@ -127,57 +127,56 @@ export function UTCIForecast() {
|
||||
icon: '🌡️',
|
||||
cols: { hour: true, air: true, rh: false, dew: false, wind: true, dir: false, cloud: true, sun: false, direct: false, diffuse: false, tmrt: false, delta: false, utci: false, uvA: false, uvB: false, burn: false, utciP: true, precip: true, soilT: false, soilT6: false, soilM: false, concreteT: false, vehicleT: false, indoorT: false, managedT: false },
|
||||
},
|
||||
outdoors: {
|
||||
label: 'Places',
|
||||
icon: '🌤️',
|
||||
// Default cols match the first variant (urban). Switching variant updates visibleCols.
|
||||
cols: { hour: true, air: true, rh: false, dew: false, wind: true, dir: true, cloud: true, sun: false, direct: false, diffuse: false, tmrt: false, delta: false, utci: false, uvA: true, uvB: false, burn: true, utciP: true, precip: true, soilT: false, soilT6: false, soilM: false, concreteT: true, vehicleT: false, indoorT: false, managedT: false },
|
||||
},
|
||||
home: {
|
||||
label: 'Home',
|
||||
icon: '🏠',
|
||||
cols: { hour: true, air: true, rh: true, dew: true, wind: true, dir: false, cloud: true, sun: false, direct: false, diffuse: false, tmrt: false, delta: false, utci: false, uvA: false, uvB: false, burn: false, utciP: false, precip: true, soilT: false, soilT6: false, soilM: false, concreteT: false, vehicleT: false, indoorT: true, managedT: true },
|
||||
},
|
||||
urban: {
|
||||
label: 'Urban',
|
||||
icon: '🏙️',
|
||||
cols: { hour: true, air: true, rh: false, dew: false, wind: true, dir: true, cloud: true, sun: false, direct: false, diffuse: false, tmrt: false, delta: false, utci: false, uvA: true, uvB: false, burn: true, utciP: true, precip: true, soilT: false, soilT6: false, soilM: false, concreteT: true, vehicleT: false, indoorT: false, managedT: false },
|
||||
},
|
||||
vehicle: {
|
||||
label: 'Vehicle',
|
||||
icon: '🚗',
|
||||
cols: { hour: true, air: true, rh: false, dew: false, wind: true, dir: false, cloud: true, sun: false, direct: false, diffuse: false, tmrt: false, delta: false, utci: false, uvA: false, uvB: false, burn: false, utciP: true, precip: true, soilT: false, soilT6: false, soilM: true, concreteT: false, vehicleT: true, indoorT: false, managedT: false },
|
||||
},
|
||||
sailing: {
|
||||
label: 'Sailing',
|
||||
icon: '⛵',
|
||||
cols: { hour: true, air: true, rh: false, dew: true, wind: true, dir: true, cloud: true, sun: true, direct: false, diffuse: false, tmrt: false, delta: false, utci: false, uvA: true, uvB: false, burn: true, utciP: true, precip: true, soilT: false, soilT6: false, soilM: false, concreteT: false, vehicleT: false, indoorT: false, managedT: false },
|
||||
},
|
||||
farming: {
|
||||
label: 'Farming',
|
||||
icon: '🌾',
|
||||
cols: { hour: true, air: true, rh: true, dew: true, wind: true, dir: true, cloud: true, sun: true, direct: false, diffuse: false, tmrt: false, delta: false, utci: true, uvA: false, uvB: false, burn: false, utciP: false, precip: true, soilT: true, soilT6: true, soilM: true, concreteT: false, vehicleT: false, indoorT: false, managedT: false },
|
||||
},
|
||||
outdoors: {
|
||||
label: 'Outdoors',
|
||||
icon: '🌤️',
|
||||
// Default cols match the first variant (beach). Switching variant updates visibleCols.
|
||||
sailing: {
|
||||
label: 'Sailing',
|
||||
icon: '⛵',
|
||||
proOnly: true,
|
||||
cols: { hour: true, air: true, rh: false, dew: true, wind: true, dir: true, cloud: true, sun: true, direct: false, diffuse: false, tmrt: false, delta: false, utci: false, uvA: true, uvB: false, burn: true, utciP: true, precip: true, soilT: false, soilT6: false, soilM: false, concreteT: false, vehicleT: false, indoorT: false, managedT: false },
|
||||
},
|
||||
alltemps: {
|
||||
label: 'All Temps',
|
||||
label: 'Temps',
|
||||
icon: '🌡️',
|
||||
proOnly: true,
|
||||
cols: { hour: true, air: true, rh: false, dew: false, wind: false, dir: false, cloud: false, sun: false, direct: false, diffuse: false, tmrt: false, delta: false, utci: false, uvA: false, uvB: false, burn: false, utciP: true, precip: false, soilT: true, soilT6: false, soilM: false, concreteT: true, vehicleT: true, indoorT: true, managedT: true },
|
||||
},
|
||||
custom: {
|
||||
label: 'Custom',
|
||||
icon: '⚙️',
|
||||
proOnly: true,
|
||||
cols: { hour: true, air: true, rh: false, dew: false, wind: true, dir: false, cloud: false, sun: false, direct: false, diffuse: false, tmrt: false, delta: false, utci: false, uvA: false, uvB: false, burn: false, utciP: true, precip: true, soilT: false, soilT6: false, soilM: false, concreteT: false, vehicleT: false, indoorT: false, managedT: false },
|
||||
},
|
||||
};
|
||||
|
||||
// Outdoors sub-variants — each has its own column set.
|
||||
// Places sub-variants — each has its own column set.
|
||||
// Selecting a variant applies its cols to visibleCols.
|
||||
const OUTDOORS_VARIANTS = {
|
||||
urban: { name: 'Urban', cols: { hour: true, air: true, rh: false, dew: false, wind: true, dir: true, cloud: true, sun: false, direct: false, diffuse: false, tmrt: false, delta: false, utci: false, uvA: true, uvB: false, burn: true, utciP: true, precip: true, soilT: false, soilT6: false, soilM: false, concreteT: true, vehicleT: false, indoorT: false, managedT: false } },
|
||||
beach: { name: 'Beach', cols: { hour: true, air: true, rh: false, dew: true, wind: true, dir: true, cloud: true, sun: true, direct: false, diffuse: false, tmrt: false, delta: false, utci: false, uvA: true, uvB: true, burn: true, utciP: true, precip: true, soilT: false, soilT6: false, soilM: false, concreteT: false, vehicleT: false, indoorT: false, managedT: false } },
|
||||
festival: { name: 'Festival', cols: { hour: true, air: true, rh: false, dew: true, wind: true, dir: true, cloud: true, sun: false, direct: false, diffuse: false, tmrt: false, delta: false, utci: false, uvA: true, uvB: false, burn: true, utciP: true, precip: true, soilT: false, soilT6: false, soilM: true, concreteT: false, vehicleT: false, indoorT: false, managedT: false } },
|
||||
events: { name: 'Events', cols: { hour: true, air: true, rh: false, dew: false, wind: true, dir: true, cloud: true, sun: false, direct: false, diffuse: false, tmrt: false, delta: false, utci: false, uvA: true, uvB: false, burn: true, utciP: true, precip: true, soilT: false, soilT6: false, soilM: false, concreteT: true, vehicleT: false, indoorT: false, managedT: false } },
|
||||
naturist: { name: 'Naturist', cols: { hour: true, air: true, rh: false, dew: true, wind: true, dir: false, cloud: true, sun: true, direct: false, diffuse: false, tmrt: false, delta: false, utci: true, uvA: true, uvB: true, burn: true, utciP: true, precip: true, soilT: false, soilT6: false, soilM: false, concreteT: false, vehicleT: false, indoorT: false, managedT: false } },
|
||||
wintersports: { name: 'Winter Sports', cols: { hour: true, air: true, rh: false, dew: false, wind: true, dir: true, cloud: true, sun: true, direct: false, diffuse: false, tmrt: false, delta: false, utci: false, uvA: true, uvB: true, burn: true, utciP: true, precip: true, soilT: false, soilT6: false, soilM: false, concreteT: false, vehicleT: false, indoorT: false, managedT: false } },
|
||||
festival: { name: 'Festival', proOnly: true, cols: { hour: true, air: true, rh: false, dew: true, wind: true, dir: true, cloud: true, sun: false, direct: false, diffuse: false, tmrt: false, delta: false, utci: false, uvA: true, uvB: false, burn: true, utciP: true, precip: true, soilT: false, soilT6: false, soilM: true, concreteT: false, vehicleT: false, indoorT: false, managedT: false } },
|
||||
wintersports: { name: 'Winter Sports', proOnly: true, cols: { hour: true, air: true, rh: false, dew: false, wind: true, dir: true, cloud: true, sun: true, direct: false, diffuse: false, tmrt: false, delta: false, utci: false, uvA: true, uvB: true, burn: true, utciP: true, precip: true, soilT: false, soilT6: false, soilM: false, concreteT: false, vehicleT: false, indoorT: false, managedT: false } },
|
||||
naturist: { name: 'Naturist', proOnly: true, cols: { hour: true, air: true, rh: false, dew: true, wind: true, dir: false, cloud: true, sun: true, direct: false, diffuse: false, tmrt: false, delta: false, utci: true, uvA: true, uvB: true, burn: true, utciP: true, precip: true, soilT: false, soilT6: false, soilM: false, concreteT: false, vehicleT: false, indoorT: false, managedT: false } },
|
||||
};
|
||||
|
||||
// Current active filter profile
|
||||
@@ -195,8 +194,8 @@ export function UTCIForecast() {
|
||||
// Vehicle type for the cabin heat column. 'car' is the default preset.
|
||||
const [vehicleType, setVehicleType] = useState('car');
|
||||
|
||||
// Outdoors profile sub-variant (beach / festival / events / naturist)
|
||||
const [outdoorsVariant, setOutdoorsVariant] = useState('beach');
|
||||
// Places profile sub-variant (urban / beach / events / festival / wintersports / naturist)
|
||||
const [outdoorsVariant, setOutdoorsVariant] = useState('urban');
|
||||
|
||||
// Vehicle ventilation — true = windows open (high convective loss).
|
||||
const [vehicleVent, setVehicleVent] = useState(false);
|
||||
@@ -250,7 +249,7 @@ export function UTCIForecast() {
|
||||
soilT6: { title: 'Soil Temp 6 cm', desc: 'Temperature of the soil at 6 cm depth, the root zone for many crops and seedlings. Lags behind surface temperature by several hours.' },
|
||||
soilM: { title: 'Soil Moisture', desc: 'Volumetric water content of the top 1 cm of soil (m³/m³). Values above 0.4 suggest saturated ground; below 0.2 indicates dry conditions.' },
|
||||
concreteT: { title: 'Concrete Surface', desc: 'Estimated temperature of sun-exposed urban concrete or paving. Concrete absorbs more solar energy than grass and cannot cool itself through evaporation — surface temps can run 15–25 °C above air temperature on sunny days.' },
|
||||
vehicleT: { title: 'Vehicle Interior', desc: 'Estimated peak ambient cabin temperature inside a sealed, parked vehicle. Adjusted for your selected vehicle type — albedo, glazing area, and insulation all affect the result. Dangerous for children and pets above 35 °C; potentially fatal above 45 °C.' },
|
||||
vehicleT: { title: 'Vehicle Interior', desc: 'Estimated ambient cabin temperature inside a parked vehicle. Cars heat rapidly through thin body panels and glass; motorhomes and caravans are modelled as insulated occupied living spaces with retained warmth, lower glass gain, and slower heat response. Dangerous for children and pets above 35 °C; potentially fatal above 45 °C.' },
|
||||
indoorT: { title: 'Indoors', desc: 'Estimated ambient temperature inside a typical UK brick house with windows closed and no air conditioning. Accounts for wall conduction, window solar gain, and thermal mass — indoor temperatures typically peak 2–4 hours after the outdoor peak.' },
|
||||
managedT: { title: 'Managed Indoors', desc: 'Estimated indoor temperature with curtains closed and windows opened when outdoor air is cooler than inside — the standard UK heatwave advice. Curtains block most direct solar gain; smart ventilation pulls the temperature down during cooler periods.' },
|
||||
};
|
||||
@@ -839,6 +838,36 @@ export function UTCIForecast() {
|
||||
const promptDate = new Date(days[proPromptDay].key + 'T00:00Z');
|
||||
const dayName = promptDate.toLocaleDateString('en-GB', { weekday: 'long', timeZone: 'UTC' });
|
||||
const dayLong = promptDate.toLocaleDateString('en-GB', { weekday: 'long', day: 'numeric', month: 'short', timeZone: 'UTC' });
|
||||
const extraPromptCopy = {
|
||||
'profile:sailing': {
|
||||
title: 'Sailing is part of SunScope Extra',
|
||||
detail: 'Extra adds specialist planning views for higher-commitment trips, including wind, exposure, UV, and wet-weather comfort for water conditions.',
|
||||
},
|
||||
'profile:alltemps': {
|
||||
title: 'Temps is part of SunScope Extra',
|
||||
detail: 'Extra unlocks the comparison view for air, soil, concrete, vehicle, and indoor temperatures in one place.',
|
||||
},
|
||||
'profile:custom': {
|
||||
title: 'Custom columns are part of SunScope Extra',
|
||||
detail: 'Extra lets you choose exactly which columns appear: mix and match Air, Dew, Soil, UV, UTCI and more to build your perfect view.',
|
||||
},
|
||||
'variant:festival': {
|
||||
title: 'Festival planning is part of SunScope Extra',
|
||||
detail: 'Extra adds multi-day comfort, ground condition, exposure, and rain planning for higher-stakes outdoor trips.',
|
||||
},
|
||||
'variant:wintersports': {
|
||||
title: 'Winter Sports is part of SunScope Extra',
|
||||
detail: 'Extra adds specialist exposure planning for snow, glare, wind, UV reflection, and cold-weather comfort.',
|
||||
},
|
||||
'variant:naturist': {
|
||||
title: 'Naturist is part of SunScope Extra',
|
||||
detail: 'Extra adds specialist skin-exposure planning with UV, wind, humidity, precipitation, and felt-temperature detail.',
|
||||
},
|
||||
};
|
||||
const promptCopy = extraPromptCopy[proPromptSource] || {
|
||||
title: `${dayName}'s forecast is part of SunScope Extra`,
|
||||
detail: 'Extra unlocks the full 14-day forecast, customisable columns, specialist profiles, and an ad-free view.',
|
||||
};
|
||||
return html`
|
||||
<div style=${{
|
||||
margin: '12px 0',
|
||||
@@ -863,9 +892,7 @@ export function UTCIForecast() {
|
||||
marginBottom: '6px',
|
||||
lineHeight: 1.25,
|
||||
}}>
|
||||
🔒 ${proPromptSource === 'custom'
|
||||
? 'Custom columns are part of SunScope Extra'
|
||||
: `${dayName}'s forecast is part of SunScope Extra`}
|
||||
🔒 ${promptCopy.title}
|
||||
</div>
|
||||
<div style=${{
|
||||
fontFamily: 'Manrope, sans-serif',
|
||||
@@ -873,9 +900,7 @@ export function UTCIForecast() {
|
||||
color: '#4a3420',
|
||||
lineHeight: 1.65,
|
||||
}}>
|
||||
${proPromptSource === 'custom'
|
||||
? 'Pro lets you choose exactly which columns appear — mix and match Air, Dew, Soil, UV, UTCI and more to build your perfect view.'
|
||||
: 'Pro unlocks the full 14-day forecast, customisable columns, and an ad-free view.'}
|
||||
${promptCopy.detail}
|
||||
</div>
|
||||
<div style=${{
|
||||
fontFamily: 'JetBrains Mono, monospace',
|
||||
@@ -962,31 +987,30 @@ export function UTCIForecast() {
|
||||
|
||||
<!--
|
||||
FILTER PROFILE SELECTOR — presets shown to all users.
|
||||
Custom is Pro-only: free users see it locked with a 🔒
|
||||
and clicking it triggers the same upsell prompt as locked days.
|
||||
Extra profiles are shown in place with a gentle 🔒 and clicking
|
||||
them triggers the same upsell prompt as locked days.
|
||||
The bottom border is removed only when the col-toggles bar
|
||||
follows (Pro users), so the two bars merge into one panel.
|
||||
-->
|
||||
<div class="filter-profiles" style=${{ borderBottom: isPro ? 'none' : '' }}>
|
||||
<span class="filter-profiles-label">Profile:</span>
|
||||
${Object.entries(FILTER_PROFILES).map(([key, profile]) => {
|
||||
const isCustom = key === 'custom';
|
||||
const locked = isCustom && !isPro;
|
||||
const locked = profile.proOnly && !isPro;
|
||||
return html`
|
||||
<button
|
||||
key=${key}
|
||||
class=${`profile-btn${activeProfile === key ? ' active' : ''}${locked ? ' locked' : ''}`}
|
||||
style=${{ opacity: locked ? 0.6 : 1, cursor: locked ? 'not-allowed' : 'pointer', position: 'relative' }}
|
||||
title=${locked ? '🔒 Custom columns are part of SunScope Extra' : ''}
|
||||
title=${locked ? `🔒 ${profile.label} is part of SunScope Extra` : ''}
|
||||
onClick=${() => {
|
||||
if (locked) {
|
||||
setProPromptSource('custom');
|
||||
setProPromptSource(`profile:${key}`);
|
||||
setProPromptDay(0);
|
||||
return;
|
||||
}
|
||||
setActiveProfile(key);
|
||||
if (key === 'outdoors') {
|
||||
// Apply the current variant's cols (default: beach on first switch)
|
||||
// Apply the current variant's cols (default: urban on first switch)
|
||||
setVisibleCols({ ...OUTDOORS_VARIANTS[outdoorsVariant].cols });
|
||||
} else if (key !== 'custom') {
|
||||
setVisibleCols({ ...profile.cols });
|
||||
@@ -1020,11 +1044,16 @@ export function UTCIForecast() {
|
||||
value=${outdoorsVariant}
|
||||
onChange=${(e) => {
|
||||
const v = e.target.value;
|
||||
if (OUTDOORS_VARIANTS[v]?.proOnly && !isPro) {
|
||||
setProPromptSource(`variant:${v}`);
|
||||
setProPromptDay(0);
|
||||
return;
|
||||
}
|
||||
setOutdoorsVariant(v);
|
||||
setVisibleCols({ ...OUTDOORS_VARIANTS[v].cols });
|
||||
}}>
|
||||
${Object.entries(OUTDOORS_VARIANTS).map(([k, v]) => html`
|
||||
<option key=${k} value=${k}>${v.name}</option>`)}
|
||||
<option key=${k} value=${k}>${v.name}${v.proOnly && !isPro ? ' 🔒' : ''}</option>`)}
|
||||
</select>`}
|
||||
|
||||
${isPro && (activeProfile === 'custom' || FILTER_PROFILES[activeProfile].cols['air']) && html`<button class=${`col-toggle${visibleCols.air ? ' on' : ''}`} onClick=${() => toggleCol('air')}>Air</button>`}
|
||||
|
||||
+25
-7
@@ -239,8 +239,13 @@ export function calcManagedIndoorTempPass(TaArr, globArr, elevArr, buildingType
|
||||
// so it adds to ambient cabin temp, not direct radiant load.
|
||||
// Above 60° the sun mostly hits the roof; below 10° it reflects.
|
||||
//
|
||||
// Wind is ignored (vehicle is sealed). No evaporative cooling.
|
||||
// Steady-state reached after ~45–60 min of parking.
|
||||
// Wind is ignored unless ventilation is enabled. No evaporative cooling.
|
||||
// Cars warm quickly; motorhomes and caravans are treated as insulated living
|
||||
// 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
|
||||
@@ -259,8 +264,9 @@ export function calcVehicleInteriorTemp(Ta, globalRad, solElev, vehicleType = 'c
|
||||
// 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: panel-to-cabin air, ~4 W/m²K through metal + trim
|
||||
const hCabin = 4;
|
||||
// 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²
|
||||
|
||||
// ── 2. Side-window glazing gain (angle-dependent) ─────────────────
|
||||
@@ -285,12 +291,24 @@ 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).
|
||||
// With windows open, convective loss is roughly 5× higher — air moves freely
|
||||
// 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
|
||||
// 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;
|
||||
const thermalMass = preset.thermalMass ?? 1;
|
||||
const solarRise = (totalGain / effectiveHLoss) * thermalMass;
|
||||
|
||||
// Motorhomes/caravans behave more like small insulated rooms than parked
|
||||
// cars. This term captures retained living-space warmth: strongest on cool
|
||||
// days, tapering away as outdoor air warms, and reduced when ventilated.
|
||||
const retainedWarmth = preset.retainedWarmth
|
||||
? Math.max(0, 8 - 0.25 * Ta) * (ventilated ? 0.35 : 1)
|
||||
: 0;
|
||||
const internalGain = (preset.internalGain ?? 0) * (ventilated ? 0.35 : 1);
|
||||
|
||||
const Ti = Ta + solarRise + retainedWarmth + internalGain;
|
||||
|
||||
// Clamp: can't be cooler than outside air; physical cap at 90 °C
|
||||
return Math.max(Ta, Math.min(Ti, 90));
|
||||
|
||||
+16
-10
@@ -127,18 +127,24 @@ export function burnLabel(mins) {
|
||||
// ═══════════════════════════════════════════════════════════════════
|
||||
// VEHICLE TYPES — presets for the cabin heat model.
|
||||
// ───────────────────────────────────────────────────────────────────
|
||||
// Each entry tweaks the three physical levers in calcVehicleInteriorTemp:
|
||||
// albedo — how much solar the bodywork reflects (0 = black, 1 = mirror)
|
||||
// glazingArea — relative side-window glass area (1.0 = typical car)
|
||||
// hCabinLoss — how easily heat escapes the cabin (W/m²K); lower = hotter
|
||||
// Motorhomes have thicker insulation → lower loss
|
||||
// Each entry tweaks the physical levers in calcVehicleInteriorTemp:
|
||||
// albedo — how much solar the bodywork reflects (0 = black, 1 = mirror)
|
||||
// glazingArea — relative sun-exposed glass area (1.0 = typical car)
|
||||
// bodyU — effective body/panel conductance into the cabin. Cars are
|
||||
// thin metal/glass boxes; motorhomes/caravans have insulated
|
||||
// sandwich panels, commonly around 25–35 mm thick.
|
||||
// hCabinLoss — effective heat rejection/infiltration from the cabin air.
|
||||
// thermalMass — lower values mean the interior warms more slowly in the hour.
|
||||
// retainedWarmth — occupied insulated living spaces hold heat from previous
|
||||
// hours, people, appliances, and background heating.
|
||||
// internalGain — small living-space warmth boost when closed up.
|
||||
// ═══════════════════════════════════════════════════════════════════
|
||||
export const VEHICLE_TYPES = {
|
||||
car: { name: 'Car / Hatchback', albedo: 0.25, glazingArea: 1.0, hCabinLoss: 2.0 },
|
||||
mpv: { name: 'MPV / People Carrier', albedo: 0.25, glazingArea: 1.3, hCabinLoss: 2.0 },
|
||||
suv: { name: 'SUV / 4×4', albedo: 0.22, glazingArea: 1.1, hCabinLoss: 1.9 },
|
||||
motorhome: { name: 'Motorhome / Campervan', albedo: 0.35, glazingArea: 0.6, hCabinLoss: 1.2 },
|
||||
caravan: { name: 'Caravan (towed)', albedo: 0.40, glazingArea: 0.4, hCabinLoss: 1.0 },
|
||||
car: { name: 'Car / Hatchback', albedo: 0.25, glazingArea: 1.0, bodyU: 4.0, hCabinLoss: 20, thermalMass: 1.00, retainedWarmth: false, internalGain: 0.0 },
|
||||
mpv: { name: 'MPV / People Carrier', albedo: 0.25, glazingArea: 1.3, bodyU: 4.0, hCabinLoss: 20, thermalMass: 1.00, retainedWarmth: false, internalGain: 0.0 },
|
||||
suv: { name: 'SUV / 4x4', albedo: 0.22, glazingArea: 1.1, bodyU: 3.8, hCabinLoss: 19, thermalMass: 0.95, retainedWarmth: false, internalGain: 0.0 },
|
||||
motorhome: { name: 'Motorhome / Campervan', albedo: 0.55, glazingArea: 0.25, bodyU: 0.9, hCabinLoss: 12, thermalMass: 0.35, retainedWarmth: true, internalGain: 1.2 },
|
||||
caravan: { name: 'Caravan (towed)', albedo: 0.60, glazingArea: 0.22, bodyU: 0.8, hCabinLoss: 11, thermalMass: 0.35, retainedWarmth: true, internalGain: 1.2 },
|
||||
};
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════
|
||||
|
||||
Reference in New Issue
Block a user