mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-06-08 11:17:27 +00:00
slice S-B29: parse measured U from full-SAP floor + roof descriptions
Parallel of S-B24 (walls) for the other envelope elements. Full-SAP
assessments lodge a measured/calculated U-value directly in the
description ("Average thermal transmittance X W/m²K") for floors
(~1 391 corpus certs) and roofs (~1 140 certs). Per spec:
- §5.11 (roofs) opening clause defers to assessor's value when
present
- §5.12 (floors): "Unless provided by the assessor the floor
U-value is calculated according to BS EN ISO 13370"
Both u_floor and u_roof now invoke `_measured_u_from_description`
first; if it parses a value, they return it directly and skip the
cascade. No range cap (consistent with S-B24 design — calculator
mirrors what the assessor lodged).
Parity probe at 300 certs, seed=7: headlines unchanged (same parquet
sampling gap as S-B24 — full-SAP certs filtered out upstream). Slice
correctness proved by:
- 1 unit test for u_floor measured-U parse
- 1 unit test for u_roof measured-U parse
- existing 287 tests passing, no regressions
A bulk-zip-based probe to measure the corpus-wide impact remains the
needed tooling investment (see S-B24 commit message).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
25261d5c8b
commit
3ab09845e7
2 changed files with 57 additions and 0 deletions
|
|
@ -417,6 +417,12 @@ def u_roof(
|
|||
as uninsulated.
|
||||
3. Table 18 age-band default.
|
||||
"""
|
||||
measured = _measured_u_from_description(description)
|
||||
if measured is not None:
|
||||
# Full-SAP cert lodges a measured roof U-value in the description
|
||||
# ("Average thermal transmittance X W/m²K"); spec §5.11 opening
|
||||
# clause defers to the assessor's value when present.
|
||||
return measured
|
||||
if insulation_thickness_mm == 0 and _described_as_insulated(description):
|
||||
# Spec §5.11.4 (page 44 footnote): "If retrofit insulation
|
||||
# present of unknown thickness use 50 mm". The cert encodes
|
||||
|
|
@ -478,7 +484,16 @@ def u_floor(
|
|||
age band". The cert encodes "thickness unknown" as either a missing
|
||||
field (`insulation_thickness_mm=None`) or "NI" which
|
||||
`_parse_thickness_mm` returns as 0.
|
||||
|
||||
Full-SAP assessments lodge a measured floor U-value directly in
|
||||
the description ("Average thermal transmittance X W/m²K"); when
|
||||
present this supersedes the BS EN ISO 13370 calculation per spec
|
||||
§5.12 opening clause ("Unless provided by the assessor the floor
|
||||
U-value is calculated according to BS EN ISO 13370").
|
||||
"""
|
||||
measured = _measured_u_from_description(description)
|
||||
if measured is not None:
|
||||
return measured
|
||||
if area_m2 is None or perimeter_m is None or perimeter_m <= 0:
|
||||
return 0.7
|
||||
w = (wall_thickness_mm or 300) / 1000.0
|
||||
|
|
|
|||
|
|
@ -466,6 +466,25 @@ def test_u_wall_uses_rdsap_unknown_thickness_default_of_50mm_when_insulated_but_
|
|||
# ----- Roofs -----
|
||||
|
||||
|
||||
def test_u_roof_description_with_measured_transmittance_returns_parsed_value() -> None:
|
||||
# Arrange — ~1 140 corpus certs lodge a full-SAP measured roof
|
||||
# U-value in the description, e.g. "Average thermal transmittance
|
||||
# 0.11 W/m²K". The age-band cascade is bypassed: the assessor's
|
||||
# measured/calculated value is used directly. Same contract as
|
||||
# `u_wall` (S-B24) and `u_floor` (S-B29 cycle 1).
|
||||
|
||||
# Act
|
||||
result = u_roof(
|
||||
country=Country.ENG,
|
||||
age_band="C",
|
||||
insulation_thickness_mm=None,
|
||||
description="Average thermal transmittance 0.11 W/m²K",
|
||||
)
|
||||
|
||||
# Assert
|
||||
assert result == pytest.approx(0.11, abs=0.001)
|
||||
|
||||
|
||||
def test_u_roof_ni_thickness_with_insulated_description_applies_50mm_per_section_5_11_4() -> None:
|
||||
# Arrange — 346 corpus certs lodge roof_insulation_thickness="NI"
|
||||
# (Not Indicated, parsed to 0 by _parse_thickness_mm). When the
|
||||
|
|
@ -621,6 +640,29 @@ def test_u_roof_explicit_thickness_beats_description() -> None:
|
|||
# ----- Floors -----
|
||||
|
||||
|
||||
def test_u_floor_description_with_measured_transmittance_returns_parsed_value() -> None:
|
||||
# Arrange — ~1 391 corpus certs lodge a full-SAP measured floor
|
||||
# U-value in the description, e.g. "Average thermal transmittance
|
||||
# 0.18 W/m²K". The BS EN ISO 13370 calculation is bypassed: the
|
||||
# assessor's measured/calculated value is used directly. Same
|
||||
# contract as `u_wall` (S-B24).
|
||||
|
||||
# Act
|
||||
result = u_floor(
|
||||
country=Country.ENG,
|
||||
age_band="B",
|
||||
construction=None,
|
||||
insulation_thickness_mm=None,
|
||||
area_m2=100.0,
|
||||
perimeter_m=40.0,
|
||||
wall_thickness_mm=300,
|
||||
description="Average thermal transmittance 0.18 W/m²K",
|
||||
)
|
||||
|
||||
# Assert
|
||||
assert result == pytest.approx(0.18, abs=0.001)
|
||||
|
||||
|
||||
def test_u_floor_ni_thickness_with_insulated_description_applies_50mm_per_table19_footnote() -> None:
|
||||
# Arrange — 2 413 corpus certs (~12%) lodge floors with
|
||||
# floor_insulation_thickness="NI" (Not Indicated, which our
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue