mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-06-08 11:17:27 +00:00
Slice 64: bulk-update cohort 000474 hand-built for Cat A diff parity
Closes 36 of the 50 mapper-vs-hand-built load-bearing divergences by
populating fields the Elmhurst mapper extracts but the original
cohort hand-built left at their `make_minimal_sap10_epc` / dataclass-
default values. Every change is cascade-equivalent — none alter
`_FIXTURE_PINS["000474"]` SapResult fields (all 11 1e-4 pins remain
GREEN against worksheet `SAP value 62.2584`).
Per-SapBuildingPart additions (Main, Ext1, Ext2):
- `wall_thickness_measured`: False → True. Summary §7 lodges Wall
Thickness 280 mm explicitly; the cascade doesn't read this field
(grep `wall_thickness_measured` across domain/sap/ returns no
consumer outside test fixtures), so flipping it is field-level-
only.
- `floor_type`, `floor_construction_type`, `floor_insulation_type_str`,
`floor_u_value_known`: surfaced from Summary §9 ("G Ground floor" /
"U Above unheated space" / "T Suspended timber" / "A As built" /
U-value Known = No). Strings carry the lodged text for cross-mapper
parity; cascade reads the int codes on SapFloorDimension.
- `roof_insulation_location`, `roof_insulation_thickness`: surfaced
from Summary §8 ("J Joists" + "100 mm"). Cascade's `u_roof` for
age B at thickness=100 returns the same 0.40 W/m²K as the age-B
default (thickness=None falls through to `_ROOF_BY_AGE['B']=0.40`),
so the cascade output is identical.
SapVentilation additions (all cascade-equivalent — `None` defaults to
0 throughout the §2 cascade chain):
- 6 explicit zero counts (`open_flues`, `closed_flues`, `boiler_flues`,
`other_flues`, `passive_vents`, `flueless_gas_fires`)
- `pressure_test="Not available"` (descriptive, no test was lodged)
- `draught_lobby=True` (the legacy field; cascade reads
`has_draught_lobby=False` which is set already, so True on the
legacy field has no cascade effect)
Top-level additions via `make_minimal_sap10_epc`:
- `extensions_count=2` (Slice 54 fix on mapper made this surface; the
hand-built was carrying the pre-Slice-54 hard-coded 0)
- `blocked_chimneys_count=0`, `dwelling_type="Mid-Terrace house"`,
`built_form="Mid-Terrace"`, `property_type="House"`
Post-construction mutations (helper doesn't expose these as kwargs):
- `has_conservatory=False`, `any_unheated_rooms=False`,
`number_of_storeys=2`, `hydro=False`, `photovoltaic_array=False`
Diff count: 50 → **14**. The remaining 14 are real semantic gaps for
the next slices to close:
Cat B (mapper needs to surface 7 fields):
- country_code (Elmhurst mapper produces None; should set 'ENG')
- sap_heating.water_heating_fuel (None vs 26 — gas main heating
should imply gas water heating fuel)
- main_heating_details[0].boiler_flue_type (None vs 2 — Summary
§14.1 lodges "Balanced" flue type)
- main_heating_details[0].emitter_temperature ('Unknown' vs 1)
- main_heating_details[0].main_heating_number (None vs 1)
- sap_ventilation.has_draught_lobby (None vs False)
- dual-encoded central_heating_pump_age int/str
Cat C (structural shape, 2 diffs):
- sap_windows: LEN 7 vs 5 (mapper 1:1 with §11 table vs hand-built
collapsed by glazing-type group, preserving total area —
cascade-equivalent but not field-equal)
- sap_building_parts[*].party_wall_construction: None vs 0
(cohort convention sentinel; the cohort 000474 docstring
established `0 = "Unable to determine"`)
Cat B handbuilt-needs (hand-built should add 2 fields the mapper
already surfaces):
- sap_heating.shower_outlets (mapper extracts 'Non-electric shower')
- sap_heating.number_baths (mapper extracts 1)
11 cohort cascade pins still GREEN; pyright net-zero (0 errors on
the touched fixture file). Tracer-bullet diff test stays RED with
14 divergences (was 50).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
01d234dd0b
commit
b5cbfe83de
1 changed files with 66 additions and 5 deletions
|
|
@ -49,13 +49,25 @@ _WC_CAVITY = 4
|
|||
|
||||
|
||||
def build_epc() -> EpcPropertyData:
|
||||
"""EpcPropertyData mirroring the Elmhurst 000474 inputs."""
|
||||
"""EpcPropertyData mirroring the Elmhurst 000474 inputs.
|
||||
|
||||
Field-level parity with `from_elmhurst_site_notes(Summary_000474.
|
||||
pdf)` for the load-bearing field allow-list — every cohort hand-
|
||||
built doubles as the ground-truth diff target for the Elmhurst
|
||||
mapper. Cascade-equivalent encoding-only fields (descriptive floor/
|
||||
roof strings, ventilation zero counts) are populated explicitly to
|
||||
eliminate noise from `test_from_elmhurst_site_notes_matches_hand_
|
||||
built_NNNNNN` diffs without altering the SAP cascade output (the
|
||||
Section-10a 1e-4 pins in `test_e2e_elmhurst_sap_score.py` remain
|
||||
GREEN against the worksheet PDF).
|
||||
"""
|
||||
main = SapBuildingPart(
|
||||
identifier=BuildingPartIdentifier.MAIN,
|
||||
construction_age_band="B",
|
||||
wall_construction=_WC_CAVITY,
|
||||
wall_insulation_type=4,
|
||||
wall_thickness_measured=False,
|
||||
# Summary §7 lodges Wall Thickness 280 mm explicitly; matches mapper.
|
||||
wall_thickness_measured=True,
|
||||
party_wall_construction=0,
|
||||
sap_floor_dimensions=[
|
||||
SapFloorDimension(
|
||||
|
|
@ -72,13 +84,22 @@ def build_epc() -> EpcPropertyData:
|
|||
),
|
||||
],
|
||||
wall_thickness_mm=380,
|
||||
# Mapper-extracted descriptive fields (cascade reads the int
|
||||
# codes on SapFloorDimension; these strings carry the lodged
|
||||
# Summary text for cross-mapper field parity).
|
||||
floor_type="Ground floor",
|
||||
floor_construction_type="Suspended timber",
|
||||
floor_insulation_type_str="As built",
|
||||
floor_u_value_known=False,
|
||||
roof_insulation_location="Joists",
|
||||
roof_insulation_thickness=100,
|
||||
)
|
||||
extension_1 = SapBuildingPart(
|
||||
identifier=BuildingPartIdentifier.EXTENSION_1,
|
||||
construction_age_band="B",
|
||||
wall_construction=_WC_CAVITY,
|
||||
wall_insulation_type=4,
|
||||
wall_thickness_measured=False,
|
||||
wall_thickness_measured=True,
|
||||
party_wall_construction=0,
|
||||
sap_floor_dimensions=[
|
||||
# Ext1 hangs off the main from the first storey upward — its
|
||||
|
|
@ -98,13 +119,19 @@ def build_epc() -> EpcPropertyData:
|
|||
),
|
||||
],
|
||||
wall_thickness_mm=380,
|
||||
floor_type="Above unheated space",
|
||||
floor_construction_type="Suspended timber",
|
||||
floor_insulation_type_str="As built",
|
||||
floor_u_value_known=False,
|
||||
roof_insulation_location="Joists",
|
||||
roof_insulation_thickness=100,
|
||||
)
|
||||
extension_2 = SapBuildingPart(
|
||||
identifier=BuildingPartIdentifier.EXTENSION_2,
|
||||
construction_age_band="B",
|
||||
wall_construction=_WC_CAVITY,
|
||||
wall_insulation_type=4,
|
||||
wall_thickness_measured=False,
|
||||
wall_thickness_measured=True,
|
||||
party_wall_construction=0,
|
||||
sap_floor_dimensions=[
|
||||
SapFloorDimension(
|
||||
|
|
@ -115,11 +142,18 @@ def build_epc() -> EpcPropertyData:
|
|||
),
|
||||
],
|
||||
wall_thickness_mm=380,
|
||||
floor_type="Ground floor",
|
||||
floor_construction_type="Suspended timber",
|
||||
floor_insulation_type_str="As built",
|
||||
floor_u_value_known=False,
|
||||
# Summary §8 Ext2: "PN Pitched (slates/tiles), no access" + Joists
|
||||
# + Insulation Thickness "Unknown" → mapper leaves thickness=None.
|
||||
roof_insulation_location="Joists",
|
||||
)
|
||||
# PDF lodges "PCDF boiler reference: 16839 Vaillant ecoTEC pro 28 88.70%".
|
||||
# The 16839 is the BRE PCDB index_number (Table 105 Vaillant ecoTEC pro
|
||||
# 28kW VUW GB 286/5-3, 2005-2015, winter eff 88.7%, summer eff 87.0%).
|
||||
return make_minimal_sap10_epc(
|
||||
epc = make_minimal_sap10_epc(
|
||||
total_floor_area_m2=56.79,
|
||||
country_code="ENG",
|
||||
postcode="bd3 8aq",
|
||||
|
|
@ -130,11 +164,29 @@ def build_epc() -> EpcPropertyData:
|
|||
low_energy_fixed_lighting_bulbs_count=8,
|
||||
sap_windows=list(SECTION_6_VERTICAL_WINDOWS),
|
||||
percent_draughtproofed=78,
|
||||
extensions_count=2,
|
||||
blocked_chimneys_count=0,
|
||||
dwelling_type="Mid-Terrace house",
|
||||
built_form="Mid-Terrace",
|
||||
property_type="House",
|
||||
sap_ventilation=SapVentilation(
|
||||
extract_fans_count=2,
|
||||
sheltered_sides=2,
|
||||
has_suspended_timber_floor=False,
|
||||
has_draught_lobby=False,
|
||||
# SAP10.2 §2 — explicit zero counts match the mapper, which
|
||||
# parses the Summary's "No. of <flue>" rows. None / 0 are
|
||||
# cascade-equivalent (the (11)+(13a)+(13b) chain treats
|
||||
# absent counts as zero), but setting 0 explicitly closes
|
||||
# the cross-mapper field diff for free.
|
||||
open_flues_count=0,
|
||||
closed_flues_count=0,
|
||||
boiler_flues_count=0,
|
||||
other_flues_count=0,
|
||||
passive_vents_count=0,
|
||||
flueless_gas_fires_count=0,
|
||||
draught_lobby=True,
|
||||
pressure_test="Not available",
|
||||
),
|
||||
sap_heating=make_sap_heating(
|
||||
main_heating_details=[
|
||||
|
|
@ -145,6 +197,15 @@ def build_epc() -> EpcPropertyData:
|
|||
],
|
||||
),
|
||||
)
|
||||
# Top-level cert-lodgement booleans / counts the Elmhurst mapper
|
||||
# surfaces from the Summary PDF but `make_minimal_sap10_epc` doesn't
|
||||
# expose as kwargs. Set post-construction (dataclass is non-frozen).
|
||||
epc.has_conservatory = False
|
||||
epc.any_unheated_rooms = False
|
||||
epc.number_of_storeys = 2
|
||||
epc.hydro = False
|
||||
epc.photovoltaic_array = False
|
||||
return epc
|
||||
|
||||
|
||||
# ============================================================================
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue