IPMVP change-point & ASHRAE G14 (pandas oracle)
PyPI helpers for Option C–style weather-normalized baselines and Guideline 14
calibration scores. Live in open_fdd.ecm_engineering (also re-exported from
open_fdd.analytics). Not product DataFusion FDD — notebooks, twin honesty,
and Wave S4 SQL twin characterization only.
Install: pip install "open-fdd[oracle]" (needs numpy for change-point fits).
External algorithm reference: Camber (Apache-2.0).
Open-FDD ports are clean-room IMT/IPMVP shapes — never vendor Camber into GHCR
central/web or treat camber serve as product UI.
G14 monthly / hourly gates
| Granularity | |NMBE| max | CVRMSE max |
|---|---|---|
| Monthly (calibrated simulation) | 5% | 15% |
| Hourly | 10% | 30% |
from open_fdd.ecm_engineering import score_g14_monthly, score_g14_fuels
measured = [1200, 1100, 900, 850, 700, 650, 800, 950, 1000, 1050, 1150, 1250]
modeled = [1180, 1120, 910, 840, 710, 640, 790, 960, 1010, 1040, 1160, 1240]
s = score_g14_monthly(measured, modeled, n_params=4, fuel="elec")
assert s.pass_ # |NMBE| ≤ 5% and CVRMSE ≤ 15%
both = score_g14_fuels(
elec_measured=measured,
elec_predicted=modeled,
gas_measured=measured,
gas_predicted=modeled,
)
assert both["pass"]
Formulas (ASHRAE Guideline 14):
\[\mathrm{NMBE} = 100 \cdot \frac{\sum (m_i - \hat{m}_i)}{(n-p)\,\bar{m}}\] \[\mathrm{CV(RMSE)} = 100 \cdot \frac{\sqrt{\sum (m_i - \hat{m}_i)^2 / (n-p)}}{\bar{m}}\]where (m_i) is measured, (\hat{m}_i) predicted, (n) samples, (p) model parameters, and (\bar{m}) the mean of measured.
Change-point models (2P / 3P / 4P / 5P)
Independent variable is usually outdoor dry-bulb (°F); dependent is period energy (kWh, therms, …).
| Kind | Shape |
|---|---|
2P |
linear: Y = a + b·X |
3PH |
heating: Y = a + b·(β − X)₊ |
3PC |
cooling: Y = a + b·(X − β)₊ |
4P |
dual slope, one change-point β |
5P |
dual slope, heating + cooling change-points |
import numpy as np
from open_fdd.ecm_engineering import fit_changepoint, select_changepoint, option_c_savings
oat_f = np.linspace(30, 90, 48)
kwh = 200 + 8 * np.maximum(oat_f - 55, 0)
model = fit_changepoint(oat_f, kwh, kind="3PC")
# or: model = select_changepoint(oat_f, kwh)
# Reporting period: same weather, lower use → positive savings
savings = option_c_savings(model, reporting_x=oat_f, reporting_y=kwh * 0.9)
print(savings["savings_total"], savings["baseline_kind"])
Same entry points via open_fdd.analytics for oracle notebooks that already import analytics.
Dual catalog — Camber ↔ Open-FDD role aliases (doc only)
Until Wave S4 SQL M&V twins land, this table is documentation only — not a runtime adapter.
| Concept | Camber-ish / IMT | Open-FDD package / SQL role |
|---|---|---|
| Outdoor temperature (X) | temp, OAT |
outside-air-temp, oa_t, weather dry_bulb_f / web-outside-air-temp |
| Energy (Y) (electric) | energy, kWh |
elec-power → monthly kWh, kwh, UTIL monthly bills |
| Energy (Y) (gas) | gas / therms | gas-flow / gas-rate, UTIL gas bills |
| Period grain | daily / monthly | package utilities monthly; historian resample in oracle |
| Fit quality | CVRMSE / NMBE | score_g14* / model .cvrmse_pct |
| Product charts | Camber UI / serve | Metering radios + future DataFusion /api/analytics/* (S4) |
Boundary
| Layer | Owns |
|---|---|
| This module (PyPI) | Clean-room math + twin honesty helpers |
| Product GHCR | DataFusion SQL FDD / analytics — no pandas in request path |
| Soft-OPEN | Full Camber family ports, PyPI publish tip, SQL twins (Wave S4) |
Related: Engineering calcs · Pandas FDD cookbook · Purpose — Excel + EnergyPlus