mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-06-30 13:10:47 +00:00
fix(elmhurst): read main-wall dry-lining + fix last-RR-row U over-read
Two compensating Summary-extractor bugs surfaced by simulated case 43 (a 2-BP mid-terrace with a detailed room-in-roof + a dry-lined extension wall). Their fabric errors nearly cancelled (walls net −0.76 W/K), hiding both behind a deceptively small +0.05 SAP delta. Bug 1 — main/extension wall dry-lining never read. The §7 "Dry-lining: Yes/No" line was parsed only for ALTERNATIVE walls; the main/extension WallDetails dropped it, so a dry-lined solid wall was billed at its un-adjusted base U. RdSAP 10 §5.8 + Table 14: a dry-lined uninsulated wall adds R=0.17 → U = 1/(1/U_base + 0.17). Case 43 Ext1: solid brick 1.70 → 1.32. Added `WallDetails.dry_lined`, read it in the extractor (both the main-wall builder and the As-Main copy), threaded it to the domain `wall_dry_lined` (emit None when undried — cascade-equivalent to False, keeps the field absent for the non-dry-lined majority). Bug 2 — the LAST room-in-roof surface row's U over-read. The per-row token scan stops at the next RIR-row name; the final surface (no successor) over- read into the following section, shifting the trailing-token slotting and silently zeroing its `default_u` (case 43 Common Wall 2: 1.90 → 0.00 → the 2.4 m² common wall billed at U=0 instead of the main-wall 1.90). Stop the scan at the row's natural end — the "Yes"/"No" u_value_known flag plus the trailing u_value numeric. Case 43 now reproduces the P960 EXACTLY: (29a) walls 74.5800, (33) fabric 172.7844, continuous SAP 73.2332 = (258), CO2 3518.30 = (272), all <1e-4 (was SAP +0.0455 / CO2 −8.04). Harness 47/47 0 raised; regression = the 3 pre-existing fails; pyright net-zero (51=51). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
8a70d22278
commit
a33707f851
4 changed files with 32 additions and 1 deletions
|
|
@ -290,6 +290,10 @@ class ElmhurstSiteNotesExtractor:
|
|||
party_wall_type=self._local_str(lines, "Party Wall Type"),
|
||||
thickness_mm=thickness_mm,
|
||||
insulation_thickness_mm=insulation_thickness_mm,
|
||||
# Summary §7 "Dry-lining: Yes/No" on the main/extension wall.
|
||||
# RdSAP 10 §5.8 + Table 14 dry-lining R=0.17 adjustment. The
|
||||
# alt-wall path reads its own "Alternative Wall N Dry-lining".
|
||||
dry_lined=self._local_bool(lines, "Dry-lining"),
|
||||
alternative_walls=self._alternative_walls_from_lines(lines),
|
||||
# Summary §7 lodges the per-BP "Curtain Wall Age" line only
|
||||
# when `Type: CW Curtain Wall`. Per RdSAP 10 §5.18 (PDF
|
||||
|
|
@ -548,6 +552,20 @@ class ElmhurstSiteNotesExtractor:
|
|||
if self._is_next_rir_row(lines[j]):
|
||||
break
|
||||
tokens.append(lines[j])
|
||||
# Every RIR row ends with [default_u, "Yes"/"No", u_value]; the
|
||||
# "Yes"/"No" is the unique u_value_known marker (gable types and
|
||||
# insulation cells never take that value). Stop once we've
|
||||
# appended that flag plus the trailing u_value numeric so the
|
||||
# LAST surface row (no next-row name to bound it) does not
|
||||
# over-read into the following section and shift the trailing
|
||||
# token slotting — which silently zeroed Common Wall 2's
|
||||
# default_u (case 43: 1.90 -> 0.00).
|
||||
if (
|
||||
len(tokens) >= 2
|
||||
and tokens[-2] in ("Yes", "No")
|
||||
and self._RIR_NUMERIC_RE.match(tokens[-1])
|
||||
):
|
||||
break
|
||||
# First two numerics = length, height
|
||||
length = float(tokens[0]) if tokens and self._RIR_NUMERIC_RE.match(tokens[0]) else 0.0
|
||||
height = float(tokens[1]) if len(tokens) > 1 and self._RIR_NUMERIC_RE.match(tokens[1]) else 0.0
|
||||
|
|
@ -698,6 +716,7 @@ class ElmhurstSiteNotesExtractor:
|
|||
party_wall_type=ext_party_wall_type,
|
||||
thickness_mm=main_walls.thickness_mm,
|
||||
insulation_thickness_mm=main_walls.insulation_thickness_mm,
|
||||
dry_lined=main_walls.dry_lined,
|
||||
alternative_walls=self._alternative_walls_from_lines(wall_lines),
|
||||
)
|
||||
else:
|
||||
|
|
|
|||
|
|
@ -505,7 +505,7 @@ class SapBuildingPart:
|
|||
building_part_number: Optional[int] = (
|
||||
None # Not sure how we get this from site notes
|
||||
)
|
||||
wall_dry_lined: Optional[bool] = None # Don't think we have this in site notes
|
||||
wall_dry_lined: Optional[bool] = None # Summary §7 "Dry-lining: Yes/No"
|
||||
wall_thickness_mm: Optional[int] = None
|
||||
# Union[str, int]: a numeric mm value when the API lodges
|
||||
# `wall_insulation_thickness == "measured"` (resolved from the
|
||||
|
|
|
|||
|
|
@ -4436,6 +4436,12 @@ def _map_elmhurst_building_part(
|
|||
wall_is_basement=_elmhurst_wall_is_basement(walls.wall_type),
|
||||
wall_insulation_type=_elmhurst_wall_insulation_int(walls.insulation),
|
||||
wall_thickness_measured=not walls.thickness_unknown,
|
||||
# Summary §7 "Dry-lining: Yes" → RdSAP 10 §5.8 Table 14 R=0.17
|
||||
# adjustment in the cascade (`dry_lined=bool(part.wall_dry_lined)`).
|
||||
# Emit None (not False) when undried so the field stays absent for
|
||||
# the non-dry-lined majority (cascade-equivalent: bool(None) == False);
|
||||
# only a lodged "Yes" populates it.
|
||||
wall_dry_lined=walls.dry_lined or None,
|
||||
party_wall_construction=_elmhurst_party_wall_construction_int(
|
||||
walls.party_wall_type
|
||||
),
|
||||
|
|
|
|||
|
|
@ -94,6 +94,12 @@ class WallDetails:
|
|||
# "Insulation Thickness" / "100 mm" line pair when a composite or
|
||||
# retrofit insulation is recorded. None when the PDF omits the line.
|
||||
insulation_thickness_mm: Optional[int] = None
|
||||
# Summary §7 "Dry-lining: Yes/No" on the main/extension wall (distinct
|
||||
# from the per-alt-wall `AlternativeWall.dry_lined`). Per RdSAP 10
|
||||
# §5.8 + Table 14 a dry-lined uninsulated wall adds R=0.17 m²K/W →
|
||||
# U = 1/(1/U_base + 0.17). Previously unread, so dry-lined solid/
|
||||
# cavity walls were billed at the un-adjusted (higher) base U.
|
||||
dry_lined: bool = False
|
||||
# Per-BP curtain-wall installation age, lodged in Summary §7 as
|
||||
# "Curtain Wall Age" when `wall_type` is "CW Curtain Wall". Per
|
||||
# RdSAP 10 §5.18 (PDF p.48) the curtain-wall U-value keys on this
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue