Spaces:
Running
Running
Download physics.js from subhan07/hvac-optimization: direct link, hf CLI and curl.
- Browser
- Download file 13.4 kB
-
https://huggingface.co/spaces/subhan07/hvac-optimization/resolve/main/physics.js
- Command line
-
hf download hf://spaces/subhan07/hvac-optimization/physics.js
-
curl -L -o physics.js https://huggingface.co/spaces/subhan07/hvac-optimization/resolve/main/physics.js
13.4 kB
| /** OEH teaching curves — same model as backend/services/oeh_guide_physics.py. Always SIMULATED. */ | |
| (function (g) { | |
| const clamp = (v, a, b) => Math.max(a, Math.min(b, v)); | |
| const avg = (arr) => (arr.length ? arr.reduce((s, n) => s + n, 0) / arr.length : 0); | |
| const oat = (hour, mean, amp) => mean + amp * Math.cos((hour - 15) * (Math.PI / 12)); | |
| const loadCurve = (hour) => clamp(Math.sin(((hour - 6) / 14) * Math.PI), 0.05, 1); | |
| const occupancyCurve = (hour) => clamp(Math.sin(((hour - 8) / 10) * Math.PI), 0.03, 1); | |
| const f = (v, key, d) => { | |
| const n = Number(v[key]); | |
| return Number.isFinite(n) ? n : d; | |
| }; | |
| const pct = (n) => `${n.toFixed(0)}%`; | |
| function pointAt(oid, x, v) { | |
| if (oid === "O1") { | |
| const occ = f(v, "occStart", 8.5); | |
| const sev = f(v, "severity", 50); | |
| const baseStart = occ - 2.5, baseStop = 17.5; | |
| const optStart = occ - (0.5 + (sev / 100) * 2); | |
| const optStop = 17.5 - (0.1 + ((100 - sev) / 100) * 0.4); | |
| return [baseStart <= x && x < baseStop ? 100 : 0, optStart <= x && x < optStop ? 100 : 0]; | |
| } | |
| if (oid === "O2") { | |
| const db = f(v, "deadBand", 1); | |
| const pb = f(v, "propBand", 1); | |
| const dev = clamp(3 * Math.sin(((x - 9) / 14) * Math.PI), -3, 3); | |
| const out = (d, band, p) => { | |
| const half = band / 2; | |
| if (Math.abs(d) <= half) return 0; | |
| return clamp(((Math.abs(d) - half) / Math.max(p, 0.01)) * 100, 0, 100); | |
| }; | |
| return [out(dev, 1, 1), out(dev, db, pb)]; | |
| } | |
| if (oid === "O3") { | |
| const phases = [0, 1.2, 2.4, 3.6, 4.8]; | |
| const devs = phases.map((ph) => Math.max(0, 2.5 * Math.sin(((x - 8 + ph) / 12) * Math.PI))); | |
| devs[4] += f(v, "faultBias", 1.5); | |
| const baseline = Math.max(...devs); | |
| const n = Math.min(Math.trunc(f(v, "numAvg", 3)), 5); | |
| const sorted = [...devs].sort((a, b) => b - a); | |
| const optimized = sorted.slice(0, n).reduce((s, n) => s + n, 0) / n; | |
| return [baseline, optimized]; | |
| } | |
| if (oid === "O4") { | |
| const load = (f(v, "peakLoad", 75) / 100) * loadCurve(x); | |
| const delay = f(v, "stageDelay", 10); | |
| const baseline = clamp(Math.ceil(load * 3 + 0.45), 0, 3); | |
| const optimized = clamp(Math.ceil(load * 3 - ((delay - 5) / 15) * 0.3), 0, 3); | |
| return [baseline, optimized]; | |
| } | |
| if (oid === "O5") { | |
| const demand = (f(v, "demandAmp", 80) / 100) * loadCurve(x); | |
| const openNeeded = 40 + demand * 60; | |
| const optSpeed = clamp(100 * (openNeeded / Math.max(f(v, "targetOpen", 92), 1)), 25, 100); | |
| return [100, (optSpeed / 100) ** 3 * 100]; | |
| } | |
| if (oid === "O6") { | |
| const demand = (f(v, "heatSeverity", 60) / 100) * (1 - loadCurve(x)); | |
| const floor = f(v, "boilerType", 1) ? 40 : 55; | |
| return [82, floor + demand * (82 - floor)]; | |
| } | |
| if (oid === "O7") { | |
| const load = (f(v, "loadSeverity", 80) / 100) * loadCurve(x); | |
| return [6.5, 6.5 + (1 - load) * 5.5]; | |
| } | |
| if (oid === "O8") { | |
| const wb = oat(x, f(v, "wetBulbMean", 20), 4); | |
| return [f(v, "wetBulbMean", 20) + 11.5, wb + f(v, "approach", 3.5)]; | |
| } | |
| if (oid === "O9") { | |
| const load = 100 * loadCurve(x) * (f(v, "loadVar", 60) / 100) + (1 - f(v, "loadVar", 60) / 100) * 70; | |
| const baseline = 5 + (100 - load) * 0.08; | |
| const optimized = f(v, "valveType", 0) ? 2 + (100 - load) * 0.02 : baseline; | |
| return [baseline, optimized]; | |
| } | |
| if (oid === "O10") { | |
| const temp = oat(x, f(v, "oatMean", 18), 6); | |
| const active = temp < 21 && f(v, "dewPoint", 10) < 12; | |
| const demand = 100 * loadCurve(x); | |
| return [demand, active ? demand * 0.15 : demand]; | |
| } | |
| if (oid === "O11") { | |
| const residual = f(v, "residual", 27); | |
| const low = f(v, "overnightLow", 16); | |
| if (x < 4) return [residual - 1, residual - 1]; | |
| if (x < 7.5) { | |
| const pf = clamp((x - 4) / 2.5, 0, 1); | |
| return [residual, residual - pf * Math.max(0, residual - 2 - low)]; | |
| } | |
| return [23, 23]; | |
| } | |
| if (oid === "O12") { | |
| const occ = f(v, "peakOcc", 80) * occupancyCurve(x); | |
| return [100, clamp(30 + occ * 0.7, 30, 100)]; | |
| } | |
| if (oid === "O13") { | |
| const density = f(v, "peakDensity", 55) * occupancyCurve(x); | |
| const speed = clamp(25 + density * 0.75, 25, 100); | |
| return [100, (speed / 100) ** 3 * 100]; | |
| } | |
| if (oid === "O14") { | |
| const load = (f(v, "loadAmp", 75) / 100) * loadCurve(x); | |
| const speed = clamp(100 * ((40 + load * 60) / 95), 30, 100); | |
| return [100, (speed / 100) ** 3 * 100]; | |
| } | |
| if (oid === "O15") return [95, clamp((oat(x, f(v, "ambientMean", 20), 6) - 2) * 2.8, 25, 100)]; | |
| if (oid === "O16") { | |
| const load = (f(v, "loadAmp", 75) / 100) * loadCurve(x); | |
| return [100, clamp(100 * (1 - f(v, "idlePct", 20) / 100) * (0.4 + 0.6 * load), 20, 100)]; | |
| } | |
| if (oid === "O17") return [100 + x * 1.6, 100 - (f(v, "coordScore", 55) / 100) * x * 3.2]; | |
| if (oid === "O18") return [100 + x * 0.3, 100 - (f(v, "coverage", 50) / 100) * x * 0.9]; | |
| if (oid === "O19") { | |
| const base = 100 + x * 1.2; | |
| return [base, base * (1 - clamp(f(v, "freq", 4) / 12, 0, 1))]; | |
| } | |
| if (oid === "O20") { | |
| const baseline = clamp(100 - x * 4, 40, 100); | |
| const factor = f(v, "accessCtrl", 1) * 0.5 + clamp(f(v, "backupFreq", 4) / 12, 0, 1) * 0.5; | |
| return [baseline, clamp(100 - x * 4 * (1 - factor), 40, 100)]; | |
| } | |
| return [100, 100]; | |
| } | |
| function seriesFor(oid, sliders, xType) { | |
| const n = xType === "month" ? 12 : 24; | |
| const out = []; | |
| for (let x = 0; x < n; x++) { | |
| const [b, o] = pointAt(oid, x, sliders); | |
| out.push({ x, baseline: Math.round(b * 1000) / 1000, optimized: Math.round(o * 1000) / 1000 }); | |
| } | |
| return out; | |
| } | |
| function metricsFor(oid, pts, v) { | |
| const ab = avg(pts.map((p) => p.baseline)); | |
| const ao = avg(pts.map((p) => p.optimized)); | |
| const last = pts[pts.length - 1] || { baseline: 0, optimized: 0 }; | |
| if (oid === "O1") { | |
| const bh = pts.filter((p) => p.baseline > 0).length; | |
| const oh = pts.filter((p) => p.optimized > 0).length; | |
| const red = bh ? ((bh - oh) / bh) * 100 : 0; | |
| return [ | |
| { label: "Baseline runtime", value: `${bh} h/day` }, | |
| { label: "Optimized runtime", value: `${oh} h/day` }, | |
| { label: "Operating hours cut", value: pct(red) }, | |
| ]; | |
| } | |
| if (oid === "O2") { | |
| const red = ab ? ((ab - ao) / ab) * 100 : 0; | |
| return [ | |
| { label: "Baseline avg output", value: `${ab.toFixed(0)}%` }, | |
| { label: "Optimized avg output", value: `${ao.toFixed(0)}%` }, | |
| { label: "Est. HVAC energy cut", value: pct(red) }, | |
| ]; | |
| } | |
| if (oid === "O3") { | |
| const red = ab ? ((ab - ao) / ab) * 100 : 0; | |
| return [ | |
| { label: "Baseline signal (high-select)", value: `${ab.toFixed(1)}°C` }, | |
| { label: "Optimized signal (weighted)", value: `${ao.toFixed(1)}°C` }, | |
| { label: "Over-cooling reduced", value: pct(red) }, | |
| ]; | |
| } | |
| if (oid === "O4") { | |
| const red = ab ? clamp(((ab - ao) / ab) * 100, 0, 10) : 0; | |
| return [ | |
| { label: "Avg stages — baseline", value: ab.toFixed(1) }, | |
| { label: "Avg stages — optimized", value: ao.toFixed(1) }, | |
| { label: "Chiller-hours saved", value: pct(red) }, | |
| ]; | |
| } | |
| if (oid === "O5") | |
| return [ | |
| { label: "Baseline fan power", value: "100%" }, | |
| { label: "Optimized fan power", value: `${ao.toFixed(0)}%` }, | |
| { label: "Fan energy saved", value: pct(clamp(100 - ao, 0, 30)) }, | |
| ]; | |
| if (oid === "O6") | |
| return [ | |
| { label: "Baseline flow temp", value: "82°C" }, | |
| { label: "Optimized avg flow temp", value: `${ao.toFixed(0)}°C` }, | |
| { label: "Boiler efficiency gain", value: `${clamp((82 - ao) * 0.15, 0, 5).toFixed(1)}%` }, | |
| ]; | |
| if (oid === "O7") { | |
| const per = f(v, "compType", 0) ? 4.5 : 2.5; | |
| return [ | |
| { label: "Baseline CHW temp", value: "6.5°C" }, | |
| { label: "Optimized avg CHW temp", value: `${ao.toFixed(1)}°C` }, | |
| { label: "Chiller energy saved", value: pct(clamp((ao - 6.5) * per, 0, 15)) }, | |
| ]; | |
| } | |
| if (oid === "O8") { | |
| const sav = clamp((ab - ao) * 2.5, 0, 15); | |
| return [ | |
| { label: "Baseline CW temp", value: `${ab.toFixed(1)}°C` }, | |
| { label: "Optimized avg CW temp", value: `${ao.toFixed(1)}°C` }, | |
| { label: "Chiller energy saved", value: pct(sav) }, | |
| ]; | |
| } | |
| if (oid === "O9") { | |
| const sav = clamp((ab - ao) * 3, 0, 15); | |
| return [ | |
| { label: "Baseline efficiency loss", value: `${ab.toFixed(1)}%` }, | |
| { label: "Optimized efficiency loss", value: `${ao.toFixed(1)}%` }, | |
| { label: "Compressor energy saved", value: pct(sav) }, | |
| ]; | |
| } | |
| if (oid === "O10") { | |
| const hours = pts.filter((p) => p.optimized < p.baseline * 0.9).length; | |
| const sav = ab ? clamp(((ab - ao) / ab) * 100, 0, 20) : 0; | |
| return [ | |
| { label: "Economy cycle active", value: `${hours} h/day` }, | |
| { label: "Optimized avg compressor load", value: `${ao.toFixed(0)}%` }, | |
| { label: "Compressor energy saved", value: pct(sav) }, | |
| ]; | |
| } | |
| if (oid === "O11") { | |
| const window = pts.filter((p) => p.x >= 4 && p.x < 7.5); | |
| const minOpt = window.length ? Math.min(...window.map((p) => p.optimized)) : f(v, "residual", 27); | |
| const residual = f(v, "residual", 27); | |
| return [ | |
| { label: "Residual temp", value: `${residual.toFixed(1)}°C` }, | |
| { label: "Purged down to", value: `${minOpt.toFixed(1)}°C` }, | |
| { label: "Start-up energy saved", value: pct(clamp((residual - minOpt) * 4, 0, 20)) }, | |
| ]; | |
| } | |
| if (oid === "O12") { | |
| const est = Math.round(400 + (f(v, "peakOcc", 80) / 100) * (f(v, "co2SP", 800) - 400)); | |
| return [ | |
| { label: "Optimized avg OA flow", value: `${ao.toFixed(0)}%` }, | |
| { label: "Peak CO₂ estimate", value: `${est} ppm` }, | |
| { label: "Ventilation energy saved", value: pct(clamp(100 - ao, 0, 20)) }, | |
| ]; | |
| } | |
| if (oid === "O13") | |
| return [ | |
| { label: "Baseline fan power", value: "100%" }, | |
| { label: "Optimized avg fan power", value: `${ao.toFixed(0)}%` }, | |
| { label: "Fan energy saved", value: pct(clamp(100 - ao, 0, 80)) }, | |
| ]; | |
| if (oid === "O14") | |
| return [ | |
| { label: "Baseline pump power", value: "100%" }, | |
| { label: "Optimized avg pump power", value: `${ao.toFixed(0)}%` }, | |
| { label: "Pumping energy saved", value: pct(clamp(100 - ao, 0, 30)) }, | |
| ]; | |
| if (oid === "O15") { | |
| const sav = ab ? clamp(((ab - ao) / ab) * 100, 0, 30) : 0; | |
| return [ | |
| { label: "Baseline fan power", value: `${ab.toFixed(0)}%` }, | |
| { label: "Optimized avg fan power", value: `${ao.toFixed(0)}%` }, | |
| { label: "Condenser fan energy saved", value: pct(sav) }, | |
| ]; | |
| } | |
| if (oid === "O16") | |
| return [ | |
| { label: "Baseline pump power", value: "100%" }, | |
| { label: "Optimized avg pump power", value: `${ao.toFixed(0)}%` }, | |
| { label: "CW pump energy saved", value: pct(clamp(100 - ao, 0, 30)) }, | |
| ]; | |
| if (oid === "O17") { | |
| const sav = last.baseline ? clamp(((last.baseline - last.optimized) / last.baseline) * 100, 0, 50) : 0; | |
| return [ | |
| { label: "Year-end index — no plan", value: last.baseline.toFixed(0) }, | |
| { label: "Year-end index — with plan", value: last.optimized.toFixed(0) }, | |
| { label: "Total energy saved", value: pct(sav) }, | |
| ]; | |
| } | |
| if (oid === "O18") { | |
| const cov = f(v, "coverage", 50); | |
| const sav = last.baseline ? clamp(((last.baseline - last.optimized) / last.baseline) * 100, 0, 10) : 0; | |
| return [ | |
| { label: "Total energy saved", value: pct(sav) }, | |
| { label: "Est. NABERS star gain", value: `+${((cov / 100) * 0.5).toFixed(1)} ★` }, | |
| { label: "Training coverage", value: `${cov.toFixed(0)}%` }, | |
| ]; | |
| } | |
| if (oid === "O19") { | |
| const sav = last.baseline ? clamp(((last.baseline - last.optimized) / last.baseline) * 100, 0, 20) : 0; | |
| return [ | |
| { label: "Year-end index — reactive", value: last.baseline.toFixed(0) }, | |
| { label: "Year-end index — maintained", value: last.optimized.toFixed(0) }, | |
| { label: "HVAC energy saved", value: pct(sav) }, | |
| ]; | |
| } | |
| if (oid === "O20") { | |
| const sav = clamp((last.optimized - last.baseline) * 0.2, 0, 10); | |
| return [ | |
| { label: "Settings retained — baseline", value: `${last.baseline.toFixed(0)}%` }, | |
| { label: "Settings retained — managed", value: `${last.optimized.toFixed(0)}%` }, | |
| { label: "HVAC energy saved", value: pct(sav) }, | |
| ]; | |
| } | |
| const sav = ab ? clamp(((ab - ao) / ab) * 100, 0, 80) : 0; | |
| return [ | |
| { label: "Baseline avg", value: ab.toFixed(1) }, | |
| { label: "Optimized avg", value: ao.toFixed(1) }, | |
| { label: "Guide reduction", value: pct(sav) }, | |
| ]; | |
| } | |
| g.OehPhysics = { seriesFor, metricsFor, clamp }; | |
| })(window); | |