diff --git a/.claude/skills/epc-to-elmhurst-rdsap-inputs/SKILL.md b/.claude/skills/epc-to-elmhurst-rdsap-inputs/SKILL.md index 56cfc72b..65fa7526 100644 --- a/.claude/skills/epc-to-elmhurst-rdsap-inputs/SKILL.md +++ b/.claude/skills/epc-to-elmhurst-rdsap-inputs/SKILL.md @@ -53,10 +53,20 @@ mapping reference must be looked up in the cited source file, not invented. useful the **EES dropdown path** and **SAP code**. Use the lookup tables and gotchas in [reference/mapping.md](reference/mapping.md). -4. **Save** the file next to the cert json as `elmhurst_inputs.md` +4. **Flag what must NOT be mapped / must be cleared.** Elmhurst pages carry + defaults and (when re-keying into an existing assessment) stale values that + silently survive and corrupt the result — a phantom 1st extension, a + room-in-roof area, a party-wall length, leftover window rows. For every + element the cert does **not** have, say so explicitly with the Elmhurst + field to blank and the value to set (usually empty or 0). The automated + filler (`scripts/hyde/elmhurst_fill.py`) and a human both rely on this list + — an unflagged absent element defaults to "present" and skews SAP. See the + **Fields to clear** section in Output shape below. + +5. **Save** the file next to the cert json as `elmhurst_inputs.md` (e.g. `.../real_life_examples//uprn_/elmhurst_inputs.md`). -5. **Tell the operator**: key it into Elmhurst, then report the SAP score (and +6. **Tell the operator**: key it into Elmhurst, then report the SAP score (and heating cost £ if shown). If it differs from our engine's score, that's a calculator finding — capture it. @@ -70,6 +80,27 @@ Start the file with a header block: ``` Then the page sections as tables: `| Elmhurst field | Value | Notes (SAP code / EES path) |`. +End the file with a **Fields to clear in Elmhurst (do NOT map)** section — one +table listing every element the cert lacks but Elmhurst defaults or stale data +would otherwise assert. This is the explicit "absent" set the filler and the +operator clear: +``` +## Fields to clear in Elmhurst (do NOT map) +| Elmhurst field | Set to | Why absent | +|---|---|---| +| Property Description · 1st–4th Extension age band | (blank) | no extensions lodged | +| Property Description · Room-in-Roof age band | (blank) | no room-in-roof building part | +| Dimensions · Room(s) in Roof area | (blank) | ditto — leave empty, not 0 | +| Dimensions · 1st–4th Ext. sub-tabs | 0 / blank | single building part | +| Walls · Party wall | none/blank | `party_walls_w_per_k` = 0 | +| Openings · surplus window rows | delete | window count from glazed-area band only | +| Water Heating · cylinder | none | combi / instantaneous | +| Conservatory / PV / Wind / Hydro / WWHRS / FGHRS | none | not lodged | +``` +Give the **Elmhurst page · field**, the **cleared value** (prefer *blank* over +0 where the field is "not applicable"; some validators reject 0), and the +one-line reason. Absent ≠ zero everywhere — note which. + ## Critical gotchas (full detail in reference) - **Economy-7 / off-peak electricity** (`main_fuel_type`/`water_heating_fuel` 29): @@ -84,6 +115,11 @@ Then the page sections as tables: `| Elmhurst field | Value | Notes (SAP code / - **Water heating** 903 = Electric immersion off-peak → Elmhurst "Water Heater" category, not "Boiler Circulator" (901 = from main system). - **Windows** are synthesised from `glazed_area` band × TFA — not real geometry. +- **Age band — pick by construction YEAR, not the engine's band letter.** The + live RdSAP-10 tool bands differently from `mapping.md`'s older table: it + offers `…K 2007-2011`, **`L 2012-2022`**, **`M 2023 onwards`**. A 2020 build + is **L** even though the engine labels `construction_age_band` "M". Map the + year to Elmhurst's on-screen band; flag the letter divergence. ## Canonical example diff --git a/.claude/skills/expand-sap-accuracy-corpus/SKILL.md b/.claude/skills/expand-sap-accuracy-corpus/SKILL.md new file mode 100644 index 00000000..2507f61e --- /dev/null +++ b/.claude/skills/expand-sap-accuracy-corpus/SKILL.md @@ -0,0 +1,70 @@ +--- +name: expand-sap-accuracy-corpus +description: Drive the per-UPRN SAP-accuracy corpus campaign. For each UPRN ensure the EPC-API→EpcPropertyData mapping exists (extend the mapper if the schema isn't covered), build the cert in Elmhurst (hyde Playwright automation), download its Input Summary + SAP Worksheets, reconcile our SAP calculator to within 0.5 of Elmhurst's accredited worksheet by fixing mapper gaps (not tuning), and pin a RealCertExpectation regression. Use when expanding SAP-calculator schema coverage/accuracy across many real certs, working the 100-UPRN worklist one at a time, validating the EPC mapper against Elmhurst, or when the user mentions the SAP accuracy corpus / Elmhurst worksheets / the UPRN campaign. +--- + +# Expand SAP accuracy corpus + +A campaign wrapper around **`validate-cert-sap-accuracy`**, run **one UPRN at a +time** over [worklist.md](worklist.md). Why a UPRN at a time: we are rewriting / +extending the mapper incrementally, and each pass should (a) widen EPC-schema +coverage, (b) fix one real mapper gap, (c) leave the skills/tests sharper for the +next UPRN. + +**Goal per UPRN:** our SAP calculator within **0.5 SAP** of Elmhurst's +accredited worksheet, the agreed score pinned as a `RealCertExpectation`. + +The big picture: an old EPC → `EpcPropertyData` → our SAP calculator predicts the +score the property would get unchanged. Elmhurst is the accredited ground truth; +its Input Summary (parsed back to `EpcPropertyData`) exposes mapper holes, and its +worksheet exposes calculator holes. + +## The loop (one UPRN) + +1. **Pick** the first `[ ]` UPRN in [worklist.md](worklist.md). +2. **Capture** — `PYTHONPATH=/workspaces/model python scripts/fetch_real_life_epc_sample.py ` + → `epc.json` + schema + engine SAP under `…/real_life_examples//uprn_/`. + - **`NOT MAPPABLE`** (schema uncovered, e.g. full SAP) → this *is* the coverage + work: **extend the mapper** for that schema (`EpcPropertyDataMapper` / per-cert + mapper — Khalim's domain). Mark `🔧 mapper-gap`, build it, re-run. Guard with + the RdSAP-21.0.1 corpus gauge (`test_sap_accuracy_corpus.py`). +3. **Inputs** — run the **`epc-to-elmhurst-rdsap-inputs`** skill on the UPRN → + complete `elmhurst_inputs.md` (+ its **Fields to clear** section). +4. **Build in Elmhurst** (`scripts/hyde/`, mechanics in `elmhurst_README.md`): + `bash scripts/hyde/start_viewer.sh` (noVNC on :99) → log in once + (`elmhurst_session.py login`) → **create a NEW assessment for this UPRN** → + fill from `elmhurst_inputs.md`: `elmhurst_fill.py` auto-fills the derivable + pages (incl. the boiler cascade) + the clears; key the 🔸 fields by hand. Then + `elmhurst_download.py` saves **`elmhurst_summary.pdf`** + **`elmhurst_worksheet.pdf`** + into the sample dir. ⚠ The filler hard-codes one assessment + one cert's values + today — see **Limitations**. +5. **Compare** — `PYTHONPATH=/workspaces/model python scripts/compare_epc_paths.py ` + → deep-diffs gov-API vs Elmhurst-parsed `EpcPropertyData`, runs **both** through + the engine, and prints Elmhurst's own worksheet SAP. Read it: engine-on-Elmhurst- + inputs ≈ worksheet ⇒ calculator faithful; gov-API vs Elmhurst gap ⇒ input diffs. +6. **Reconcile to ≤0.5** — triage every field diff (use the + `epc-to-elmhurst-rdsap-inputs` `reference/mapping.md` for code semantics): + - **Elmhurst data-entry error** → fix in Elmhurst, re-export, re-run step 5. + - **gov-API mapper gap** → the leverage point. Fix the mapper **generically**; + never tune to one cert. Cosmetic diffs (codes vs strings, empty lists) are noise. + - **Genuine ground-truth** → align both sides to the lodged data. +7. **Pin** — add a `RealCertExpectation` to + `tests/domain/sap10_calculator/test_real_cert_sap_accuracy.py`; run it. If a + known engine bug still blocks, use `known_bug_xfail="…"` (strict xfail) — don't widen. +8. **Bank** — fold what you learned into `mapping.md` / this skill / the tests so + the next UPRN is easier, and set the worklist status (see legend). + +## Worklist status legend +`[ ]` todo · `[x]` pinned (≤0.5) · `🔧` mapper extended · `⚠` xfail (engine bug) · +`⛔` blocked. Annotate each done line: `— · eng / elm · `. + +## Limitations / next improvements (make the campaign scale) +- **Per-assessment GUID** — `elmhurst_fill.py` / `elmhurst_download.py` hard-code + `ASSESSMENT_GUID` (the Khalim-test cert). Each UPRN needs its own Elmhurst + assessment; create one and update the GUID, or parameterize it (`--guid`). +- **Cert-specific fill values** — `PAGES` in `elmhurst_fill.py` hold one cert's + values. Make it **data-driven** (read field values from each cert's + `elmhurst_inputs.md` or a JSON sidecar) so one filler serves all UPRNs. Until + then, build each cert guided by its `elmhurst_inputs.md` (the filler is a template). +- **More dialogs** — boiler is automated (cascade); apply the same + open→select→OK pattern to the control code (2110) and the MV unit when wanted. diff --git a/.claude/skills/expand-sap-accuracy-corpus/start-prompt.md b/.claude/skills/expand-sap-accuracy-corpus/start-prompt.md new file mode 100644 index 00000000..16127a59 --- /dev/null +++ b/.claude/skills/expand-sap-accuracy-corpus/start-prompt.md @@ -0,0 +1,39 @@ +# Start prompt — run the corpus campaign one UPRN at a time + +Paste the block below into Claude to process the **next** UPRN. It does exactly +one UPRN and stops; reply **"next"** (or re-paste) to advance. Reuses the single +Elmhurst assessment/GUID and overwrites it each time — the EPC-API ↔ Input-Summary +diff in the Compare step is the safety net for leftover state. + +--- + +``` +Run the `expand-sap-accuracy-corpus` skill to process the NEXT UPRN, ONE at a time. + +1. Open `.claude/skills/expand-sap-accuracy-corpus/worklist.md` and take the first `[ ]` UPRN. +2. Run that single UPRN through the skill's loop: + - Capture: `PYTHONPATH=/workspaces/model python scripts/fetch_real_life_epc_sample.py `. + If it prints NOT MAPPABLE (schema uncovered), that's the job — extend the + EpcPropertyDataMapper for that schema generically, then re-run. Guard with the + RdSAP-21.0.1 corpus gauge. + - Inputs: run the `epc-to-elmhurst-rdsap-inputs` skill on the UPRN → complete + `elmhurst_inputs.md` (with the "Fields to clear" section). + - Build in Elmhurst, REUSING the existing assessment: + GUID B44A0DB4-4C08-4241-B818-86F060172105 (the "Khalim-test" cert). + `bash scripts/hyde/start_viewer.sh` (noVNC on :99). The filler's PAGES values + are from the previous cert — update them to THIS cert's values from the new + `elmhurst_inputs.md` (it's one-at-a-time, so editing the specs is fine), set + AND clear every field (overwrite the prior UPRN's state — use the Fields-to-clear + list), and key the 🔸 dialog fields by hand. Then run `elmhurst_download.py` + to save `elmhurst_summary.pdf` + `elmhurst_worksheet.pdf` into the sample dir. + NEVER click Submit. + - Compare: `PYTHONPATH=/workspaces/model python scripts/compare_epc_paths.py `. + - Reconcile to ≤0.5 SAP: fix mapper gaps GENERICALLY (never tune to one cert); + Elmhurst's Input Summary diff localises them. Cosmetic diffs are noise. + - Pin a RealCertExpectation in + tests/domain/sap10_calculator/test_real_cert_sap_accuracy.py and run it. + Use strict known_bug_xfail for a known engine bug instead of widening. + - Update that worklist line: [x]/🔧/⚠/⛔ — · eng / elm · . +3. STOP after this one UPRN. Report: schema, any mapper changes, engine vs Elmhurst + SAP and what reconciled them, and the test result. Then wait for me to say "next". +``` diff --git a/.claude/skills/expand-sap-accuracy-corpus/worklist.md b/.claude/skills/expand-sap-accuracy-corpus/worklist.md new file mode 100644 index 00000000..6794e933 --- /dev/null +++ b/.claude/skills/expand-sap-accuracy-corpus/worklist.md @@ -0,0 +1,114 @@ +# SAP accuracy corpus — UPRN worklist + +Process **one at a time**, top to bottom (see [SKILL.md](SKILL.md)). After each +UPRN, tick it and annotate: `— · eng / elm · `. + +**Legend:** `[ ]` todo · `[x]` pinned (≤0.5) · `🔧` mapper extended · `⚠` xfail +(engine bug) · `⛔` blocked. + +**Worked reference (not in the 100):** `uprn_10092973954` (SAP-Schema-17.1, +2020 new-build flat) — full loop proven: eng 77 / elm 78, engine-on-Elmhurst- +inputs 79 (calculator faithful within ~1). Use it to sanity-check the pipeline. + +## The 100 + +- [ ] 10093116528 +- [ ] 10093116543 +- [ ] 10093116529 +- [ ] 100020933699 +- [ ] 44012843 +- [ ] 10023444324 +- [ ] 10092970673 +- [ ] 10094601287 +- [ ] 10090844932 +- [ ] 10090844948 +- [ ] 100090182288 +- [ ] 10093114053 +- [ ] 10091568921 +- [ ] 10093718424 +- [ ] 10022893721 +- [ ] 10023443426 +- [ ] 10093412452 +- [ ] 10014314798 +- [ ] 10094601294 +- [ ] 10090343335 +- [ ] 10093115480 +- [ ] 68151071 +- [ ] 100021985993 +- [ ] 100020665611 +- [ ] 10093388044 +- [ ] 10090944225 +- [ ] 10090341811 +- [ ] 10010215568 +- [ ] 10093117227 +- [ ] 10023444170 +- [ ] 100020980961 +- [ ] 200003714056 +- [ ] 10094601280 +- [ ] 10093386418 +- [ ] 100090108846 +- [ ] 10093116324 +- [ ] 10094895444 +- [ ] 10092973960 +- [ ] 10012028763 +- [ ] 10093049867 +- [ ] 10093116336 +- [ ] 100020235156 +- [ ] 10093116334 +- [ ] 10014314853 +- [ ] 10013924849 +- [ ] 100062116493 +- [ ] 10091636116 +- [ ] 10093049853 +- [ ] 10093390790 +- [ ] 10093116330 +- [ ] 10093116326 +- [ ] 10090317693 +- [ ] 10090034872 +- [ ] 10093115985 +- [ ] 10023230742 +- [ ] 10009432998 +- [ ] 100022015916 +- [ ] 202211170 +- [ ] 200003400077 +- [ ] 100061795739 +- [ ] 10093305101 +- [ ] 100020933894 +- [ ] 100020937013 +- [ ] 10023444320 +- [ ] 100062188801 +- [ ] 10008048040 +- [ ] 10093101966 +- [ ] 10093084691 +- [ ] 100061086424 +- [ ] 10023443568 +- [ ] 100090182702 +- [ ] 10090342180 +- [ ] 200003398613 +- [ ] 10093718415 +- [ ] 100061850726 +- [ ] 10091636031 +- [ ] 10014314830 +- [ ] 202211161 +- [ ] 10094895409 +- [ ] 100060930947 +- [ ] 10093388053 +- [ ] 100020973465 +- [ ] 100062190000 +- [ ] 10093114178 +- [ ] 10093303593 +- [ ] 10093303465 +- [ ] 10091194525 +- [ ] 10093386427 +- [ ] 10002917849 +- [ ] 100020212302 +- [ ] 10012028772 +- [ ] 10093083532 +- [ ] 10090034761 +- [ ] 10070622696 +- [ ] 200003725383 +- [ ] 10093387673 +- [ ] 10093412389 +- [ ] 10090343767 +- [ ] 10090845805 +- [ ] 10013924858 diff --git a/.claude/skills/validate-cert-sap-accuracy/SKILL.md b/.claude/skills/validate-cert-sap-accuracy/SKILL.md index be3a6283..a7d84c46 100644 --- a/.claude/skills/validate-cert-sap-accuracy/SKILL.md +++ b/.claude/skills/validate-cert-sap-accuracy/SKILL.md @@ -30,6 +30,14 @@ Sample home for every cert: `backend/epc_api/json_samples/real_life_examples/ None: diff --git a/backend/epc_api/json_samples/real_life_examples/RdSAP-Schema-20.0.0/uprn_60027826/elmhurst_inputs.md b/backend/epc_api/json_samples/real_life_examples/RdSAP-Schema-20.0.0/uprn_60027826/elmhurst_inputs.md new file mode 100644 index 00000000..3d479b91 --- /dev/null +++ b/backend/epc_api/json_samples/real_life_examples/RdSAP-Schema-20.0.0/uprn_60027826/elmhurst_inputs.md @@ -0,0 +1,182 @@ +# Elmhurst RdSAP inputs — UPRN 60027826 (cert 0080-2673-2086-2299-1061, RdSAP-Schema-20.0.0) + +**Lodged SAP:** 72 **Our engine:** 70 ← compare Elmhurst against **70** (the production gov-API path) +**Known divergences:** None of the off-peak Economy-7 bugs apply — this is a **mains-gas** boiler with **mains-gas** hot water. Watch instead: (a) RdSAP-20 synthesised-window U translation, (b) the room-in-roof entry, (c) the unlodged party wall defaulting to U=0.25. + +10 Kingsland Close, Shoreham-by-Sea, BN43 6NQ · Semi-detached house · inspected 2021-08-17, lodged 2022-04-13. + +--- + +## Property Description + +| Elmhurst field | Value | Notes (SAP code / EES path) | +|---|---|---| +| Property Type | House | `property_type` 0 | +| Built Form | Semi-detached | `built_form` 2 | +| Age Band (England & Wales) | **H — 1991–1995** | `construction_age_band` H | +| Storeys | **3** | ground + first + room-in-roof (intermediate `storey_count`=3.0) | +| Habitable rooms | 6 | `habitable_room_count` | +| Heated rooms | 6 | `heated_room_count` | +| Extensions | 0 | `extensions_count` | +| Rooms in roof | **Yes — 1** | `sap_room_in_roof` lodged (see Roofs) | + +## Dimensions (`measurement_type` 1 = Internal) + +| Floor | Area m² | Room Height m | Heat-Loss Perimeter m | Party-Wall Length m | +|---|---|---|---|---| +| Ground (floor 0) | **42.85** | 2.3 | 21.7 | 6 | +| First (floor 1) | **36** | 2.3 | 18 | 6 | +| Room-in-roof | **36** | (entered on Rooms-in-Roof page, not as a dimension floor) | — | — | + +TFA check: 42.85 + 36 + 36 = **114.85 m²** (lodged `total_floor_area` 115). No heated basement. + +## Conservatory + +None — `conservatory_type` 1, `has_heated_separate_conservatory` false → leave unchecked. + +## Walls + +**Main / external wall** +| Elmhurst field | Value | Notes | +|---|---|---| +| Construction | **Cavity wall** | `wall_construction` 4; description "Cavity wall, filled cavity" | +| Insulation | **Filled cavity** | `wall_insulation_type` 2 (retrofit cavity fill) | +| Dry-lined | **Yes** | `wall_dry_lined` Y | +| Wall Thickness | **300 mm** | `wall_thickness` 300; `wall_thickness_measured` Y → "Wall Thickness Unknown" unchecked | +| (cross-check) | lodged `wall_u_value` 0.33; engine `walls_w_per_k` 26.27 | | + +No alternative wall lodged. + +**Party wall** — `party_wall_construction` **0 (not lodged)** +| Elmhurst field | Value | Notes | +|---|---|---| +| Type | **Unable to determine** | code 0 → engine default **U = 0.25** (NOT the code-1 "Solid" trap — here it is genuinely unlodged, so "Unable to determine" is correct and matches the engine). Engine `party_walls_w_per_k` 7.275 ≈ 29 m² × 0.25. | + +## Roofs + +| Elmhurst field | Value | Notes | +|---|---|---| +| Type | **Room in roof** (pitched, slates/tiles) | description "Roof room(s), insulated (assumed)"; `roof_construction` 4 | +| Rooms-in-roof area | **36 m²** | `sap_room_in_roof.floor_area` 36 | +| Rooms-in-roof insulation | **As built** | `sap_room_in_roof.insulation` "AB"; age band H; `roof_room_connected` N | +| Roof insulation location | **At joists** | `roof_insulation_location` 2 | +| Roof insulation thickness | **100 mm** | `roof_insulation_thickness` "100mm" (drives default U) | +| (cross-check) | engine `roof_w_per_k` 24.17 | | + +## Floors + +| Elmhurst field | Value | Notes | +|---|---|---| +| Construction | **Solid** | `floor_construction` 1; description "Solid, no insulation (assumed)" | +| Insulation | **As built** (none) | `floor_insulation_thickness` "NI" | +| (cross-check) | engine `floor_w_per_k` 30.0 | | + +## Openings — Windows (RdSAP reduced-data, synthesised) + +Glazed-area band = **Normal** (`glazed_area` 1, ×1.00). Synthesised total glazing area += 0.148 × 114.85 × 1.00 = **17.02 m²**, split 4-way N/E/S/W (4 windows × 4.255 m wide × 1.0 m high). + +| Elmhurst field | Value | Notes | +|---|---|---| +| Glazing Type | **Double, unknown install date** | `multiple_glazing_type` 3; "Fully double glazed" | +| Frame Type | **PVC** | `pvc_window_frames` true | +| Glazing Gap | **16 mm** | `glazing_gap` "16+" | +| Orientation | spread evenly N / E / S / W | not lodged — matches engine synth (orients 1,3,5,7) | +| Location | External wall | | +| Draught Proofed | **Yes (100%)** | `percent_draughtproofed` 100 | +| U-value / g-value | leave Elmhurst default; **lodged U = 2.6, g = 0.76** | `windows_transmission_details`; engine effective U = `windows_w_per_k` 42.86 ÷ 17.02 ≈ **2.52**. ⚠️ Report the U Elmhurst assigns — RdSAP-20 glazing-coefficient translation is the most likely source of the lodged-72-vs-engine-70 gap. | + +## Openings — Doors + +| Elmhurst field | Value | Notes | +|---|---|---| +| Total doors | **2** | `door_count` 2 | +| Insulated | **0** | `insulated_door_count` 0 | +| Draught proofed | 2 | `percent_draughtproofed` 100 → engine `doors_w_per_k` 11.1 ≈ 2 × 1.85 m² × 3.0 | + +## Ventilation & Lighting + +| Elmhurst field | Value | Notes | +|---|---|---| +| Open chimneys | 0 | `open_fireplaces_count` 0 | +| Open flues / passive vents / flueless gas / extract fans | 0 | none lodged | +| Fixed air conditioning | No | `has_fixed_air_conditioning` false | +| Mechanical Ventilation | **Natural** (unchecked) | `mechanical_ventilation` 0 | +| Air Pressure Test | **Not available** (uses % draughtproofing) | RdSAP cert | +| Lighting — total fixed outlets | **14** | `fixed_lighting_outlets_count` 14 | +| Lighting — low-energy outlets | **12** | `low_energy_fixed_lighting_outlets_count` 12 (86%) | + +## Space Heating + +**Main Heating 1** — `sap_main_heating_code` **102** +| Elmhurst field | Value | Notes | +|---|---|---| +| System | **Boiler + radiators, mains gas** | "Boiler and radiators, mains gas"; `main_heating_category` 2; `heat_emitter_type` 1 = radiators | +| Boiler type | **Regular condensing, automatic ignition** | code 102 (Table 4b) | +| Efficiency | **84% winter / 74% summer** | Table 4b code 102; engine `main_heating_efficiency` 0.84. `main_heating_data_source` 2 (age-band/Table default, no PCDB record) | +| Fuel | **Mains gas** | `main_fuel_type` 26 (engine space-heat cost 3.48 p/kWh) | +| Flue | Fanned flue present | `boiler_flue_type` 2, `fan_flue_present` Y | +| Controls | **Programmer + room thermostat + TRVs** | `main_heating_control` 2106 | +| % of heat | 100% | `main_heating_fraction` 1 | +| Heat Emitter / Pump Age | leave default (no PCDF) | `central_heating_pump_age` 0 | + +**Main Heating 2** — none (single main system). + +**Secondary Heating** — leave Elmhurst **default (assumed portable electric heaters)**. +`secondary_heating_type` 691; engine applies fraction **0.1** at **standard-tariff electricity** (fuel 30), `secondary_heating_fuel_kwh` ≈ 976 kWh. (Cert lodges `secondary_fuel_type` 29 but the engine prices the assumed secondary at standard tariff per SAP convention — leave the Elmhurst default and it agrees.) + +**Community Heating** — none. + +## Meters + +| Elmhurst field | Value | Notes | +|---|---|---| +| Mains gas supply available | **Yes** | `sap_energy_source.mains_gas` Y | +| Electricity meter type | **Single (standard)** | `meter_type` 1 — no Economy-7 here; off-peak fuel-cost bug **does not apply** | + +## Water Heating + +| Elmhurst field | Value | Notes | +|---|---|---| +| Source | **From main heating system** (Boiler Circulator) | `water_heating_code` 901; "From main system" | +| Fuel | **Mains gas** | `water_heating_fuel` 26 (engine HW cost 3.48 p/kWh) | +| Hot Water Cylinder Present | **Yes** | `has_hot_water_cylinder` true | +| Cylinder size | **110 L** (pick the litre value, not "Normal") | `cylinder_size` code 2 = 110 L | +| Cylinder insulation type | **Factory foam** | `cylinder_insulation_type` 1 = Foam | +| Cylinder insulation thickness | **50 mm** | `cylinder_insulation_thickness` 50 | +| Cylinder thermostat | **Yes** | `cylinder_thermostat` Y | +| (cross-check) | engine `hot_water_kwh_per_yr` 3640 | | + +- **Number of baths** (Water Heating → **WWHRS sub-tab → "Total no. of Baths"**): **1** + — `instantaneous_wwhrs.rooms_with_bath_and_or_shower` 1 + `rooms_with_bath_and_mixer_shower` 0 = 1. Elmhurst defaults to 0; set it to **1** or HW demand diverges. +- **WWHRS**: **No** — the `instantaneous_wwhrs` block is bath/shower ROOM counts (ADR-0028), not a heat-recovery device. +- **Community Hot Water**: none. +- **Solar Water Heating**: No — `solar_water_heating` N. +- **FGHRS**: No — `main_heating_details[].has_fghrs` N. + +## New Technologies + +| Elmhurst field | Value | Notes | +|---|---|---| +| PV | **None** | `photovoltaic_supply.none_or_no_details` | +| Wind | **None** | `wind_turbines_count` 0 | +| Hydro | None | not lodged | +| Special Features (Appendix Q) | None | not lodged | + +--- + +### Engine reference outputs (the numbers Elmhurst should reproduce) + +| Quantity | Engine value | +|---|---| +| SAP score (integer) | **70** (continuous 69.77) | +| Space-heating demand | 9 755 kWh/yr | +| Main-heating fuel (gas) | 10 452 kWh/yr | +| Hot-water | 3 640 kWh/yr | +| Secondary (electric, assumed) | 976 kWh/yr | +| CO₂ | 3 184 kg/yr | +| Total fuel cost | £824.73/yr | +| Heat-transfer coefficient | 245.96 W/K | +| Heat-loss parameter | 2.14 W/m²K | + +**Operator: key this into Elmhurst, then export the Summary PDF (`elmhurst_summary.pdf`) and the SAP-10.2 worksheet PDF (`elmhurst_worksheet.pdf`) into this directory, and report the Elmhurst SAP score.** Then re-run `scripts/compare_epc_paths.py 60027826` to diff the two paths. diff --git a/backend/epc_api/json_samples/real_life_examples/RdSAP-Schema-20.0.0/uprn_60027826/epc.json b/backend/epc_api/json_samples/real_life_examples/RdSAP-Schema-20.0.0/uprn_60027826/epc.json new file mode 100644 index 00000000..243c1f2d --- /dev/null +++ b/backend/epc_api/json_samples/real_life_examples/RdSAP-Schema-20.0.0/uprn_60027826/epc.json @@ -0,0 +1,343 @@ +{ + "uprn": 60027826, + "roofs": [ + { + "description": { + "value": "Roof room(s), insulated (assumed)", + "language": "1" + }, + "energy_efficiency_rating": 4, + "environmental_efficiency_rating": 4 + } + ], + "walls": [ + { + "description": { + "value": "Cavity wall, filled cavity", + "language": "1" + }, + "energy_efficiency_rating": 4, + "environmental_efficiency_rating": 4 + } + ], + "floors": [ + { + "description": { + "value": "Solid, no insulation (assumed)", + "language": "1" + }, + "energy_efficiency_rating": 0, + "environmental_efficiency_rating": 0 + } + ], + "status": "entered", + "tenure": 2, + "window": { + "description": { + "value": "Fully double glazed", + "language": "1" + }, + "energy_efficiency_rating": 3, + "environmental_efficiency_rating": 3 + }, + "lighting": { + "description": { + "value": "Low energy lighting in 86% of fixed outlets", + "language": "1" + }, + "energy_efficiency_rating": 5, + "environmental_efficiency_rating": 5 + }, + "postcode": "BN43 6NQ", + "hot_water": { + "description": { + "value": "From main system", + "language": "1" + }, + "energy_efficiency_rating": 4, + "environmental_efficiency_rating": 4 + }, + "post_town": "SHOREHAM-BY-SEA", + "built_form": 2, + "created_at": "2022-04-13 09:33:39.943417", + "door_count": 2, + "glazed_area": 1, + "glazing_gap": "16+", + "region_code": 14, + "report_type": 2, + "sap_heating": { + "cylinder_size": 2, + "water_heating_code": 901, + "water_heating_fuel": 26, + "cylinder_thermostat": "Y", + "instantaneous_wwhrs": { + "rooms_with_bath_and_or_shower": 1, + "rooms_with_mixer_shower_no_bath": 0, + "rooms_with_bath_and_mixer_shower": 0 + }, + "secondary_fuel_type": 29, + "main_heating_details": [ + { + "has_fghrs": "N", + "main_fuel_type": 26, + "boiler_flue_type": 2, + "fan_flue_present": "Y", + "heat_emitter_type": 1, + "emitter_temperature": 0, + "main_heating_number": 1, + "main_heating_control": 2106, + "main_heating_category": 2, + "main_heating_fraction": 1, + "sap_main_heating_code": 102, + "central_heating_pump_age": 0, + "main_heating_data_source": 2 + } + ], + "immersion_heating_type": "NA", + "secondary_heating_type": 691, + "cylinder_insulation_type": 1, + "has_fixed_air_conditioning": "false", + "cylinder_insulation_thickness": 50 + }, + "sap_version": 9.94, + "schema_type": "RdSAP-Schema-20.0.0", + "uprn_source": "Energy Assessor", + "country_code": "EAW", + "main_heating": [ + { + "description": { + "value": "Boiler and radiators, mains gas", + "language": "1" + }, + "energy_efficiency_rating": 4, + "environmental_efficiency_rating": 4 + } + ], + "dwelling_type": { + "value": "Semi-detached house", + "language": "1" + }, + "language_code": 1, + "property_type": 0, + "address_line_1": "10 KINGSLAND CLOSE", + "assessment_type": "RdSAP", + "completion_date": "2022-04-13", + "inspection_date": "2021-08-17", + "extensions_count": 0, + "measurement_type": 1, + "total_floor_area": 115, + "transaction_type": 13, + "conservatory_type": 1, + "heated_room_count": 6, + "pvc_window_frames": "true", + "registration_date": "2022-04-13", + "sap_energy_source": { + "mains_gas": "Y", + "meter_type": 1, + "photovoltaic_supply": { + "none_or_no_details": { + "pv_connection": 0, + "percent_roof_area": 0 + } + }, + "wind_turbines_count": 0, + "wind_turbines_terrain_type": 2 + }, + "secondary_heating": { + "description": { + "value": "None", + "language": "1" + }, + "energy_efficiency_rating": 0, + "environmental_efficiency_rating": 0 + }, + "sap_building_parts": [ + { + "identifier": "Main Dwelling", + "wall_u_value": 0.33, + "wall_dry_lined": "Y", + "wall_thickness": 300, + "floor_heat_loss": 7, + "sap_room_in_roof": { + "floor_area": { + "value": 36, + "quantity": "square metres" + }, + "insulation": "AB", + "roof_room_connected": "N", + "construction_age_band": "H" + }, + "roof_construction": 4, + "wall_construction": 4, + "building_part_number": 1, + "sap_floor_dimensions": [ + { + "floor": 0, + "room_height": { + "value": 2.3, + "quantity": "metres" + }, + "floor_insulation": 1, + "total_floor_area": { + "value": 42.85, + "quantity": "square metres" + }, + "party_wall_length": { + "value": 6, + "quantity": "metres" + }, + "floor_construction": 1, + "heat_loss_perimeter": { + "value": 21.7, + "quantity": "metres" + } + }, + { + "floor": 1, + "room_height": { + "value": 2.3, + "quantity": "metres" + }, + "total_floor_area": { + "value": 36, + "quantity": "square metres" + }, + "party_wall_length": { + "value": 6, + "quantity": "metres" + }, + "heat_loss_perimeter": { + "value": 18, + "quantity": "metres" + } + } + ], + "wall_insulation_type": 2, + "construction_age_band": "H", + "party_wall_construction": 0, + "wall_thickness_measured": "Y", + "roof_insulation_location": 2, + "roof_insulation_thickness": "100mm", + "floor_insulation_thickness": "NI" + } + ], + "low_energy_lighting": 86, + "solar_water_heating": "N", + "habitable_room_count": 6, + "heating_cost_current": { + "value": 522, + "currency": "GBP" + }, + "insulated_door_count": 0, + "co2_emissions_current": 3.4, + "energy_rating_average": 60, + "energy_rating_current": 72, + "lighting_cost_current": { + "value": 111, + "currency": "GBP" + }, + "main_heating_controls": [ + { + "description": { + "value": "Programmer, room thermostat and TRVs", + "language": "1" + }, + "energy_efficiency_rating": 4, + "environmental_efficiency_rating": 4 + } + ], + "multiple_glazing_type": 3, + "open_fireplaces_count": 0, + "has_hot_water_cylinder": "true", + "heating_cost_potential": { + "value": 486, + "currency": "GBP" + }, + "hot_water_cost_current": { + "value": 129, + "currency": "GBP" + }, + "mechanical_ventilation": 0, + "percent_draughtproofed": 100, + "suggested_improvements": [ + { + "sequence": 1, + "typical_saving": { + "value": 37, + "currency": "GBP" + }, + "indicative_cost": "\u00a34,000 - \u00a36,000", + "improvement_type": "W2", + "improvement_details": { + "improvement_number": 58 + }, + "improvement_category": 5, + "energy_performance_rating": 74, + "environmental_impact_rating": 71 + }, + { + "sequence": 2, + "typical_saving": { + "value": 42, + "currency": "GBP" + }, + "indicative_cost": "\u00a34,000 - \u00a36,000", + "improvement_type": "N", + "improvement_details": { + "improvement_number": 19 + }, + "improvement_category": 5, + "energy_performance_rating": 75, + "environmental_impact_rating": 73 + }, + { + "sequence": 3, + "typical_saving": { + "value": 400, + "currency": "GBP" + }, + "indicative_cost": "\u00a33,500 - \u00a35,500", + "improvement_type": "U", + "improvement_details": { + "improvement_number": 34 + }, + "improvement_category": 5, + "energy_performance_rating": 84, + "environmental_impact_rating": 81 + } + ], + "co2_emissions_potential": 1.8, + "energy_rating_potential": 84, + "lighting_cost_potential": { + "value": 111, + "currency": "GBP" + }, + "schema_version_original": "LIG-19.0", + "hot_water_cost_potential": { + "value": 85, + "currency": "GBP" + }, + "renewable_heat_incentive": { + "water_heating": 2737, + "impact_of_loft_insulation": -71, + "space_heating_existing_dwelling": 8821 + }, + "energy_consumption_current": 167, + "has_fixed_air_conditioning": "false", + "multiple_glazed_proportion": 100, + "calculation_software_version": "2.1.1.0", + "energy_consumption_potential": 89, + "environmental_impact_current": 69, + "fixed_lighting_outlets_count": 14, + "windows_transmission_details": { + "u_value": 2.6, + "data_source": 2, + "solar_transmittance": 0.76 + }, + "current_energy_efficiency_band": "C", + "environmental_impact_potential": 81, + "has_heated_separate_conservatory": "false", + "potential_energy_efficiency_band": "B", + "co2_emissions_current_per_floor_area": 29, + "low_energy_fixed_lighting_outlets_count": 12 +} \ No newline at end of file diff --git a/backend/epc_api/json_samples/real_life_examples/SAP-Schema-17.1/uprn_10092973954/elmhurst_inputs.md b/backend/epc_api/json_samples/real_life_examples/SAP-Schema-17.1/uprn_10092973954/elmhurst_inputs.md new file mode 100644 index 00000000..f531a917 --- /dev/null +++ b/backend/epc_api/json_samples/real_life_examples/SAP-Schema-17.1/uprn_10092973954/elmhurst_inputs.md @@ -0,0 +1,182 @@ +# Elmhurst RdSAP inputs — UPRN 10092973954 (cert 0862-3892-7875-2690-2325, SAP-Schema-17.1) + +**Lodged SAP:** 83 (band B) **Our engine:** 77 (continuous 76.85) ← compare Elmhurst against this +**Total fuel cost (engine):** £445.80/yr **Heat transfer coeff:** 112.11 W/K +**Known divergences:** full-SAP cert forced through RdSAP — window area is banded, U-values/thermal-bridging are age-band proxies (see notes). MEV extract is the engine's −4 SAP driver. + +> **Complete + filler-ready.** Every row names the **live Elmhurst control id** +> (suffix after `ContentBody_ContentPlaceHolder1_`) from the DOM dumps in +> `scripts/hyde/elmhurst_dom/`, so `scripts/hyde/elmhurst_fill.py` can map each +> directly. Dropdown values are the **exact option labels** (they carry a code +> prefix, e.g. `F Flat`). 🔸 = **assessor-only** field the EPC can't supply. +> The **Fields to clear** table at the bottom is the explicit "absent" set. + +--- + +## Property Details (admin — does NOT affect SAP) +| Elmhurst field (`id`) | Enter | Notes | +|---|---|---| +| `DropDownListPurpose` | None of the above | not a retrofit/modelling job | +| `DropDownListTenure` | Owner-occupied | `tenure` ND; tenure doesn't affect SAP | +| `DropDownListTransactionType` | None of the above | `transaction_type` 6 = new dwelling (no exact option) | +| `TextBoxInspectionDate` | as lodged | admin only | + +## Property Description ✅ committed +| Elmhurst field (`id`) | Enter | Notes | +|---|---|---| +| `DropDownListPropertyType1` | **F Flat** | `property_type` 2 | +| `DropDownListPropertyType2` | **D Detached** | `built_form` 1 (block form) | +| `TextBoxStoreys` | **1** | single-storey flat | +| `TextBoxHabitableRooms` | **3** | back-solved from `living_area` 23.45 / TFA | +| `TextBoxHeatedHabitableRooms` | **3** | all heated | +| `DropDownListDateBuiltMain` | **L 2012-2022** | 2020 build — by YEAR (engine band "M"; see gotcha) | + +## Dimensions (Type = Internal) — Lowest Floor row only +| Elmhurst field (`id`, under `…DimensionsMain_`) | Enter | Notes | +|---|---|---| +| `RadioButtonDimensionsType` | Internal | `measurement_type` 1 | +| `TextBoxFloorAreaLowestFloor` | **67.84** | engine TFA (cert top-level 68) | +| `TextBoxRoomHeightLowestFloor` | **2.40** | volume 162.816 ÷ 67.84 | +| `TextBoxWallPerimeterLowestFloor` | **38.14** | heat-loss perimeter (= 91.54 m² ÷ 2.4) | +| `TextBoxPartyWallLengthLowestFloor` | **0** | `party_walls_w_per_k` = 0 | + +## Conservatory ✅ already correct +| Elmhurst field (`id`) | Enter | Notes | +|---|---|---| +| `CheckBoxIsConservatory` | unchecked | `conservatory_type` 1 = none | + +## Flats +| Elmhurst field (`id`) | Enter | Notes | +|---|---|---| +| `DropDownListPositionOfFlat` | **Ground Floor** | `sap_flat_details.level` 1 / ground-floor flat | +| `TextBoxFloor` | **0** | ground = storey 0 | +| 🔸 `TextBoxStoriesInBlock` | *assessor* | EPC doesn't give block height; not in cert | +| 🔸 `RadioButtonListFlatCoridor` | *assessor* | corridor/stairwell adjacency; `sheltered_sides_count` 1 hints 1 sheltered side | +| 🔸 `TextBoxLengthOfShelteredWall` | *assessor* | only if a sheltered/corridor wall is set | + +## Walls (Main + alternative + party) +| Elmhurst field (`id`, under `…WallMain_`) | Enter | Notes | +|---|---|---| +| `DropDownListType` | **TI Timber Frame** | new-build timber frame | +| `DropDownListInsulation` | **A As Built** | factory-insulated | +| `TextBoxWallThickness` | as built | — | +| `CheckBoxUvalueKnown` + U-value | **0.17** if override offered | cert "0.17 W/m²K"; engine wall ≈12.60 W/K | +| `CheckBoxAditionalWallSheltered` / `DropDownListAlternativeWall1Type` | **clear** (area 0) | leftover `SO Solid Brick` is placeholder — see Fields to clear | +| `DropDownListPartyWallType` | n/a | party-wall **length 0** on Dimensions makes type irrelevant | + +## Roofs ✅ committed +| Elmhurst field (`id`, under `…RoofMain_`) | Enter | Notes | +|---|---|---| +| `DropDownListType` | **A Another dwelling above** | "(other premises above)" → `roof_w_per_k` = 0 (insulation fields disappear) | + +## Floors +| Elmhurst field (`id`, under `…FloorsMain_`) | Enter | Notes | +|---|---|---| +| `DropDownListLocation` | **G Ground floor** | already correct | +| `DropDownListType` | **S Solid** | `floor_type` 2 | +| `DropDownListInsulation` | **A As built** | new-build | +| `CheckBoxUValueKnown` + U-value | **0.13** if override offered | cert "0.13 W/m²K"; engine floor ≈8.82 W/K | + +## Openings — Windows (Elmhurst uses a per-window GRID) +RdSAP synthesises window area from `glazed_area` band × TFA = **15.16 m²** +("Much more than typical", ×1.51), whole-window **U 1.4**, **g 0.63**, 5 windows, +PVC frame (factor 0.7). Engine windows = **20.68 W/K**. +| Elmhurst grid field (`id`, under `…WindowsPanel_`) | Enter | Notes | +|---|---|---| +| `DropDownListExtGlazing` | **Double post or during 2022** (high-perf low-E) | cert `glazing_type` 7, 100% multiple-glazed | +| `DropDownListExtFrameType` | **PVC** | `frame_type` 2 | +| `TextBoxExtUValue` | **1.4** | manufacturer whole-window U | +| `TextBoxExtGValue` | **0.63** | cert g | +| 🔸 `TextBoxExtWidth` / `TextBoxExtHeight` / per-row geometry | *assessor* | RdSAP has no per-window geometry; enter rows totalling **15.16 m²**, or one combined row | +| `DropDownListExtOrientation` | as RdSAP default | cert orientations 8,2,6,2,4 — RdSAP ignores per-window orientation | + +## Openings — Doors +| Elmhurst field (`id`, under `…DoorsPanel_`) | Enter | Notes | +|---|---|---| +| `TextBoxDoors` | **1** | `door_count` 1 | +| `TextBoxDoorsInsulated` | **1** | new-build, cert door U 1.4 (engine `doors_w_per_k` 2.59) | + +## Ventilation & Lighting +| Elmhurst field (`id`) | Enter | Notes | +|---|---|---| +| `TabPanelMechVent_CheckBoxMechanicalVentilation` | **checked → dMEV** | 🔶 `ventilation_type` 6, EXTRACT_OR_PIV_OUTSIDE — the **−4 SAP driver** | +| 🔸 `SelectMVDialog_*` (MV unit) | *assessor* | pick the dMEV unit; `is_mechanical_vent_approved_installer_scheme` true | +| `TabPanelAirPressureTest_DropDownListTestMethod` | **Blower Door → 2.6** | `air_permeability` 2.6, `pressure_test` 1 (don't leave "Not available") | +| `TabPanelLighting_*` low-energy | **100% LED** (6 of 6) | "Low energy lighting in all fixed outlets" | +| `TabPanelVentilationPanel_TextBox*` chimneys/flues/fans | **0** | none lodged | +| Sheltered sides | **1** | `sheltered_sides_count` 1 (RdSAP defaults 2 — change) | + +## Space Heating — Main Heating 1 +Cert: "Boiler and radiators, mains gas", `main_fuel_type` 1, combi, **PCDB index 17929**, +efficiency **89%**, controls **2110** (time + temp zone), radiators. +| Elmhurst field (`id`, under `…MainHeating1_`) | Enter | Notes | +|---|---|---| +| 🔸 `TextBoxPCDFBoilerReference` (via `SelectBoilerDialog`) | **17929** | PCDB index; entered through the boiler-search dialog | +| `RadioButtonListHeatEmitter` | **Radiators** | `heat_emitter_type` 1 | +| `RadioButtonListFlueType` | **Balanced** | room-sealed; `is_flue_fan_present` true | +| `RadioButtonListFanAssistedFlue` | **Yes** | `main_heating_flue_type` 2 | +| `RadioButtonListHeatingPumpAge` | **2013 or later** | `central_heating_pump_age` 2, in heated space | +| 🔸 `TextBoxMainHeatingControls` (via `SelectHeatingControlsDialog`) | **2110** | time & temperature zone control; interlocked | +| `TextBoxPercentageOfHeat` | **100** | single main system | + +## Space Heating — Meters +| Elmhurst field (`id`, under `TabPanelMeters_`) | Enter | Notes | +|---|---|---| +| `RadioButtonListElectricityType` | **Single** | `electricity_tariff` 1 — **not** Economy 7 (no off-peak bug here) | +| `CheckBoxMainGas` | **checked** | mains gas available | + +## Water Heating +Cert: from main system (combi), `water_heating_code` 901, mains gas. Engine HW = **2,271 kWh/yr**. +| Elmhurst field (`id`, under `TabPanelWaterHeating_`) | Enter | Notes | +|---|---|---| +| 🔸 `TextBoxWaterHeatingCode` | **from main system** (901) | combi; **Boiler Circulator / from main** category (not Water Heater) | +| `CheckBoxHotWaterCylinder` | **unchecked** | combi instantaneous — **no cylinder** (clears CylinderSize/Insulated/Thickness/Thermostat) | +| `TabPanelWWHRS_DropDownListWWHRSPresent` | **No** | none lodged (`instantaneous_wwhrs` = bath/shower ROOM count, ADR-0028, not a device) | +| `TabPanelWWHRS_TextBoxRoomsWithBath` | leave default | baths not separately lodged; `design_water_use` 1 | + +## New Technologies ✅ already correct (all none) +| Elmhurst field (`id`) | Enter | Notes | +|---|---|---| +| `…PvPanelData` | None | no PV | +| `…CheckBoxWindTurbinePresent` | unchecked | `wind_turbines_count` 0 | +| `…TextBoxElectricityGenerated` (hydro) | 0 | none | + +--- + +## Fields to clear in Elmhurst (do NOT map) +These default/stale values read as **present** and skew SAP. Blank them. +| Elmhurst page · field (`id`) | Set to | Why absent | +|---|---|---| +| Property Description · `DropDownListDateBuiltFirst`…`Forth` | (blank) | no extensions lodged | +| Property Description · `DropDownListRoomInRoofMain`…`Forth` | (blank) | no room-in-roof building part | +| Dimensions · `…TextBoxFloorAreaRoomInRoof` | (blank) | leave empty, **not 0** (0 flags ✖) | +| Dimensions · 1st–4th Ext. sub-tabs (`TabPanel1stExt_…` etc.) | 0 / blank | single building part — phantom extension otherwise | +| Walls · `…DropDownListAlternativeWall1Type` + `TextBoxAddWallArea` | (blank / 0) | leftover `SO Solid Brick` placeholder; cert wall is one timber-frame element | +| Walls · `…CheckBoxAditionalWallSheltered` | unchecked | unless an assessor sets a sheltered wall | +| Openings · surplus window grid rows (`GridViewExtendedWidows_DeleteButton_*`) | delete | window count from glazed-area band, not real rows | +| Space Heating · `DropDownListSecondaryHeatingPresent` + `TextBoxSecondaryHeatingCode` | **No** / (blank) | `secondary_heating_category` 1 = None (placeholder `RWM`) | +| Space Heating · `TabPanelMainHeating2_*` | (blank) | single main system | +| Water Heating · `CheckBoxHotWaterCylinder` + cylinder fields | unchecked / (blank) | combi instantaneous, no cylinder | +| Water Heating · Solar (`…CheckBoxSolaWaterHeating`), WWHRS, FGHRS | none | not lodged | +| Conservatory / PV / Wind / Hydro | none | not lodged | + +## Engine reference figures (reconcile against Elmhurst's worksheet) +| Quantity | Engine value | +|---|---| +| **SAP score** | **77** (continuous 76.85) | +| Total fuel cost | £445.80/yr | +| Space-heating demand | 3,917.4 kWh/yr | +| Main heating fuel | 4,401.5 kWh/yr | +| Hot water | 2,271.0 kWh/yr | +| Pumps & fans | 483.3 kWh/yr | +| Lighting | 222.4 kWh/yr | +| CO₂ | 1,501.8 kg/yr | +| Heat transfer coefficient | 112.11 W/K | + +**Heat-loss element breakdown (W/K):** walls 12.60 · floor 8.82 · roof 0 · party wall 0 · +windows 20.68 · doors 2.59 · thermal bridging 12.75 · infiltration 54.68 → total HTC 112.11. + +> **🔸 Assessor-only fields** (EPC can't supply — a human must set them): +> stories-in-block & corridor type (Flats), per-window geometry rows (Openings), +> boiler PCDB reference 17929 & control code 2110 via their search dialogs +> (Space Heating), MV unit selection (Ventilation), water-heating code dialog. diff --git a/backend/epc_api/json_samples/real_life_examples/SAP-Schema-17.1/uprn_10092973954/elmhurst_summary.pdf b/backend/epc_api/json_samples/real_life_examples/SAP-Schema-17.1/uprn_10092973954/elmhurst_summary.pdf new file mode 100644 index 00000000..c48e5752 Binary files /dev/null and b/backend/epc_api/json_samples/real_life_examples/SAP-Schema-17.1/uprn_10092973954/elmhurst_summary.pdf differ diff --git a/backend/epc_api/json_samples/real_life_examples/SAP-Schema-17.1/uprn_10092973954/elmhurst_worksheet.pdf b/backend/epc_api/json_samples/real_life_examples/SAP-Schema-17.1/uprn_10092973954/elmhurst_worksheet.pdf new file mode 100644 index 00000000..7f785404 Binary files /dev/null and b/backend/epc_api/json_samples/real_life_examples/SAP-Schema-17.1/uprn_10092973954/elmhurst_worksheet.pdf differ diff --git a/backend/epc_api/json_samples/real_life_examples/SAP-Schema-17.1/uprn_10093116528/epc.json b/backend/epc_api/json_samples/real_life_examples/SAP-Schema-17.1/uprn_10093116528/epc.json new file mode 100644 index 00000000..e6cf72d9 --- /dev/null +++ b/backend/epc_api/json_samples/real_life_examples/SAP-Schema-17.1/uprn_10093116528/epc.json @@ -0,0 +1,438 @@ +{ + "uprn": 10093116528, + "roofs": [ + { + "description": "Average thermal transmittance 0.11 W/m\u00b2K", + "energy_efficiency_rating": 5, + "environmental_efficiency_rating": 5 + } + ], + "walls": [ + { + "description": "Average thermal transmittance 0.19 W/m\u00b2K", + "energy_efficiency_rating": 5, + "environmental_efficiency_rating": 5 + } + ], + "floors": [ + { + "description": "Average thermal transmittance 0.11 W/m\u00b2K", + "energy_efficiency_rating": 5, + "environmental_efficiency_rating": 5 + } + ], + "status": "entered", + "tenure": "ND", + "windows": { + "description": "High performance glazing", + "energy_efficiency_rating": 5, + "environmental_efficiency_rating": 5 + }, + "lighting": { + "description": "Low energy lighting in all fixed outlets", + "energy_efficiency_rating": 5, + "environmental_efficiency_rating": 5 + }, + "postcode": "PO10 8BG", + "data_type": 2, + "hot_water": { + "description": "From main system, flue gas heat recovery", + "energy_efficiency_rating": 5, + "environmental_efficiency_rating": 5 + }, + "post_town": "EMSWORTH", + "built_form": 2, + "created_at": "2018-06-04 10:56:39", + "living_area": 28.81, + "orientation": 0, + "region_code": 16, + "report_type": 3, + "sap_heating": { + "thermal_store": 1, + "water_fuel_type": 1, + "water_heating_code": 901, + "main_heating_details": [ + { + "has_fghrs": "true", + "main_fuel_type": 1, + "heat_emitter_type": 1, + "fghrs_index_number": 60031, + "emitter_temperature": 1, + "is_flue_fan_present": "true", + "main_heating_number": 1, + "main_heating_control": 2110, + "is_interlocked_system": "true", + "main_heating_category": 2, + "main_heating_fraction": 1, + "main_heating_flue_type": 2, + "central_heating_pump_age": 2, + "main_heating_data_source": 1, + "main_heating_index_number": 17644, + "has_separate_delayed_start": "true", + "load_or_weather_compensation": 0, + "is_central_heating_pump_in_heated_space": "true" + } + ], + "has_hot_water_cylinder": "false", + "has_fixed_air_conditioning": "false", + "secondary_heating_category": 1 + }, + "sap_version": 9.92, + "schema_type": "SAP-Schema-17.1", + "uprn_source": "Energy Assessor", + "country_code": "ENG", + "main_heating": [ + { + "description": "Boiler and radiators, mains gas", + "energy_efficiency_rating": 4, + "environmental_efficiency_rating": 4 + } + ], + "air_tightness": { + "description": "Air permeability 4.5 m\u00b3/h.m\u00b2 (as tested)", + "energy_efficiency_rating": 4, + "environmental_efficiency_rating": 4 + }, + "dwelling_type": "Semi-detached house", + "language_code": 1, + "property_type": 0, + "address_line_1": "4, Woodfield Park Road", + "assessment_date": "2018-06-01", + "assessment_type": "SAP", + "completion_date": "2018-06-04", + "inspection_date": "2018-06-01", + "sap_ventilation": { + "psv_count": 0, + "pressure_test": 1, + "air_permeability": 4.5, + "open_flues_count": 0, + "ventilation_type": 1, + "extract_fans_count": 3, + "open_fireplaces_count": 0, + "sheltered_sides_count": 1, + "flueless_gas_fires_count": 0 + }, + "design_water_use": 1, + "sap_data_version": 9.92, + "total_floor_area": 90, + "transaction_type": 6, + "conservatory_type": 1, + "registration_date": "2018-06-04", + "sap_energy_source": { + "electricity_tariff": 1, + "wind_turbines_count": 0, + "wind_turbine_terrain_type": 2, + "fixed_lighting_outlets_count": 10, + "low_energy_fixed_lighting_outlets_count": 10, + "low_energy_fixed_lighting_outlets_percentage": 100 + }, + "sap_opening_types": [ + { + "name": 1, + "type": 4, + "u_value": 1.4, + "data_source": 2, + "frame_factor": 0.7, + "glazing_type": 4, + "solar_transmittance": 0.72 + }, + { + "name": 5, + "type": 1, + "u_value": 1.6, + "data_source": 2, + "glazing_type": 1 + } + ], + "secondary_heating": { + "description": "None", + "energy_efficiency_rating": 0, + "environmental_efficiency_rating": 0 + }, + "sap_building_parts": [ + { + "sap_roofs": [ + { + "name": "Roof 1", + "u_value": 0.113, + "roof_type": 2, + "description": "Roof 1", + "total_roof_area": 44.93 + } + ], + "sap_walls": [ + { + "name": "Wall 1", + "u_value": 0.187, + "wall_type": 2, + "description": "External", + "total_wall_area": 67.29, + "is_curtain_walling": "false" + }, + { + "name": "Wall 2", + "u_value": 0, + "wall_type": 4, + "description": "To other house", + "total_wall_area": 79.1 + } + ], + "identifier": "Main Dwelling", + "overshading": 2, + "sap_openings": [ + { + "name": 1, + "type": 1, + "width": 0.9, + "height": 2.5, + "location": "Wall 1", + "orientation": 3 + }, + { + "name": 2, + "type": 1, + "width": 2.85, + "height": 2.5, + "location": "Wall 1", + "orientation": 3 + }, + { + "name": 3, + "type": 1, + "width": 0.8, + "height": 2.5, + "location": "Wall 1", + "orientation": 3 + }, + { + "name": 4, + "type": 1, + "width": 1.4, + "height": 2.5, + "location": "Wall 1", + "orientation": 3 + }, + { + "name": 5, + "type": 5, + "width": 1, + "height": 2.3, + "location": "Wall 1", + "orientation": 0 + }, + { + "name": 6, + "type": 1, + "width": 1, + "height": 2.45, + "location": "Wall 1", + "orientation": 7 + }, + { + "name": 7, + "type": 1, + "width": 1.45, + "height": 2.2, + "location": "Wall 1", + "orientation": 7 + }, + { + "name": 8, + "type": 1, + "width": 1.35, + "height": 2.45, + "location": "Wall 1", + "orientation": 7 + } + ], + "construction_year": 2017, + "sap_thermal_bridges": { + "thermal_bridges": [ + { + "length": 10.75, + "psi_value": 0.3, + "psi_value_source": 2, + "thermal_bridge_type": "E2" + }, + { + "length": 9.75, + "psi_value": 0.04, + "psi_value_source": 2, + "thermal_bridge_type": "E3" + }, + { + "length": 38.8, + "psi_value": 0.05, + "psi_value_source": 2, + "thermal_bridge_type": "E4" + }, + { + "length": 12.94, + "psi_value": 0.16, + "psi_value_source": 2, + "thermal_bridge_type": "E5" + }, + { + "length": 12.94, + "psi_value": 0.14, + "psi_value_source": 4, + "thermal_bridge_type": "E7" + }, + { + "length": 10, + "psi_value": 0.06, + "psi_value_source": 2, + "thermal_bridge_type": "E10" + }, + { + "length": 2.1, + "psi_value": 0.24, + "psi_value_source": 2, + "thermal_bridge_type": "E12" + }, + { + "length": 5.2, + "psi_value": 0.09, + "psi_value_source": 2, + "thermal_bridge_type": "E16" + }, + { + "length": 5.2, + "psi_value": 0.06, + "psi_value_source": 2, + "thermal_bridge_type": "E18" + }, + { + "length": 15.21, + "psi_value": 0.16, + "psi_value_source": 4, + "thermal_bridge_type": "P1" + }, + { + "length": 15.21, + "psi_value": 0, + "psi_value_source": 4, + "thermal_bridge_type": "P3" + }, + { + "length": 15.24, + "psi_value": 0.24, + "psi_value_source": 4, + "thermal_bridge_type": "P4" + } + ], + "thermal_bridge_code": 5 + }, + "building_part_number": 1, + "sap_floor_dimensions": [ + { + "storey": 0, + "u_value": 0.11, + "floor_type": 2, + "description": "Ground floor", + "storey_height": 2.6, + "heat_loss_area": 44.93, + "total_floor_area": 44.93 + }, + { + "storey": 1, + "u_value": 0, + "floor_type": 3, + "storey_height": 2.6, + "heat_loss_area": 0, + "total_floor_area": 44.93 + } + ], + "thermal_mass_parameter": 250 + } + ], + "heating_cost_current": { + "value": 202, + "currency": "GBP" + }, + "co2_emissions_current": 1.2, + "energy_rating_average": 60, + "energy_rating_current": 84, + "lighting_cost_current": { + "value": 61, + "currency": "GBP" + }, + "main_heating_controls": [ + { + "description": "Time and temperature zone control", + "energy_efficiency_rating": 5, + "environmental_efficiency_rating": 5 + } + ], + "has_hot_water_cylinder": "false", + "heating_cost_potential": { + "value": 202, + "currency": "GBP" + }, + "hot_water_cost_current": { + "value": 72, + "currency": "GBP" + }, + "suggested_improvements": [ + { + "sequence": 1, + "typical_saving": { + "value": 29, + "currency": "GBP" + }, + "indicative_cost": "\u00a34,000 - \u00a36,000", + "improvement_type": "N", + "improvement_details": { + "improvement_number": 19 + }, + "improvement_category": 5, + "energy_performance_rating": 85, + "environmental_impact_rating": 88 + }, + { + "sequence": 2, + "typical_saving": { + "value": 327, + "currency": "GBP" + }, + "indicative_cost": "\u00a35,000 - \u00a38,000", + "improvement_type": "U", + "improvement_details": { + "improvement_number": 34 + }, + "improvement_category": 5, + "energy_performance_rating": 95, + "environmental_impact_rating": 97 + } + ], + "co2_emissions_potential": -0.1, + "energy_rating_potential": 95, + "lighting_cost_potential": { + "value": 61, + "currency": "GBP" + }, + "schema_version_original": "LIG-17.0", + "hot_water_cost_potential": { + "value": 43, + "currency": "GBP" + }, + "is_in_smoke_control_area": "unknown", + "renewable_heat_incentive": { + "rhi_new_dwelling": { + "space_heating": 2310, + "water_heating": 1818 + } + }, + "seller_commission_report": "Y", + "energy_consumption_current": 75, + "has_fixed_air_conditioning": "false", + "multiple_glazed_percentage": 100, + "calculation_software_version": 6.2, + "energy_consumption_potential": -6, + "environmental_impact_current": 87, + "current_energy_efficiency_band": "B", + "environmental_impact_potential": 97, + "has_heated_separate_conservatory": "false", + "potential_energy_efficiency_band": "A", + "co2_emissions_current_per_floor_area": 13 +} \ No newline at end of file