test(accuracy): validate + pin 100061275133 (slice-5 external-insulation U confirmed)

2-storey semi, solid brick 400mm with EXTERNAL insulation, 300mm loft,
suspended uninsulated ground floor, gas combi, double glazing (glazed_type 13).
Built in accredited Elmhurst RdSAP10: roof (U 0.14, 5.459 W/K) and windows
(15.32 x eff U 1.3258 = 20.311 W/K) match the engine EXACTLY; the
externally-insulated wall is engine U 0.29 vs Elmhurst 0.30, validating the
slice-5 external-insulation default (100mm added over solid brick); suspended
ground floor engine 0.68 vs Elmhurst 0.70. Elmhurst's overall SAP 67 is not
comparable (reused-assessment Standard-Combi keep-hot loss, a build artifact).
Engine 77 vs lodged 76 (within 1.24). Pinned to the observed 77.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Jun-te Kim 2026-07-03 04:28:25 +00:00
parent 17ced10e94
commit 1032a4f128
5 changed files with 656 additions and 0 deletions

View file

@ -0,0 +1,410 @@
{
"uprn": 100061275133,
"roofs": [
{
"description": "Pitched, 300 mm loft insulation",
"energy_efficiency_rating": 5,
"environmental_efficiency_rating": 5
}
],
"walls": [
{
"description": "Solid brick, with external insulation",
"energy_efficiency_rating": 4,
"environmental_efficiency_rating": 4
}
],
"floors": [
{
"description": "Suspended, no insulation (assumed)",
"energy_efficiency_rating": 0,
"environmental_efficiency_rating": 0
}
],
"status": "entered",
"tenure": 2,
"window": {
"description": "High performance glazing",
"energy_efficiency_rating": 4,
"environmental_efficiency_rating": 4
},
"lighting": {
"description": "Excellent lighting efficiency",
"energy_efficiency_rating": 5,
"environmental_efficiency_rating": 5
},
"postcode": "ME7 2ES",
"hot_water": {
"description": "From main system",
"energy_efficiency_rating": 4,
"environmental_efficiency_rating": 4
},
"post_town": "GILLINGHAM",
"built_form": 2,
"created_at": "2026-05-20 08:39:48",
"door_count": 2,
"region_code": 14,
"report_type": 2,
"sap_heating": {
"number_baths": 1,
"cylinder_size": 1,
"number_baths_wwhrs": 0,
"water_heating_code": 901,
"water_heating_fuel": 26,
"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,
"central_heating_pump_age": 0,
"main_heating_data_source": 1,
"main_heating_index_number": 18908
}
],
"immersion_heating_type": "NA",
"has_fixed_air_conditioning": "false"
},
"sap_version": 10.2,
"sap_windows": [
{
"orientation": 8,
"window_type": 1,
"glazing_type": 13,
"window_width": 1.19,
"window_height": 1.13,
"draught_proofed": "true",
"window_location": 0,
"window_wall_type": 1,
"permanent_shutters_present": "N",
"permanent_shutters_insulated": "N"
},
{
"orientation": 8,
"window_type": 1,
"glazing_type": 13,
"window_width": 0.57,
"window_height": 0.55,
"draught_proofed": "true",
"window_location": 0,
"window_wall_type": 1,
"permanent_shutters_present": "N",
"permanent_shutters_insulated": "N"
},
{
"orientation": 8,
"window_type": 1,
"glazing_type": 13,
"window_width": 1.19,
"window_height": 1.11,
"draught_proofed": "true",
"window_location": 0,
"window_wall_type": 1,
"permanent_shutters_present": "N",
"permanent_shutters_insulated": "N"
},
{
"orientation": 2,
"window_type": 1,
"glazing_type": 13,
"window_width": 1.05,
"window_height": 0.85,
"draught_proofed": "true",
"window_location": 0,
"window_wall_type": 1,
"permanent_shutters_present": "N",
"permanent_shutters_insulated": "N"
},
{
"orientation": 2,
"window_type": 1,
"glazing_type": 13,
"window_width": 0.57,
"window_height": 0.56,
"draught_proofed": "true",
"window_location": 0,
"window_wall_type": 1,
"permanent_shutters_present": "N",
"permanent_shutters_insulated": "N"
},
{
"orientation": 4,
"window_type": 1,
"glazing_type": 13,
"window_width": 2.11,
"window_height": 1.13,
"draught_proofed": "true",
"window_location": 0,
"window_wall_type": 1,
"permanent_shutters_present": "N",
"permanent_shutters_insulated": "N"
},
{
"orientation": 4,
"window_type": 1,
"glazing_type": 13,
"window_width": 1.8,
"window_height": 0.97,
"draught_proofed": "true",
"window_location": 0,
"window_wall_type": 1,
"permanent_shutters_present": "N",
"permanent_shutters_insulated": "N"
},
{
"orientation": 4,
"window_type": 1,
"glazing_type": 13,
"window_width": 1.8,
"window_height": 0.97,
"draught_proofed": "true",
"window_location": 0,
"window_wall_type": 1,
"permanent_shutters_present": "N",
"permanent_shutters_insulated": "N"
},
{
"orientation": 4,
"window_type": 1,
"glazing_type": 13,
"window_width": 1.2,
"window_height": 0.99,
"draught_proofed": "true",
"window_location": 0,
"window_wall_type": 1,
"permanent_shutters_present": "N",
"permanent_shutters_insulated": "N"
},
{
"orientation": 8,
"window_type": 1,
"glazing_type": 13,
"window_width": 1.8,
"window_height": 0.97,
"draught_proofed": "true",
"window_location": 0,
"window_wall_type": 1,
"permanent_shutters_present": "N",
"permanent_shutters_insulated": "N"
},
{
"orientation": 8,
"window_type": 1,
"glazing_type": 13,
"window_width": 1.19,
"window_height": 0.97,
"draught_proofed": "true",
"window_location": 0,
"window_wall_type": 1,
"permanent_shutters_present": "N",
"permanent_shutters_insulated": "N"
},
{
"orientation": 8,
"window_type": 1,
"glazing_type": 13,
"window_width": 1.18,
"window_height": 0.99,
"draught_proofed": "true",
"window_location": 0,
"window_wall_type": 1,
"permanent_shutters_present": "N",
"permanent_shutters_insulated": "N"
}
],
"schema_type": "RdSAP-Schema-21.0.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": "(not tested)",
"energy_efficiency_rating": 0,
"environmental_efficiency_rating": 0
},
"dwelling_type": "Semi-detached house",
"language_code": 1,
"pressure_test": 4,
"property_type": 0,
"address_line_1": "75 Toronto Road",
"assessment_type": "RdSAP",
"completion_date": "2026-05-20",
"inspection_date": "2026-05-19",
"extensions_count": 0,
"measurement_type": 1,
"total_floor_area": 78,
"transaction_type": 8,
"conservatory_type": 1,
"heated_room_count": 3,
"registration_date": "2026-05-20",
"sap_energy_source": {
"mains_gas": "Y",
"meter_type": 2,
"pv_connection": 0,
"photovoltaic_supply": {
"none_or_no_details": {
"percent_roof_area": 0
}
},
"wind_turbines_count": 0,
"gas_smart_meter_present": "true",
"is_dwelling_export_capable": "false",
"wind_turbines_terrain_type": 2,
"electricity_smart_meter_present": "true"
},
"secondary_heating": {
"description": "None",
"energy_efficiency_rating": 0,
"environmental_efficiency_rating": 0
},
"sap_building_parts": [
{
"identifier": "Main Dwelling",
"wall_dry_lined": "N",
"wall_thickness": 400,
"floor_heat_loss": 7,
"roof_construction": 4,
"wall_construction": 3,
"building_part_number": 1,
"sap_floor_dimensions": [
{
"floor": 0,
"room_height": {
"value": 2.4,
"quantity": "metres"
},
"floor_insulation": 1,
"total_floor_area": {
"value": 38.99,
"quantity": "square metres"
},
"party_wall_length": {
"value": 5.09,
"quantity": "metres"
},
"floor_construction": 0,
"heat_loss_perimeter": {
"value": 17.92,
"quantity": "metres"
}
},
{
"floor": 1,
"room_height": {
"value": 2.4,
"quantity": "metres"
},
"total_floor_area": {
"value": 38.99,
"quantity": "square metres"
},
"party_wall_length": {
"value": 5.09,
"quantity": "metres"
},
"heat_loss_perimeter": {
"value": 17.92,
"quantity": "metres"
}
}
],
"wall_insulation_type": 1,
"construction_age_band": "B",
"party_wall_construction": 1,
"wall_thickness_measured": "Y",
"roof_insulation_location": 2,
"roof_insulation_thickness": "300mm",
"wall_insulation_thickness": "100mm",
"floor_insulation_thickness": "NI"
}
],
"solar_water_heating": "N",
"habitable_room_count": 3,
"heating_cost_current": {
"value": 608,
"currency": "GBP"
},
"insulated_door_count": 0,
"co2_emissions_current": 1.9,
"energy_rating_average": 60,
"energy_rating_current": 76,
"lighting_cost_current": {
"value": 58,
"currency": "GBP"
},
"main_heating_controls": [
{
"description": "Programmer, room thermostat and TRVs",
"energy_efficiency_rating": 4,
"environmental_efficiency_rating": 4
}
],
"has_hot_water_cylinder": "false",
"heating_cost_potential": {
"value": 608,
"currency": "GBP"
},
"hot_water_cost_current": {
"value": 182,
"currency": "GBP"
},
"mechanical_ventilation": 0,
"percent_draughtproofed": 100,
"suggested_improvements": [
{
"sequence": 1,
"typical_saving": {
"value": 216,
"currency": "GBP"
},
"indicative_cost": "\u00a38,000 - \u00a310,000",
"improvement_type": "U",
"improvement_details": {
"improvement_number": 34
},
"improvement_category": 5,
"energy_performance_rating": 81,
"environmental_impact_rating": 79
}
],
"co2_emissions_potential": 1.8,
"energy_rating_potential": 81,
"lighting_cost_potential": {
"value": 58,
"currency": "GBP"
},
"schema_version_original": "21.0.1",
"hot_water_cost_potential": {
"value": 182,
"currency": "GBP"
},
"renewable_heat_incentive": {
"water_heating": 2203.16,
"space_heating_existing_dwelling": 5517.85
},
"draughtproofed_door_count": 2,
"energy_consumption_current": 133,
"has_fixed_air_conditioning": "false",
"multiple_glazed_proportion": 100,
"calculation_software_version": "5.02r0344",
"energy_consumption_potential": 118,
"environmental_impact_current": 78,
"current_energy_efficiency_band": "C",
"environmental_impact_potential": 79,
"led_fixed_lighting_bulbs_count": 5,
"has_heated_separate_conservatory": "false",
"potential_energy_efficiency_band": "B",
"co2_emissions_current_per_floor_area": 24,
"incandescent_fixed_lighting_bulbs_count": 0
}

View file

@ -0,0 +1,224 @@
"""Elmhurst build for UPRN 100061275133 (RdSAP-21.0.1, MID-TERRACE HOUSE, band B,
SOLID BRICK 250 mm uninsulated, pitched insulated (assumed) loft, floor to
UNHEATED space, mains-gas COMBI (PCDB 19008) + gas room-heater SECONDARY, DUAL
meter, 11 DG windows ~18.32 , TFA 116. Lodged SAP 63; engine 65.0 (Δ +2.0).
Per-cert Elmhurst validation. Uses set_single_window (deletes the shared
assessment's carried-over window rows) + reset_transient_state at the end so the
assessment is left neutral. Run:
DISPLAY=:99 python scripts/hyde/build_100061275133.py <page>
"""
from __future__ import annotations
import sys
from pathlib import Path
import elmhurst_lib as E
DIM = "TabContainer_TabPanelMain_WebUserControlDimensionsMain_"
WALL = ("TabContainer_TabPanelMain_InnerTabContainerMain_"
"TabPanelExternalWallMain_WebUserControlWallMain_")
PWALL = ("TabContainer_TabPanelMain_InnerTabContainerMain_"
"TabPanelPartyWallMain_WebUserControlPartyWallMain_")
ROOF = "TabContainer_TabPanelMain_WebUserControlRoofMain_"
FLOOR = "TabContainer_TabPanelMain_WebUserControlFloorsMain_"
DP = "TabContainer_TabPanelDoorsPanel_"
VP = "TabContainer_TabPanelVentilationPanel_"
APT = "TabContainer_TabPanelAirPressureTest_"
LP = "TabContainer_TabPanelLighting_"
MV = "TabContainer_TabPanelMechVent_"
WH = "TabContainer_TabPanelWaterHeating_"
MH1B = "TabContainer_TabPanelMainHeating1_WebUserControlMainHeating1_"
_SAMPLE_DIR = (Path(__file__).parent.parent.parent
/ "backend/epc_api/json_samples/real_life_examples"
/ "RdSAP-Schema-21.0.1/uprn_100061275133")
def _pick(page, suffix, contains):
val = page.evaluate(
"""(a)=>{const s=document.getElementById(a[0]);if(!s)return null;
for(const o of s.options){if(o.text.toLowerCase().includes(a[1].toLowerCase()))return o.value;}return null;}""",
[f"{E.FP}{suffix}", contains])
if val is not None:
E.set_select(page, suffix, val)
return val
def property_description(page):
E.goto(page, "PropertyDescription", "WebFormPropertyDescription.aspx")
E.set_select(page, "DropDownListPropertyType1", "H House")
_pick(page, "DropDownListPropertyType2", "semi")
E.set_text(page, "TextBoxStoreys", "2")
E.set_text(page, "TextBoxHabitableRooms", "3")
E.set_text(page, "TextBoxHeatedHabitableRooms", "3")
print("date ->", _pick(page, "DropDownListDateBuiltMain", "1900-1929"))
E.set_select(page, "DropDownListDateBuiltFirst", "")
E.set_select(page, "DropDownListRoomInRoofMain", "")
E.save_close(page)
def dimensions(page):
E.goto(page, "Dimensions", "WebFormDimensions.aspx")
E.set_text(page, f"{DIM}TextBoxFloorAreaLowestFloor", "38.99")
E.set_text(page, f"{DIM}TextBoxRoomHeightLowestFloor", "2.40")
E.set_text(page, f"{DIM}TextBoxWallPerimeterLowestFloor", "17.92")
E.set_text(page, f"{DIM}TextBoxPartyWallLengthLowestFloor", "5.09")
E.set_text(page, f"{DIM}TextBoxFloorArea1stFloor", "38.99")
E.set_text(page, f"{DIM}TextBoxRoomHeight1stFloor", "2.40")
E.set_text(page, f"{DIM}TextBoxWallPerimeter1stFloor", "17.92")
E.set_text(page, f"{DIM}TextBoxPartyWallLength1stFloor", "5.09")
E.save_close(page)
def walls(page):
E.goto(page, "Walls", "WebFormWalls.aspx")
_pick(page, f"{WALL}DropDownListType", "solid brick")
page.wait_for_timeout(500)
print("insulation ->", _pick(page, f"{WALL}DropDownListInsulation", "external"))
print("party ->", _pick(page, f"{PWALL}DropDownListPartyWallType", "determine")
or _pick(page, f"{PWALL}DropDownListPartyWallType", "masonry"))
E.save_close(page)
def roofs(page):
E.goto(page, "Roofs", "WebFormRoofs.aspx")
_pick(page, f"{ROOF}DropDownListType", "access to loft") or \
_pick(page, f"{ROOF}DropDownListType", "loft")
_pick(page, f"{ROOF}DropDownListInsulationAt", "joists")
_pick(page, f"{ROOF}DropDownListThickness", "300")
E.save_close(page)
def floors(page):
E.goto(page, "Floors", "WebFormFloors.aspx")
_pick(page, f"{FLOOR}DropDownListLocation", "ground floor")
page.wait_for_timeout(400)
_pick(page, f"{FLOOR}DropDownListType", "suspended")
ins = page.locator(f"#{E.FP}{FLOOR}DropDownListInsulation")
if ins.count():
E.set_select(page, f"{FLOOR}DropDownListInsulation", "A As built")
E.save_close(page)
def openings(page):
E.goto(page, "Openings", "WebFormOpenings.aspx")
E.click_tab(page, "TabContainer_TabPanelWindowsPanel")
page.wait_for_timeout(500)
E.set_single_window(page, 15.32, "North", _glazing(page))
E.click_tab(page, "TabContainer_TabPanelDoorsPanel")
E.set_text(page, f"{DP}TextBoxDoors", "2")
E.set_text(page, f"{DP}TextBoxDoorsInsulated", "0")
E.set_text(page, f"{DP}TextBoxDraughtProofedDoors", "0")
E.save_close(page)
def _glazing(page):
# glazed_type 13 -> double glazed, 2022 or later (U 1.4)
return "Double post or during 2022"
def ventilation(page):
E.goto(page, "VentilationAndCooling", "WebFormVentilationAndCooling.aspx")
E.click_tab(page, "TabContainer_TabPanelVentilationPanel")
E.set_text(page, f"{VP}TextBoxIntermittentFans", "1")
cool = page.locator(f"#{E.FP}{VP}CheckBoxFixedSpaceCooling")
if cool.count() and cool.is_checked():
E.commit(page, cool.uncheck)
E.click_tab(page, "TabContainer_TabPanelMechVent")
mv = page.locator(f"#{E.FP}{MV}CheckBoxMechanicalVentilation")
if mv.count() and mv.is_checked():
E.commit(page, mv.uncheck)
E.click_tab(page, "TabContainer_TabPanelAirPressureTest")
E.set_select(page, f"{APT}DropDownListTestMethod", "Not available")
E.click_tab(page, "TabContainer_TabPanelLighting")
E.set_text(page, f"{LP}TextBoxLightsTotal", "6")
E.set_text(page, f"{LP}TextBoxLedLightsTotal", "0")
E.set_text(page, f"{LP}TextBoxCflLightsTotal", "0")
E.save_close(page)
def space_heating(page):
E.goto(page, "SpaceHeating", "WebFormSpaceHeating.aspx")
page.wait_for_timeout(1000)
mhc = page.locator(f"#{E.MH1}TextBoxMainHeatingCode")
code = mhc.input_value() if mhc.count() else ""
if code and code not in ("0",):
print(f"clearing leftover MainHeatingCode {code}")
page.evaluate("""(id)=>{const e=document.getElementById(id);if(e){e.value='';
e.dispatchEvent(new Event('change',{bubbles:true}));}}""", f"{E.MH1}TextBoxMainHeatingCode")
page.wait_for_timeout(400)
E.save_close(page)
return
E.set_heating_dialog(page, f"{MH1B}ButtonMainHeatingCode",
"^Gas", "Mains gas", "Boilers", "Post 1998", "Condensing",
"Combi condens")
print("code:", page.locator(f"#{E.MH1}TextBoxMainHeatingCode").input_value())
E.set_heating_dialog(page, f"{MH1B}ButtonMainHeatingControls",
"^Boilers", "^Standard", "CBE Programmer, room thermostat and TRVs")
print("control:", page.locator(f"#{E.MH1}TextBoxMainHeatingControls").input_value())
E.set_select(page, "DropDownListSecondaryHeatingPresent", "No")
E.save_close(page)
def _unused_secondary(page):
E.goto(page, "SpaceHeating", "WebFormSpaceHeating.aspx")
page.wait_for_timeout(600)
E.set_select(page, "DropDownListSecondaryHeatingPresent", "Yes")
page.wait_for_timeout(900)
E.set_heating_dialog(page, "ButtonSecondaryHeatingCode",
"^Gas", "Mains gas", "Room Heater")
tb = page.locator(f"#{E.FP}TextBoxSecondaryHeatingCode")
print("secondary code:", tb.input_value() if tb.count() else "?")
E.save_close(page)
def water_heating(page):
E.goto(page, "WaterHeating", "WebFormWaterHeating.aspx")
E.click_tab(page, "TabContainer_TabPanelWaterHeating")
page.wait_for_timeout(400)
E.clear_hot_water_cylinder(page)
E.set_heating_dialog(page, f"{WH}ButtonWaterHeatingCode",
"From Space Heating", "From the primary heating system")
print("water code:", page.locator(f"#{E.FP}{WH}TextBoxWaterHeatingCode").input_value())
E.save_close(page)
def download(page):
_SAMPLE_DIR.mkdir(parents=True, exist_ok=True)
E.goto(page, "Recommendations", "WebFormRecommendations.aspx")
page.wait_for_timeout(1500)
with page.expect_navigation(wait_until="networkidle", timeout=40000):
page.click("#ContentBody_buttonActionSummary_Link", timeout=15000)
page.wait_for_timeout(2000)
for btn, fname in (("ContentBody_ContentPlaceHolder1_LinkButtonSummary", "elmhurst_summary.pdf"),
("ContentBody_ContentPlaceHolder1_ButtonDebugInforPdf", "elmhurst_worksheet.pdf")):
try:
with page.expect_download(timeout=60000) as dl:
page.click(f"#{btn}", timeout=15000)
dl.value.save_as(str(_SAMPLE_DIR / fname))
print("saved", fname)
except Exception as e:
print("!! download failed", fname, type(e).__name__, str(e)[:120])
def cleanup(page):
"""Leave the shared assessment neutral for the next build."""
E.reset_transient_state(page)
print("reset transient state (RR/secondary/meter)")
_ORDER = ["property_description", "dimensions", "walls", "roofs", "floors",
"openings", "ventilation", "space_heating",
"water_heating", "download", "cleanup"]
def main():
if len(sys.argv) < 2 or sys.argv[1] not in _ORDER:
print("usage: build_100061275133.py <" + "|".join(_ORDER) + ">")
return 2
with E.session() as (ctx, page):
globals()[sys.argv[1]](page)
print("done:", sys.argv[1], "->", page.url)
return 0
if __name__ == "__main__":
raise SystemExit(main())

View file

@ -863,6 +863,28 @@ _EXPECTATIONS: Final[tuple[RealCertExpectation, ...]] = (
cert_num="uprn-217091901",
sap_score=61,
),
# UPRN 100061275133 (corpus; no cert number lodged). RdSAP-21.0.1 native —
# 2-storey SEMI-DETACHED HOUSE, band B, SOLID BRICK 400 mm with EXTERNAL
# insulation, pitched 300 mm loft, SUSPENDED uninsulated ground floor,
# mains-gas COMBI, double glazing (glazed_type 13, 2022+), TFA 78.
# Lodged 76 / engine 77 (77.24). Built in accredited Elmhurst RdSAP10
# (evidence saved: elmhurst_summary.pdf / elmhurst_worksheet.pdf): the
# checkable FABRIC matches the engine closely — ROOF (300 mm loft, U 0.14)
# and WINDOWS (15.32 m² × eff U 1.3258 = 20.3106 W/K) match EXACTLY; the
# externally-insulated WALL is U 0.29 (engine) vs 0.30 (Elmhurst) — which
# VALIDATES the slice-5 external-insulation default (100 mm added over solid
# brick, `_WALL_ADDED_INSULATION_UNKNOWN_THICKNESS_MM`); the suspended
# ground floor is U 0.68 (engine) vs 0.70 (Elmhurst). (Elmhurst''s overall
# worksheet SAP 67 is NOT comparable — the reused-assessment combi entered
# as a "Standard Combi" carries a ~50 kWh/month Table-3a keep-hot loss the
# lodged assessment did not, a build-side heating artifact, not an engine
# gap.) Engine 77 sits within 1.24 of lodged; PINNED to the observed 77.
RealCertExpectation(
schema="RdSAP-Schema-21.0.1",
sample="uprn_100061275133",
cert_num="uprn-100061275133",
sap_score=77,
),
)