From cbc7a22c78b2d0cded7ee0fee6d5f233720c03b9 Mon Sep 17 00:00:00 2001 From: Kjell Tore Guttormsen Date: Mon, 29 Jun 2026 09:42:13 +0200 Subject: [PATCH] =?UTF-8?q?docs(shared):=20bygg-energi=20mikro-eksempel=20?= =?UTF-8?q?=E2=80=94=20OKF-bundle=20+=20golden=20+=20load-bearing=20test?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Persistent dev-fixture for energieffektivisering (energiledelse/M&V), valgt for sin lærings-overflate: gapet mellom modellert besparelse (validatoren regner) og faktisk realisert besparelse i drift (eksperten kjenner) — det ExpeL skal lære. Ett kontorbygg, ett LED-retrofit-tiltak. OKF-bundle (index/project/hypothesis/ methodology/reference/verdict) bærer kontekst-laget; verdict-led-fro.md koder realiseringsgraden (RR ≈ 0,82, forankret i National Grid SBS 2010) som ExpeL-frø. Energi mappet inn i den EKSISTERENDE kost-IR-en uendret (affected = byggets totale energikostnad, claimed = modellert besparelse ~10 % < 30 %-cap), så validatoren kjører som-den-er — src/ urørt. golden.json fryser de seeded percentilene; testen beviser at fixturen er konsumerbar (validerer, ikke Rejection), ikke bare til stede. Domenetall verifisert mot primærkilder (EVO/IPMVP, DOE/NREL UMP, CPUC, fire evalueringsstudier); norsk energipris mot SSB Q1 2026. README + shared/README oppdatert (eksempel finnes, ikke lenger "planned"). Suite 121/4, ruff+mypy rene. Co-Authored-By: Claude Opus 4.8 (1M context) Claude-Session: https://claude.ai/code/session_01MHR8iKxJRxDiDfNw8HZmWE --- README.md | 4 +- shared/README.md | 7 +- .../bygg-energi-mikro/bygg-kontor-nord.md | 40 ++++++ shared/examples/bygg-energi-mikro/golden.json | 21 +++ shared/examples/bygg-energi-mikro/index.md | 41 ++++++ .../kilder-realiseringsgap.md | 46 +++++++ .../bygg-energi-mikro/metode-ipmvp-a.md | 40 ++++++ .../bygg-energi-mikro/tiltak-led-retrofit.md | 66 ++++++++++ .../bygg-energi-mikro/validator-input.json | 16 +++ .../bygg-energi-mikro/verdict-led-fro.md | 56 ++++++++ tests/test_bygg_energi_mikro.py | 123 ++++++++++++++++++ 11 files changed, 457 insertions(+), 3 deletions(-) create mode 100644 shared/examples/bygg-energi-mikro/bygg-kontor-nord.md create mode 100644 shared/examples/bygg-energi-mikro/golden.json create mode 100644 shared/examples/bygg-energi-mikro/index.md create mode 100644 shared/examples/bygg-energi-mikro/kilder-realiseringsgap.md create mode 100644 shared/examples/bygg-energi-mikro/metode-ipmvp-a.md create mode 100644 shared/examples/bygg-energi-mikro/tiltak-led-retrofit.md create mode 100644 shared/examples/bygg-energi-mikro/validator-input.json create mode 100644 shared/examples/bygg-energi-mikro/verdict-led-fro.md create mode 100644 tests/test_bygg_energi_mikro.py diff --git a/README.md b/README.md index beb5be1..468da8c 100644 --- a/README.md +++ b/README.md @@ -12,8 +12,10 @@ The result will never fit any single customer 100%. The goal is a **~90% genuine ## Docs +- [`docs/plan/2026-06-26-maalbilde-agentic-loop.md`](docs/plan/2026-06-26-maalbilde-agentic-loop.md) — target picture: the agentic cost-saving loop + OKF knowledge architecture (north star). - [`docs/research/2026-06-23-prior-art-platform.md`](docs/research/2026-06-23-prior-art-platform.md) — prior-art & platform research (incl. implementation register §15). -- [`docs/plan/2026-06-23-incremental-plan.md`](docs/plan/2026-06-23-incremental-plan.md) — incremental delivery plan. +- [`docs/plan/2026-06-23-incremental-plan.md`](docs/plan/2026-06-23-incremental-plan.md) — incremental delivery plan (deterministic backbone). +- [`shared/`](shared/) — framework-neutral shared core (concept + example OKF knowledge bundles), reused unchanged by both reference implementations. ## Stack diff --git a/shared/README.md b/shared/README.md index bcdb1c1..3a2e2b6 100644 --- a/shared/README.md +++ b/shared/README.md @@ -16,8 +16,11 @@ so the only thing that differs is the agent framework itself. - [`CONCEPT.md`](CONCEPT.md) — the business concept, written for a non-specialist (e.g. a business developer at another company). -- *(planned)* the method specification, the example knowledge bundles (OKF / LLM-wiki), - the expert-reviewer persona, and the golden-suite of expected validator outcomes. +- [`examples/bygg-energi-mikro/`](examples/bygg-energi-mikro/) — the first example knowledge + bundle (OKF / LLM-wiki): one office building, one LED-retrofit measure, with a seed expert + verdict encoding the realization gap and a golden-suite of expected validator outcomes. A + small **dev fixture** for exercising the agentic loop; a realistic full-scale example comes later. +- *(planned)* the method specification and the expert-reviewer persona. ## Rules diff --git a/shared/examples/bygg-energi-mikro/bygg-kontor-nord.md b/shared/examples/bygg-energi-mikro/bygg-kontor-nord.md new file mode 100644 index 0000000..c90fb85 --- /dev/null +++ b/shared/examples/bygg-energi-mikro/bygg-kontor-nord.md @@ -0,0 +1,40 @@ +--- +type: project +title: "Kontorbygg Nord" +description: "Fiktivt næringsbygg (kontor) brukt som mikro-eksempel. Energibaseline og rammer for ett effektiviseringstiltak." +resource: BYGG-KONTOR-NORD +tags: [kontorbygg, energibaseline, naeringsbygg] +timestamp: 2026-06-29 +--- + +# Kontorbygg Nord (BYGG-KONTOR-NORD) + +Fiktivt kontorbygg. Tallene er **illustrative men forankret i typiske norske verdier** — +ikke et ekte bygg. En produksjons-deployer erstatter denne med en ekte kunnskapsbase. + +## Energibaseline + +| Størrelse | Verdi | Merknad | +|---|---|---| +| Oppvarmet bruksareal (BRA) | ~2 500 m² | [I] illustrativt | +| Totalt elforbruk | **300 000 kWh/år** | [I]; ~120 kWh/m²/år — typisk norsk kontor | +| Herav belysning | ~54 000 kWh/år (~18 %) | 200 armaturer × 90 W × 3 000 t/år | +| Variabel energikostnad | **1,00 NOK/kWh** ekskl. mva | [V-forankret] kraftpris + nettleie-energiledd + elavgift | + +**Energiprisen** (1,00 NOK/kWh) er den marginale variable kostnaden et spart kWh faktisk +unngår, ekskl. mva (næring trekker fra mva). Sammensetning, forankret i SSB Q1 2026: +kraftpris tjenesteytende næringer ~0,80–1,12 NOK/kWh + nettleie energiledd ~0,10–0,13 + +elavgift 0,0713. Den varierer kraftig med prisområde (NO4 ~0,13 vs NO2 ~0,96 i kraftpris +alene) og sesong — derfor er den **konfigurerbar**, og usikkerheten håndteres i Monte +Carlo-steget (band 0,70–1,40 NOK/kWh). Se [kilder-realiseringsgap.md](kilder-realiseringsgap.md). + +## Rammer (constraints) + +- Tiltak vurderes **inne i** dette prosjektet (ikke på tvers av en portefølje). +- Budsjett og tekniske rammer eies av deployer; her holdes de minimale. +- Bygget driftes i normal kontortid; belysning styres delvis på timeplan (relevant for + realiseringsgapet — se [verdict-led-fro.md](verdict-led-fro.md)). + +## Kandidat-tiltak + +- [tiltak-led-retrofit.md](tiltak-led-retrofit.md) — LED-retrofit av belysning. diff --git a/shared/examples/bygg-energi-mikro/golden.json b/shared/examples/bygg-energi-mikro/golden.json new file mode 100644 index 0000000..bbe407e --- /dev/null +++ b/shared/examples/bygg-energi-mikro/golden.json @@ -0,0 +1,21 @@ +{ + "_note": "Golden-suite (forventet utfall) for bygg-energi-mikro. To deler: (1) 'validator' fryser den deterministiske (seeded, _MC_SEED=20260624) outputen av validate_proposal pa validator-input.json — en regresjon som fanger utilsiktede endringer i validator/MC. Den MENINGSFULLE assertionen er validates=true (claimed < P90). (2) 'learning_surface' koder realiseringsgapet validatoren IKKE kan regne — ExpeL-froet fra verdict-led-fro.md. expected_actual = realization_rate * modelled.", + "validator": { + "outcome": "ValidatedProposal", + "validates": true, + "claimed_saving_nok": 30000, + "nominal_feasible": 90000.0, + "p10": 68543.08886748762, + "p50": 95443.98966314227, + "p90": 121057.08845805985, + "_percentile_meaning": "OVRE FEASIBLE GRENSE (30% av samplet energikostnad), IKKE LED-besparelsens fysiske band. Se tiltak-led-retrofit.md, 'Mapping til validatoren'." + }, + "learning_surface": { + "modelled_saving_nok": 30000, + "realization_rate": 0.82, + "expected_actual_saving_nok": 24600, + "gap_source": "hours-of-use-overestimation", + "context_key": "kontorbygg; HOU-kilde=timeplan-stipulert", + "_meaning": "Det validatoren IKKE kan regne fra parameterne. ExpeL-frøet loopens steg 1 skal hente og anvende. Se verdict-led-fro.md." + } +} diff --git a/shared/examples/bygg-energi-mikro/index.md b/shared/examples/bygg-energi-mikro/index.md new file mode 100644 index 0000000..de3da80 --- /dev/null +++ b/shared/examples/bygg-energi-mikro/index.md @@ -0,0 +1,41 @@ +--- +type: index +title: "Bygg-energi mikro-eksempel — kontorbygg, LED-retrofit" +description: "Minimal OKF-bundle for ett kontorbygg med ett energieffektiviseringstiltak (LED-belysningsretrofit). Utviklings-fixture for den agentiske loopen: kontekst → hypotese → deterministisk validering → ekspert-dom → læring." +tags: [energieffektivisering, M&V, IPMVP, mikro-eksempel, fixture] +timestamp: 2026-06-29 +--- + +# Bygg-energi mikro-eksempel + +En **mikro OKF-bundle** (Open Knowledge Format): ett kontorbygg, ett kandidat-tiltak. +Den er liten med vilje — formålet er **rask småskala-testing og validering** av den +agentiske loopen gjennom hele utviklingsløpet. Et **realistisk fullskala-eksempel** +bygges senere (målbilde §8); dette er forløperen. + +> Dette er en framework-nøytral artefakt (null kode-avhengighet). Den deles uendret +> mellom MAF- og Claude-SDK-implementasjonene. Se [shared/README.md](../../README.md). + +## Hvorfor energieffektivisering + +Domenet ble valgt for sin **lærings-overflate**: et reelt, dokumentert gap mellom det +en deterministisk validator kan *regne* (modellert besparelse fra parametere) og det en +fagekspert *kjenner* (faktisk realisert besparelse i drift). Det gapet — realiseringsgraden +— er det lærings-sløyfa (ExpeL) skal lære. Se [verdict-led-fro.md](verdict-led-fro.md). + +## Innhold (progressiv disclosure) + +- [bygg-kontor-nord.md](bygg-kontor-nord.md) — `type: project` — bygget og energibaseline. +- [tiltak-led-retrofit.md](tiltak-led-retrofit.md) — `type: hypothesis` — kandidat-tiltaket + (LED-retrofit) med alle parametere for den modellerte besparelsen. +- [metode-ipmvp-a.md](metode-ipmvp-a.md) — `type: methodology` — M&V-metoden (IPMVP Option A). +- [kilder-realiseringsgap.md](kilder-realiseringsgap.md) — `type: reference` — verifisert + litteratur om realiseringsgrad og dets årsaker. +- [verdict-led-fro.md](verdict-led-fro.md) — `type: verdict` — frøsatt ekspert-dom som koder + realiseringsgapet. **ExpeL-frøet** loopens steg 1 henter fra. + +## Hvordan den kjøres i dag + +`validator-input.json` er IR-projeksjonen den **eksisterende deterministiske validatoren** +konsumerer uendret (energitiltaket mappet inn i kost-IR-en); `golden.json` fryser det +forventede utfallet. Se [tiltak-led-retrofit.md](tiltak-led-retrofit.md) §«Mapping til validatoren». diff --git a/shared/examples/bygg-energi-mikro/kilder-realiseringsgap.md b/shared/examples/bygg-energi-mikro/kilder-realiseringsgap.md new file mode 100644 index 0000000..156bce3 --- /dev/null +++ b/shared/examples/bygg-energi-mikro/kilder-realiseringsgap.md @@ -0,0 +1,46 @@ +--- +type: reference +title: "Realiseringsgrad og energy performance gap — verifisert litteratur" +description: "Kildebelagte tall for gapet mellom modellert og faktisk realisert besparelse, og dets systematiske årsaker. Grunnlaget for verdict-frøets realiseringsgrad." +tags: [realization-rate, performance-gap, M&V, evaluering, kilder] +timestamp: 2026-06-29 +--- + +# Realiseringsgrad (realization rate) — verifisert litteratur + +**Realiseringsgrad (RR)** = faktisk evaluert besparelse (ex-post) ÷ modellert/påstått +besparelse (ex-ante). RR < 1 betyr at drift leverte mindre enn modellen lovte. Avviket +kalles *energy performance gap*. Alle tall under er verifisert mot primærkilde [V]. + +| Nivå | Funn | Kilde | +|---|---|---| +| Program (regulatorisk default) | Default gross RR **0,90** for kWh/kW/therm; ex-ante «generally over-estimated» | CPUC Resolution E-4952 | +| Program (lys, drift lavere) | Operational adjustment ned til **81,1 %** (metrede driftstimer 15 % lavere); coincidence factor **0,566** vs antatt 1,0 | National Grid SBS 2010 (DNV KEMA) | +| Program (lys, drift høyere) | Hours-of-Use RR **106,5 %**; coincidence 72,2 % — gapet går **begge veier** | Massachusetts Impact Evaluation 2010 | +| Parameter (driftstimer) | Metret **3 053 t/år** vs antatt **3 772 t/år** (≈19 % lavere); CV ≈ 0,5 | Efficiency Maine 2021 | +| Portefølje | Commercial lighting **98 %** vs residential **61 %** vs total **93 %** | LADWP Retrospective FY15/16–19/20 | +| Bygg (grønne næringsbygg) | Predikert besparelse **1,5–3×** realisert; ~⅓ av LEED-bygg bruker mer energi | "Mind the energy performance gap", ScienceDirect | +| Måleterskel | Besparelse bør overstige **~10 % av baseline** for å skilles pålitelig fra støy | FEMP/RDH M&V-veiledning | + +## Systematiske årsaker (hvorfor faktisk < modellert) [V] + +1. **Driftstimer / Hours-of-Use** — dominerende. Timeplan-baserte estimat (det Option A + stipulerer) treffer sjelden metret brenntid (3 053 vs 3 772). +2. **Baseline- og værjustering** — over-predikert baseline blåser opp absolutt besparelse. +3. **Coincidence / diversity factor** — for effekt(kW): andel last under nett-topp ~0,57–0,72, ikke 1,0. +4. **HVAC interactive effects** — mindre spillvarme → endret kjøle-/varmebehov; «too small to measure», stipuleres. +5. **In-service rate, drift & persistens** — ikke alt installeres/forblir; styringer overstyres; degradering. +6. **Måleusikkerhet** — under ~10 %-terskelen drukner signalet i støy. +7. **Rebound / atferd** — mer lys på, lengre, fordi det «koster mindre». + +## Kilder (URL) + +- EVO IPMVP Generally Accepted M&V Principles (okt. 2018): https://evo-world.org/images/corporate_documents/IPMVP-Generally-Accepted-Principles_Final_26OCT2018.pdf +- DOE/NREL Uniform Methods Project, Ch. 2 Commercial & Industrial Lighting (NREL 68558): https://docs.nrel.gov/docs/fy17osti/68558.pdf +- Massachusetts Impact Evaluation of 2010 Prescriptive Lighting: https://ma-eeac.org/wp-content/uploads/Impact-Evaluation-of-2010-Prescriptive-Lighting-Installations-Final-Report-6-21-13.pdf +- National Grid SBS 2010 Prescriptive Lighting (DNV KEMA): https://www.nationalgridus.com/media/pdfs/our-company/eereports/2014-ngrid-sbs-impact-eval-final-prot.pdf +- Efficiency Maine Retail & Distributor Lighting 2021: https://www.efficiencymaine.com/docs/Retail-and-Distributor-Lighting-Final-Impact-Evaluation-Report-2021.pdf +- LADWP Retrospective Impact Evaluation FY15/16–19/20: https://www.ladwp.com/sites/default/files/2024-01/LADWP%20Retrospective%20Report%20FINAL%20V4.pdf +- CPUC Resolution E-4952: https://docs.cpuc.ca.gov/publisheddocs/published/g000/m232/k459/232459122.pdf +- "Mind the energy performance gap" (ScienceDirect): https://www.sciencedirect.com/science/article/abs/pii/S0921344918303860 +- SSB Elektrisitetspriser (kraftpris tjenesteytende næringer, Q1 2026): https://www.ssb.no/energi-og-industri/energi/statistikk/elektrisitetspriser diff --git a/shared/examples/bygg-energi-mikro/metode-ipmvp-a.md b/shared/examples/bygg-energi-mikro/metode-ipmvp-a.md new file mode 100644 index 0000000..687bccb --- /dev/null +++ b/shared/examples/bygg-energi-mikro/metode-ipmvp-a.md @@ -0,0 +1,40 @@ +--- +type: methodology +title: "IPMVP Option A — Retrofit Isolation, Key Parameter Measurement" +description: "M&V-metoden for å verifisere besparelsen fra ett isolert tiltak ved å måle nøkkelparameteren (effekt) og estimere resten (driftstimer)." +methodology: IPMVP +option: A +tags: [IPMVP, M&V, EVO, retrofit-isolation] +timestamp: 2026-06-29 +--- + +# M&V-metode: IPMVP Option A + +**IPMVP** (International Performance Measurement and Verification Protocol) er +konsensus-rammeverket for å måle og verifisere energibesparelser, eid og vedlikeholdt av +**EVO** (Efficiency Valuation Organization). Kjerneinnsikten som begrunner hele +lærings-sløyfa står eksplisitt i protokollen [V]: + +> *"Savings cannot be directly measured, because savings represent the absence of energy use."* + +Besparelse er en **kontrafaktisk** størrelse — det finnes ingen måler for «det som ikke ble +brukt». Den *beregnes*: `Baseline-energi − Rapporterings-energi ± justeringer` (IPMVP Eq. 1). + +## De fire opsjonene (EVO, offisielle navn) [V] + +- **Option A — Retrofit Isolation: Key Parameter Measurement.** Måler nøkkelparameteren + (typisk effekt) på det berørte utstyret; øvrige parametere (typisk driftstimer) *estimeres*. +- **Option B — Retrofit Isolation: All Parameter Measurement.** Måler alle relevante parametere. +- **Option C — Whole Facility.** Besparelse fra byggets hovedmåler, med rutinejustering (vær/produksjon). +- **Option D — Calibrated Simulation.** Besparelse via simuleringsmodell kalibrert mot måledata. + +## Hvorfor Option A for dette tiltaket [V] + +EVOs egen tabell bruker nettopp et **lysarmatur-retrofit** som den kanoniske Option A-saken: +effekt før/etter måles (billig, presist), mens **driftstimer stipuleres** fra byggets +timeplan. Det gjør Option A enklest og billigst for ett isolert tiltak. + +**Kritisk for lærings-overflaten:** parameteren Option A tillater å *estimere* — driftstimer +— er nøyaktig der realiseringsgapet oppstår. Den stipulerte timeplanen treffer sjelden den +faktiske, metrede brenntiden. Se [verdict-led-fro.md](verdict-led-fro.md) og +[kilder-realiseringsgap.md](kilder-realiseringsgap.md). diff --git a/shared/examples/bygg-energi-mikro/tiltak-led-retrofit.md b/shared/examples/bygg-energi-mikro/tiltak-led-retrofit.md new file mode 100644 index 0000000..fd96bae --- /dev/null +++ b/shared/examples/bygg-energi-mikro/tiltak-led-retrofit.md @@ -0,0 +1,66 @@ +--- +type: hypothesis +title: "LED-retrofit av kontorbelysning" +description: "Bytte 200 lysrørarmaturer (3-rørs T8 troffer, ~90 W) til LED-paneler (~40 W). Kandidat-tiltak med modellert besparelse og usikkerhet." +resource: BYGG-KONTOR-NORD +measure_id: LED-RETROFIT-01 +tags: [LED, belysning, retrofit, ECM] +timestamp: 2026-06-29 +--- + +# Tiltak: LED-retrofit av kontorbelysning + +Bytte av 200 lysrørarmaturer (2×4 fluorescerende troffer) til LED-paneler. Dette er det +vanligste enkelt-ECM-et (Energy Conservation Measure) og IPMVPs egen kanoniske +Option A-illustrasjon — se [metode-ipmvp-a.md](metode-ipmvp-a.md). + +## Parametere + +| Parameter | Verdi | Status | Kilde/forankring | +|---|---|---|---| +| Antall armaturer | 200 | [I] | mikro-skala valgt | +| Effekt før (T8 troffer m/ ballast) | 90 W | [V] | 3×32 W ≈ 90–96 W m/ ballastfaktor | +| Effekt etter (LED-panel) | 40 W | [V] | kommersielt 2×4 LED-panel ~40 W | +| Reduksjon per armatur (ΔW) | 50 W | beregnet | 90 − 40 | +| Driftstimer (HOU) | 3 000 t/år | [I] | forankret i metret 3 053 t (Efficiency Maine) | +| Variabel energipris | 1,00 NOK/kWh | [V-forankret] | se [bygg-kontor-nord.md](bygg-kontor-nord.md) | + +## Modellert besparelse (ex-ante) + +Lysligning (DOE/NREL Uniform Methods Project, Eq. 3): +`kWh = Σ (W_før − W_etter) × antall × HOU / 1000` + +> ΔW = 90 − 40 = **50 W/armatur** +> kWh/år = 50 × 200 × 3 000 / 1 000 = **30 000 kWh/år** +> kr/år = 30 000 × 1,00 = **30 000 NOK/år** + +HVAC-interaktiv effekt (effektivt lys → mindre spillvarme → endret kjøle-/varmebehov, +UMP Eq. 6) er ~+5 % i elektrisk kjølte bygg, men **utelatt fra kjernetallet** her +(konservativt; den lille interaktive justeringen er en ex-post-vurdering eksperten kan +legge til). Modellert kjernebesparelse: **30 000 kWh/år ≈ 30 000 NOK/år**. + +## Usikkerhet (for Monte Carlo P10/P50/P90) + +Den dominerende usikkerheten i en *energibesparelse* ligger i driftstimer (HOU), ikke +prisen — men den eksisterende validatorens Monte Carlo varierer enhetspris. I denne +mikro-mappingen brukes derfor **prisbandet 0,70–1,40 NOK/kWh** som usikkerhetsakse +(region/sesong, jf. [bygg-kontor-nord.md](bygg-kontor-nord.md)). Den fysiske HOU-usikkerheten +og — viktigere — den *systematiske* HOU-skjevheten håndteres i verdict-laget, ikke her. + +## Mapping til validatoren (hvorfor `validator-input.json` ser ut som den gjør) + +Den eksisterende deterministiske validatoren er en *feasibility-gate* (`claimed ≤ 30 % av +affected total`, Monte Carlo over enhetspris) bygd for kostnadskutt. Energitiltaket mappes +inn **uendret**: + +- `affected_items = [{code: "ENERGI-TOTAL-EL", quantity: 300000 kWh/år, unit_cost: 1.00 NOK/kWh}]` + → byggets **totale** årlige energikostnad (300 000 NOK). LED-besparelsen er ~10 % av den, + godt innenfor 30 %-cap-en. +- `claimed_saving_nok = 30000` → den modellerte LED-besparelsen. +- `assumptions = {"ENERGI-TOTAL-EL": [0.70, 1.40]}` → prisbandet for Monte Carlo. + +**Ærlig begrensning:** validatorens P10/P50/P90 betyr her «øvre feasible grense» (30 % av +samplet energikostnad), *ikke* «LED-besparelsens fysiske band». Det er bevisst — den +domenetro besparelses-modelleringen og realiseringsgapet hører hjemme i verdict-laget +([verdict-led-fro.md](verdict-led-fro.md)), som er nettopp det lærings-sløyfa skal lære. +En energi-bevisst validator (ΔW × antall × HOU) er senere fase-arbeid, ikke dette fixturet. diff --git a/shared/examples/bygg-energi-mikro/validator-input.json b/shared/examples/bygg-energi-mikro/validator-input.json new file mode 100644 index 0000000..642a1fb --- /dev/null +++ b/shared/examples/bygg-energi-mikro/validator-input.json @@ -0,0 +1,16 @@ +{ + "_note": "IR-projeksjon (ir.SavingsProposal) for det eksisterende deterministiske validatoren. Energitiltaket er mappet inn i kost-IR-en UENDRET: affected_items = byggets totale arlige energikostnad; claimed_saving_nok = modellert LED-besparelse (~10% av total, innenfor 30%-cap); assumptions = energipris-band (NOK/kWh) for Monte Carlo. Se tiltak-led-retrofit.md, seksjon 'Mapping til validatoren'.", + "project_id": "BYGG-KONTOR-NORD", + "measure": "LED-retrofit av 200 lysrorarmaturer (90 W -> 40 W) i kontorlokaler", + "affected_items": [ + { + "code": "ENERGI-TOTAL-EL", + "quantity": 300000, + "unit_cost": 1.0 + } + ], + "claimed_saving_nok": 30000, + "assumptions": { + "ENERGI-TOTAL-EL": [0.70, 1.40] + } +} diff --git a/shared/examples/bygg-energi-mikro/verdict-led-fro.md b/shared/examples/bygg-energi-mikro/verdict-led-fro.md new file mode 100644 index 0000000..c33d5c2 --- /dev/null +++ b/shared/examples/bygg-energi-mikro/verdict-led-fro.md @@ -0,0 +1,56 @@ +--- +type: verdict +title: "Ekspert-dom (frø): LED-retrofit — godkjent med realiseringskorreksjon" +description: "Frøsatt ekspert-dom som koder realiseringsgapet for LED-tiltaket. ExpeL-frøet loopens steg 1 henter fra: modellert besparelse korrigeres ned med realiseringsgraden eksperten kjenner fra drift." +resource: BYGG-KONTOR-NORD +measure_id: LED-RETROFIT-01 +decision: approved_with_adjustment +realization_rate: 0.82 +modelled_saving_nok: 30000 +expected_actual_saving_nok: 24600 +gap_source: hours-of-use-overestimation +context_key: "kontorbygg; HOU-kilde=timeplan-stipulert" +provenance: "frø — AI-forfattet, forankret i National Grid SBS 2010 (RR 0,81); erstattes av ekte HITL i produksjon" +tags: [verdict, realization-rate, ExpeL-seed, HITL] +timestamp: 2026-06-29 +--- + +# Ekspert-dom (frø): LED-retrofit + +> **Dette er et frø**, ikke en ekte dom. I simulering gir en ekspert-persona slike dommer; +> i produksjon gir et menneske dem via samme mappe-grensesnitt. Frøet er forankret i +> verifisert litteratur ([kilder-realiseringsgap.md](kilder-realiseringsgap.md)), ikke +> oppdiktet. Det er **provenance-merket** og promoteres til wikien kun fordi en (simulert) +> ekspert har godkjent det (målbilde, steg 8 gated). + +## Dommen + +**Beslutning:** godkjent — med realiseringskorreksjon. + +Den modellerte besparelsen (**30 000 NOK/år**) er teknisk korrekt fra parameterne, og +validatoren bekrefter at den er innenfor feasibelt område. Men i drift realiseres erfaringsvis +**~82 %** av en timeplan-stipulert LED-besparelse i kontorbygg → forventet faktisk +besparelse **≈ 24 600 NOK/år**. + +## Begrunnelse (det validatoren ikke kan regne) + +Realiseringsgapet her er **ikke** spredning i parameterne — det er en *systematisk skjevhet* +mellom de stipulerte parameterne og driftsvirkeligheten: + +- **Driftstimer overvurdert (dominerende):** den antatte timeplanen (3 000 t) ligger over + faktisk metret brenntid — naturlig dagslys, tomme rom, sensorstyring. Jf. National Grid + (RR-bidrag 0,81 fra 15 % lavere metrede timer) og Efficiency Maine (3 053 vs 3 772 t). +- **In-service rate < 1:** ikke alle 200 armaturer er nødvendigvis montert/i drift ved + evaluering. +- **Atferd/persistens:** rebound (mer lys på fordi det «er gratis») og overstyrte styringer. + +Du kan **ikke** regne deg til RR = 0,82 fra `{200, 90 W, 40 W, 3 000 t, 1,00 NOK/kWh}`. +Skjevheten er epistemikk parameterne ikke bærer — den finnes bare i akkumulert drifts-erfaring. + +## Lærings-signalet (ExpeL) + +Korreksjonen er **kontekstbetinget**: `context_key = "kontorbygg; HOU-kilde=timeplan-stipulert"`. +Neste kjøring, gitt en lignende hypotese i samme kontekst, skal hente denne dommen og +justere den modellerte ex-ante-besparelsen mot forventet ex-post (≈ 0,82×) — uten å vente +på 12 måneders måling. **Det er denne dataflyten — verdict inn i neste hypotese — loopens +steg 1 må wire (målbilde §5, §7). Dette frøet er testankeret.** diff --git a/tests/test_bygg_energi_mikro.py b/tests/test_bygg_energi_mikro.py new file mode 100644 index 0000000..9a5e3a5 --- /dev/null +++ b/tests/test_bygg_energi_mikro.py @@ -0,0 +1,123 @@ +"""Bygg-energi mikro-eksempel — fixture-konsumerbarhet (shared/examples/bygg-energi-mikro). + +The energi micro-fixture is a *dev fixture* exercised through the whole build. These tests +prove it is actually consumable, not merely present: + +1. the IR projection (``validator-input.json``) drives the EXISTING deterministic validator to + a ``ValidatedProposal`` (not a ``Rejection``) — energi mapped into the cost-IR unchanged; +2. that validator output is frozen against ``golden.json`` (seeded ``_MC_SEED=20260624``); +3. the OKF bundle is well-formed (every file declares ``type``; index cross-links resolve); +4. the seed verdict encodes a realization gap (RR < 1) consistent with the golden — the anchor + the later step-1 ExpeL wiring becomes load-bearing against. + +No ``yaml`` dependency: frontmatter is parsed with a minimal key:value reader. +""" + +from __future__ import annotations + +import json +import re +from pathlib import Path + +import pytest + +from portfolio_optimiser.ir import AffectedItem, SavingsProposal +from portfolio_optimiser.validator import Rejection, ValidatedProposal, validate_proposal + +BUNDLE_DIR = Path(__file__).resolve().parents[1] / "shared" / "examples" / "bygg-energi-mikro" + +_EXPECTED_TYPES = { + "index.md": "index", + "bygg-kontor-nord.md": "project", + "tiltak-led-retrofit.md": "hypothesis", + "metode-ipmvp-a.md": "methodology", + "kilder-realiseringsgap.md": "reference", + "verdict-led-fro.md": "verdict", +} + + +def _parse_frontmatter(path: Path) -> dict[str, str]: + """Minimal YAML-frontmatter reader: the ``---``-delimited leading block as key:value + strings. Enough for ``type`` and the verdict's scalar fields; list values (``tags: [...]``) + are kept verbatim and unused.""" + lines = path.read_text(encoding="utf-8").splitlines() + if not lines or lines[0].strip() != "---": + return {} + fm: dict[str, str] = {} + for line in lines[1:]: + if line.strip() == "---": + break + key, sep, val = line.partition(":") + if sep: + fm[key.strip()] = val.strip() + return fm + + +def _proposal_from_input() -> SavingsProposal: + raw = json.loads((BUNDLE_DIR / "validator-input.json").read_text(encoding="utf-8")) + return SavingsProposal( + project_id=raw["project_id"], + measure=raw["measure"], + affected_items=[AffectedItem(**a) for a in raw["affected_items"]], + claimed_saving_nok=raw["claimed_saving_nok"], + assumptions={k: tuple(v) for k, v in raw["assumptions"].items()}, + ) + + +def _golden() -> dict: + return json.loads((BUNDLE_DIR / "golden.json").read_text(encoding="utf-8")) + + +def test_validator_input_validates() -> None: + """The core: the energi tiltak, mapped into the cost-IR, passes the blocking validator — + a ``ValidatedProposal``, never a ``Rejection`` (claimed 30k < P90 feasible).""" + result = validate_proposal(_proposal_from_input()) + assert isinstance(result, ValidatedProposal) + assert not isinstance(result, Rejection) + assert result.proposal.claimed_saving_nok < result.p90 + assert result.p10 <= result.p50 <= result.p90 + + +def test_validator_output_matches_golden() -> None: + """Regression: the deterministic (seeded) percentiles match the frozen golden.""" + result = validate_proposal(_proposal_from_input()) + assert isinstance(result, ValidatedProposal) + g = _golden()["validator"] + assert g["validates"] is True + assert result.nominal_feasible == pytest.approx(g["nominal_feasible"]) + assert result.p10 == pytest.approx(g["p10"]) + assert result.p50 == pytest.approx(g["p50"]) + assert result.p90 == pytest.approx(g["p90"]) + + +def test_bundle_files_declare_type() -> None: + """Every OKF bundle file carries the one required field (``type``) with the expected value.""" + for name, expected_type in _EXPECTED_TYPES.items(): + fm = _parse_frontmatter(BUNDLE_DIR / name) + assert fm.get("type") == expected_type, f"{name}: type={fm.get('type')!r}" + + +def test_index_crosslinks_resolve() -> None: + """Intra-bundle cross-links in ``index.md`` resolve to existing files (our own example + must have no broken links, even though OKF consumers must tolerate them).""" + index = (BUNDLE_DIR / "index.md").read_text(encoding="utf-8") + targets = re.findall(r"\]\(([^)]+\.md)\)", index) + internal = [t for t in targets if "/" not in t] + assert internal, "expected intra-bundle .md links in index.md" + for target in internal: + assert (BUNDLE_DIR / target).exists(), f"index links to missing {target}" + + +def test_verdict_seed_encodes_realization_gap() -> None: + """The seed verdict encodes a realization gap (RR < 1), and the golden learning-surface is + internally consistent (expected_actual = RR x modelled). This is the anchor the later + step-1 ExpeL wiring is made load-bearing against.""" + fm = _parse_frontmatter(BUNDLE_DIR / "verdict-led-fro.md") + rr = float(fm["realization_rate"]) + assert 0.0 < rr < 1.0, "a realization gap means RR strictly below 1" + + ls = _golden()["learning_surface"] + assert ls["realization_rate"] == pytest.approx(rr) + assert ls["expected_actual_saving_nok"] == pytest.approx( + ls["realization_rate"] * ls["modelled_saving_nok"] + )