// ════════════════════════════════════════════════════════════════════════ // physics.js — Physical constants and meteorological calculations. // // Exports: // SIGMA, EPSILON_P, A_K, ALBEDO_GRASS (radiation constants) // vaporPressureHpa(Ta, RH) Magnus formula → hPa // 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, 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, BUILDING_TYPES } from './utils.js'; // ═══════════════════════════════════════════════════════════════════ // PHYSICAL CONSTANTS // ─────────────────────────────────────────────────────────────────── // These are real-world physics values — don't change them unless you // have a peer-reviewed reason. They're used by calcTmrt() below to // work out how much heat your skin actually absorbs from the sun. // SIGMA — Stefan–Boltzmann constant (radiates heat) // EPSILON_P — emissivity of human skin (~0.97) // A_K — short-wave absorption coefficient for clothing // ALBEDO_GRASS — how much sun grass reflects back at you (23%) // ALBEDO_CONCRETE — how much sun concrete reflects back (30%) // Concrete absorbs more net solar than grass and has // no evaporative cooling, so its surface runs hot. // ═══════════════════════════════════════════════════════════════════ export const SIGMA = 5.670374419e-8; export const EPSILON_P = 0.97; export const A_K = 0.7; export const ALBEDO_GRASS = 0.23; export const ALBEDO_CONCRETE = 0.30; // ═══════════════════════════════════════════════════════════════════ // CONCRETE SURFACE TEMPERATURE (Urban profile) // ─────────────────────────────────────────────────────────────────── // Estimates the surface temperature of exposed concrete using a // simplified energy-balance approach: // • Absorbed solar = globalRad × (1 − albedo) // • No latent heat (no evaporation — concrete is dry) // • Convective loss to air proportional to wind speed // • Result is clamped to a physically plausible range // // This is what matters for contact heat stress in cities — the UTCI // standard uses grass, which runs ~5–15 °C cooler than urban concrete // on a sunny day because grass sweats (transpires). // ═══════════════════════════════════════════════════════════════════ export function calcConcreteTemp(Ta, globalRad, windSpeed) { if (globalRad == null || Ta == null) return null; const absorbed = globalRad * (1 - ALBEDO_CONCRETE); // W/m² // Convective heat transfer coefficient: ~5 W/m²K still air, rises with wind const hc = 5 + 4.5 * Math.sqrt(Math.max(windSpeed || 0, 0)); // Surface temp: Ta + solar gain / convective loss const Ts = Ta + absorbed / hc; // Clamp: can't be cooler than air, cap at 85 °C (melting asphalt territory) return Math.max(Ta, Math.min(Ts, 85)); } // ═══════════════════════════════════════════════════════════════════ // UK HOUSE INDOOR TEMPERATURE (windows closed, no active cooling) // ─────────────────────────────────────────────────────────────────── // Estimates the ambient indoor air temperature of a typical UK brick // house with windows closed and no air conditioning. // // Two heat pathways are modelled: // // 1. WALL CONDUCTION // Heat conducts through brick cavity walls and roof. UK Part L // compliant walls have a U-value around 0.28–0.45 W/m²K; older // solid-brick stock runs higher. A representative mid-stock value // is used. This drives a slow, steady heat transfer proportional // to the difference between outdoor and indoor air temperature. // // 2. WINDOW SOLAR GAIN // A typical UK semi has ~15–18% glazing ratio. Solar energy // transmits through glass, is absorbed by floors and furniture, // and heats the indoor air. Gain is averaged across orientations // (not all windows face south). Diffuse radiation contributes // regardless of sun angle. // // THERMAL LAG // Brick and concrete have high thermal mass — the house responds // 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. // // No mechanical cooling. Minimal infiltration (windows closed). // Internal heat gains (people, appliances) are not modelled. // // Colour thresholds: // < 20 °C — cool, may need heating // 20–26 °C — comfortable // 26–32 °C — warm; WHO heatwave advisory threshold for sleeping // > 32 °C — hot; risk for elderly and vulnerable occupants // ═══════════════════════════════════════════════════════════════════ // Single-hour instantaneous heat load (W/m² effective, indoor side). // Used internally by calcIndoorTempPass — not exported. function _houseHeatLoad(Ta, globalRad, solElev) { // Wall + roof conduction: U-value ~0.35 W/m²K × effective envelope area ratio // We express as a gain-per-degree-delta — applied against Ti later in the pass. // (See calcIndoorTempPass for how this feeds the lag model.) // Window solar gain: glazing ratio 0.16, g-value 0.63 (standard double glazing), // averaged across orientations (0.5 factor — not all windows face the sun). const glazingRatio = 0.16; const gValue = 0.63; const orientFactor = 0.50; const solarGain = globalRad * glazingRatio * gValue * orientFactor; return { conductionDelta: Ta, solarGain }; } // Two-pass function: call with the full arrays of hourly Ta and globalRad. // Returns an array of indoor temperatures, one per hour. // 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 { 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 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; const glob = globArr[i] ?? 0; // Solar gain through windows (W/m² effective) const solarGain = glob * glazingRatio * gValue * orientFactor; const retainedWarmth = Math.max(0, (baseTemp ?? 16) - Ta) * (retainedScale ?? 0.35); const target = Ta + solarGain * (solarScale ?? 0.1) + retainedWarmth + (internalGain ?? 0.7); // 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)); } return result; } // ═══════════════════════════════════════════════════════════════════ // UK HOUSE INDOOR TEMPERATURE — MANAGED (curtains closed, windows open) // ─────────────────────────────────────────────────────────────────── // Models the same UK brick house as calcIndoorTempPass but with two // behavioural interventions that reflect standard heatwave advice: // // 1. CURTAINS CLOSED // Thick curtains block ~80% of window solar gain before it enters // the room. The small remaining fraction is diffuse light through // the curtain fabric. Wall conduction is unchanged. // // 2. WINDOWS OPEN (smart ventilation) // When outdoor air is cooler than the indoor air, windows are open // and a ventilation heat exchange pulls the indoor temp toward // outdoor. When outdoor is hotter than indoor, windows are kept // shut — so this strategy never makes things worse, only better. // Ventilation rate ~2 air changes/hour for a well-opened house. // // Same thermal lag model as calcIndoorTempPass (4 h brick time constant). // ═══════════════════════════════════════════════════════════════════ // 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 { 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 = Math.max(TaArr[0] ?? 15, baseTemp ?? 16); for (let i = 0; i < n; i++) { const Ta = TaArr[i] ?? Ti; const glob = globArr[i] ?? 0; // Solar gain — curtains block curtainBlock fraction const solarGain = glob * glazingRatio * gValue * orientFactor * (1 - curtainBlock); const retainedWarmth = Math.max(0, (baseTemp ?? 16) - Ta) * (retainedScale ?? 0.35); const target = Ta + solarGain * (solarScale ?? 0.1) + retainedWarmth + (internalGain ?? 0.7); // Apply thermal lag toward the managed target. Ti = Ti + alpha * (target - Ti); // Smart ventilation: only open windows when outside is cooler if (Ta < Ti) { Ti = Ti + ventAlpha * (Ta - Ti); } result[i] = Math.max(Math.min(Ti, 55), Math.min(Ta, Ti)); } return result; } // ═══════════════════════════════════════════════════════════════════ // VEHICLE INTERIOR CABIN TEMPERATURE (seated occupant, not in sunbeam) // ─────────────────────────────────────────────────────────────────── // Models the ambient cabin air temperature experienced by an occupant // seated out of direct sunlight inside a sealed, parked vehicle. // Two heat sources are combined: // // 1. PANEL CONDUCTION // Aluminium body panels absorb solar radiation and conduct heat // into the cabin. Albedo ~0.25 (mid-point for typical mixed-colour // fleet; dark paint ~0.10, silver/white ~0.40). // Panel surface temp → conductive gain into cabin air. // // 2. SIDE-WINDOW GLAZING GAIN (sun-angle dependent) // When solar elevation is between ~10° and ~60°, the sun's rays // cut through the side glass at an angle that allows significant // transmission into the cabin (rather than hitting the roof or // reflecting off at a shallow angle). This warms the cabin air // but the occupant is modelled as NOT sitting in the beam — // 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 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 // 35–45 °C — dangerous for children/pets (hyperthermia risk) // > 45 °C — potentially fatal within minutes // ═══════════════════════════════════════════════════════════════════ 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. const preset = VEHICLE_TYPES[vehicleType] || VEHICLE_TYPES.car; // ── 1. Panel conduction ────────────────────────────────────────── const panelAbsorbed = globalRad * (1 - preset.albedo); // W/m² absorbed by bodywork // Panel surface temp: absorbed solar / convective loss to outside air // 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. 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) ───────────────── // Glazing transmission for auto glass ~0.70; scaled by vehicle glazing area. const tau = 0.70 * preset.glazingArea; let glazingGain = 0; if (solElev != null && solElev > 10 && solElev < 60) { // Scale factor: peaks around 30–40° elevation (sun cuts squarely // through side glass), tapers off toward 10° (shallow/reflected) // and 60° (sun increasingly hitting roof not side glass). // Use a simple tent function peaking at 35°. const peak = 35; const halfWidth = 25; // degrees either side const factor = Math.max(0, 1 - Math.abs(solElev - peak) / halfWidth); // Diffuse radiation also enters through glass regardless of angle glazingGain = tau * globalRad * factor * 0.5; // occupant not in beam → 50% ambient } else { // Outside the side-window zone: diffuse only (scattered sky light) glazingGain = tau * (globalRad * 0.15); // ~15% diffuse fraction } // ── Combine into cabin air temperature ─────────────────────────── // Total heat input per m² of cabin surface const totalGain = conductionGain + glazingGain; // 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 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)); } // ── FUTURE FEATURE: Vehicle-at-speed thermal model ──────────────────────────── // Idea: extend calcVehicleInteriorTemp (or add a companion function) to model // cabin temperature for a vehicle travelling at speed, not just parked. // // Key physics differences from the static model: // • Forced convection over the shell scales with vehicle speed (v²), so // hOut rises significantly — shell cools much faster than when parked. // • Above ~30 mph the vehicle's own forward motion dominates airflow, so // ambient wind direction becomes largely irrelevant (simplifies the model). // • Speed classes to model: urban (~20 mph), dual carriageway (~50 mph), // motorway (~70 mph) — each with a derived hOut multiplier. // • Windows-open behaviour changes completely at speed: at 70 mph open // windows create high-velocity through-flow, dramatically cutting cabin // temp vs. the sealed-car case (but much less pleasant than AC!). // • Roof and bonnet solar gain stays the same; side-glass gain is unchanged. // • AC-off vs AC-on would be the primary user toggle alongside speed class. // // Suggested signature: // calcVehicleInteriorTempAtSpeed(Ta, globalRad, solElev, vehicleType, speedMph, windowsOpen) // // This would be a useful companion output column (e.g. "Vehicle (moving)") // for road-trip planning, dog-in-car safety at a rest stop vs. motorway, etc. // ───────────────────────────────────────────────────────────────────────────── // Vapour pressure (Magnus → hPa) export function vaporPressureHpa(Ta, RH) { const es = 6.105 * Math.exp((17.27 * Ta) / (237.7 + Ta)); return es * (RH / 100); } // Solar elevation (NOAA simplified, degrees) export function solarElevationDeg(lat, lon, dateUTC) { const start = Date.UTC(dateUTC.getUTCFullYear(), 0, 0); const diff = dateUTC - start; const DOY = Math.floor(diff / 86400000); const hourUTC = dateUTC.getUTCHours() + dateUTC.getUTCMinutes() / 60 + dateUTC.getUTCSeconds() / 3600; const gamma = ((2 * Math.PI) / 365) * (DOY - 1 + (hourUTC - 12) / 24); const eqtime = 229.18 * (0.000075 + 0.001868 * Math.cos(gamma) - 0.032077 * Math.sin(gamma) - 0.014615 * Math.cos(2 * gamma) - 0.040849 * Math.sin(2 * gamma)); const decl = 0.006918 - 0.399912 * Math.cos(gamma) + 0.070257 * Math.sin(gamma) - 0.006758 * Math.cos(2 * gamma) + 0.000907 * Math.sin(2 * gamma) - 0.002697 * Math.cos(3 * gamma) + 0.00148 * Math.sin(3 * gamma); const timeOffset = eqtime + 4 * lon; const tst = hourUTC * 60 + timeOffset; const ha = (((tst / 4) - 180) * Math.PI) / 180; const latRad = (lat * Math.PI) / 180; const cosZenith = Math.sin(latRad) * Math.sin(decl) + Math.cos(latRad) * Math.cos(decl) * Math.cos(ha); const zenith = Math.acos(Math.max(-1, Math.min(1, cosZenith))); return (Math.PI / 2 - zenith) * (180 / Math.PI); } // Mean radiant temperature export function calcTmrt(Ta, dirRad, diffRad, globalRad, solElev) { const TaK = Ta + 273.15; let fp = 0; if (solElev > 0) { const h2 = solElev; fp = 0.308 * Math.cos((Math.PI / 180) * h2 * (0.998 - (h2 * h2) / 50000)); } let DNI = 0; if (solElev > 1) { DNI = dirRad / Math.sin((solElev * Math.PI) / 180); DNI = Math.min(DNI, 1100); } const Sshort = A_K * (fp * DNI + 0.5 * diffRad + 0.5 * ALBEDO_GRASS * globalRad); const Slong = EPSILON_P * SIGMA * Math.pow(TaK, 4); const TmrtK = Math.pow((Sshort + Slong) / (EPSILON_P * SIGMA), 0.25); return TmrtK - 273.15; } // ═══════════════════════════════════════════════════════════════════ // UTCI POLYNOMIAL — DO NOT EDIT (or be VERY careful if you do) // ─────────────────────────────────────────────────────────────────── // This is the official 210-term Bröde et al. (2012) approximation. // It takes air temp, mean radiant temp, wind, and humidity and returns // the "felt" temperature. Every number is a peer-reviewed coefficient. // Scroll past it — there's nothing here you'll want to change. // ═══════════════════════════════════════════════════════════════════ export function utciApprox(Ta, Tmrt, va10, ehPa) { const va = Math.max(0.5, Math.min(17, va10)); const D_Tmrt = Tmrt - Ta; const Pa = ehPa / 10; const T = Ta, V = va, D = D_Tmrt, P = Pa; const T2=T*T, T3=T2*T, T4=T3*T, T5=T4*T, T6=T5*T; const V2=V*V, V3=V2*V, V4=V3*V, V5=V4*V, V6=V5*V; const D2=D*D, D3=D2*D, D4=D3*D, D5=D4*D, D6=D5*D; const P2=P*P, P3=P2*P, P4=P3*P, P5=P4*P, P6=P5*P; return T + 6.07562052e-1 + -2.27712343e-2 * T + 8.06470249e-4 * T2 + -1.54271372e-4 * T3 + -3.24651735e-6 * T4 + 7.32602852e-8 * T5 + 1.35959073e-9 * T6 + -2.25836520e0 * V + 8.80326035e-2 * T*V + 2.16844454e-3 * T2*V + -1.53347087e-5 * T3*V + -5.72983704e-7 * T4*V + -2.55090145e-9 * T5*V + -7.51269505e-1 * V2 + -4.08350271e-3 * T*V2 + -5.21670675e-5 * T2*V2 + 1.94544667e-6 * T3*V2 + 1.14099531e-8 * T4*V2 + 1.58137256e-1 * V3 + -6.57263143e-5 * T*V3 + 2.22697524e-7 * T2*V3 + -4.16117031e-8 * T3*V3 + -1.27762753e-2 * V4 + 9.66891875e-6 * T*V4 + 2.52785852e-9 * T2*V4 + 4.56306672e-4 * V5 + -1.74202546e-7 * T*V5 + -5.91491269e-6 * V6 + 3.98374029e-1 * D + 1.83945314e-4 * T*D + -1.73754510e-4 * T2*D + -7.60781159e-7 * T3*D + 3.77830287e-8 * T4*D + 5.43079673e-10 * T5*D + -2.00518269e-2 * V*D + 8.92859837e-4 * T*V*D + 3.45433048e-6 * T2*V*D + -3.77925774e-7 * T3*V*D + -1.69699377e-9 * T4*V*D + 1.69992415e-4 * V2*D + -4.99204314e-5 * T*V2*D + 2.47417178e-7 * T2*V2*D + 1.07596466e-8 * T3*V2*D + 8.49242932e-5 * V3*D + 1.35191328e-6 * T*V3*D + -6.21531254e-9 * T2*V3*D + -4.99410301e-6 * V4*D + -1.89489258e-8 * T*V4*D + 8.15300114e-8 * V5*D + 7.55043090e-4 * D2 + -5.65095215e-5 * T*D2 + -4.52166564e-7 * T2*D2 + 2.46688878e-8 * T3*D2 + 2.42674348e-10 * T4*D2 + 1.54547250e-4 * V*D2 + 5.24110970e-6 * T*V*D2 + -8.75874982e-8 * T2*V*D2 + -1.50743064e-9 * T3*V*D2 + -1.56236307e-5 * V2*D2 + -1.33895614e-7 * T*V2*D2 + 2.49709824e-9 * T2*V2*D2 + 6.51711721e-7 * V3*D2 + 1.94960053e-9 * T*V3*D2 + -1.00361113e-8 * V4*D2 + -1.21206673e-5 * D3 + -2.18203660e-7 * T*D3 + 7.51269482e-9 * T2*D3 + 9.79063848e-11 * T3*D3 + 1.25006734e-6 * V*D3 + -1.81584736e-9 * T*V*D3 + -3.52197671e-10 * T2*V*D3 + -3.36514630e-8 * V2*D3 + 1.35908359e-10 * T*V2*D3 + 4.17032620e-10 * V3*D3 + -1.30369025e-9 * D4 + 4.13908461e-10 * T*D4 + 9.22652254e-12 * T2*D4 + -5.08220384e-9 * V*D4 + -2.24730961e-11 * T*V*D4 + 1.17139133e-10 * V2*D4 + 6.62154879e-10 * D5 + 4.03863260e-13 * T*D5 + 1.95087203e-12 * V*D5 + -4.73602469e-12 * D6 + 5.12733497e0 * P + -3.12788561e-1 * T*P + -1.96701861e-2 * T2*P + 9.99690870e-4 * T3*P + 9.51738512e-6 * T4*P + -4.66426341e-7 * T5*P + 5.48050612e-1 * V*P + -3.30552823e-3 * T*V*P + -1.64119440e-3 * T2*V*P + -5.16670694e-6 * T3*V*P + 9.52692432e-7 * T4*V*P + -4.29223622e-2 * V2*P + 5.00845667e-3 * T*V2*P + 1.00601257e-6 * T2*V2*P + -1.81748644e-6 * T3*V2*P + -1.25813502e-3 * V3*P + -1.79330391e-4 * T*V3*P + 2.34994441e-6 * T2*V3*P + 1.29735808e-4 * V4*P + 1.29064870e-6 * T*V4*P + -2.28558686e-6 * V5*P + -3.69476348e-2 * D*P + 1.62325322e-3 * T*D*P + -3.14279680e-5 * T2*D*P + 2.59835559e-6 * T3*D*P + -4.77136523e-8 * T4*D*P + 8.64203390e-3 * V*D*P + -6.87405181e-4 * T*V*D*P + -9.13863872e-6 * T2*V*D*P + 5.15916806e-7 * T3*V*D*P + -3.59217476e-5 * V2*D*P + 3.28696511e-5 * T*V2*D*P + -7.10542454e-7 * T2*V2*D*P + -1.24382300e-5 * V3*D*P + -7.38584400e-9 * T*V3*D*P + 2.20609296e-7 * V4*D*P + -7.32469180e-4 * D2*P + -1.87381964e-5 * T*D2*P + 4.80925239e-6 * T2*D2*P + -8.75492040e-8 * T3*D2*P + 2.77862930e-5 * V*D2*P + -5.06004592e-6 * T*V*D2*P + 1.14325367e-7 * T2*V*D2*P + 2.53016723e-6 * V2*D2*P + -1.72857035e-8 * T*V2*D2*P + -3.95079398e-8 * V3*D2*P + -3.59413173e-7 * D3*P + 7.04388046e-7 * T*D3*P + -1.89309167e-8 * T2*D3*P + -4.79768731e-7 * V*D3*P + 7.96079978e-9 * T*V*D3*P + 1.62897058e-9 * V2*D3*P + 3.94367674e-8 * D4*P + -1.18566247e-9 * T*D4*P + 3.34678041e-10 * V*D4*P + -1.15606447e-10 * D5*P + -2.80626406e0 * P2 + 5.48712484e-1 * T*P2 + -3.99428410e-3 * T2*P2 + -9.54009191e-4 * T3*P2 + 1.93090978e-5 * T4*P2 + -3.08806365e-1 * V*P2 + 1.16952364e-2 * T*V*P2 + 4.95271903e-4 * T2*V*P2 + -1.90710882e-5 * T3*V*P2 + 2.10787756e-3 * V2*P2 + -6.98445738e-4 * T*V2*P2 + 2.30109073e-5 * T2*V2*P2 + 4.17856590e-4 * V3*P2 + -1.27043871e-5 * T*V3*P2 + -3.04620472e-6 * V4*P2 + 5.14507424e-2 * D*P2 + -4.32510997e-3 * T*D*P2 + 8.99281156e-5 * T2*D*P2 + -7.14663943e-7 * T3*D*P2 + -2.66016305e-4 * V*D*P2 + 2.63789586e-4 * T*V*D*P2 + -7.01199003e-6 * T2*V*D*P2 + -1.06823306e-4 * V2*D*P2 + 3.61341136e-6 * T*V2*D*P2 + 2.29748967e-7 * V3*D*P2 + 3.04788893e-4 * D2*P2 + -6.42070836e-5 * T*D2*P2 + 1.16257971e-6 * T2*D2*P2 + 7.68023384e-6 * V*D2*P2 + -5.47446896e-7 * T*V*D2*P2 + -3.59937910e-8 * V2*D2*P2 + -4.36497725e-6 * D3*P2 + 1.68737969e-7 * T*D3*P2 + 2.67489271e-8 * V*D3*P2 + 3.23926897e-9 * D4*P2 + -3.53874123e-2 * P3 + -2.21201190e-1 * T*P3 + 1.55126038e-2 * T2*P3 + -2.63917279e-4 * T3*P3 + 4.53433455e-2 * V*P3 + -4.32943862e-3 * T*V*P3 + 1.45389826e-4 * T2*V*P3 + 2.17508610e-4 * V2*P3 + -6.66724702e-5 * T*V2*P3 + 3.33217140e-5 * V3*P3 + -2.26921615e-3 * D*P3 + 3.80261982e-4 * T*D*P3 + -5.45314314e-9 * T2*D*P3 + -7.96355448e-4 * V*D*P3 + 2.53458034e-5 * T*V*D*P3 + -6.31223658e-6 * V2*D*P3 + 3.02122035e-4 * D2*P3 + -4.77403547e-6 * T*D2*P3 + 1.73825715e-6 * V*D2*P3 + -4.09087898e-7 * D3*P3 + 6.14155345e-1 * P4 + -6.16755931e-2 * T*P4 + 1.33374846e-3 * T2*P4 + 3.55375387e-3 * V*P4 + -5.13027851e-4 * T*V*P4 + 1.02449757e-4 * V2*P4 + -1.48526421e-3 * D*P4 + -4.11469183e-5 * T*D*P4 + -6.80434415e-6 * V*D*P4 + -9.77675906e-6 * D2*P4 + 8.82773108e-2 * P5 + -3.01859306e-3 * T*P5 + 1.04452989e-3 * V*P5 + 2.47090539e-4 * D*P5 + 1.48348065e-3 * P6; }