mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-06-08 11:17:27 +00:00
Cohort residual slice 9: u_rr_default_all_elements — RdSAP10 Table 18 col (4)
Adds the "Room-in-roof, all elements" U-value lookup keyed by age band, with Scotland override for age K per Table 18 footnote (2). This is the fallback U-value for the §3.9 Simplified RR cascade when no detailed per-surface lodgement is available (the "as built / unknown" path per footnote (1)). Tests cover the spec table verbatim: - A-D 2.30, E 1.50, F 0.80, G 0.50, H 0.35, I 0.35, J 0.30, - K 0.25 (England) / 0.20 (Scotland), L 0.18, M 0.15. Mid-range fallback 0.50 (matching age G) when neither age band nor country lodged — robustness contract identical to u_roof. Reference: RdSAP 10 (10-06-2025) Table 18 page 45. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
639b7ee2d7
commit
82627ebbfa
2 changed files with 91 additions and 0 deletions
|
|
@ -390,6 +390,19 @@ _ROOF_BY_AGE: Final[dict[str, float]] = {
|
|||
"K": 0.16, "L": 0.16, "M": 0.15,
|
||||
}
|
||||
|
||||
# Table 18 column (4): "Room-in-roof, all elements" default U by age band
|
||||
# when no detailed RR lodgement is available. Footnote (1) on each entry
|
||||
# confirms "value from the table applies for unknown and as built".
|
||||
# Scotland override per footnote (2): age band K → 0.20 W/m²K (other bands
|
||||
# unchanged).
|
||||
_RR_ALL_ELEMENTS_BY_AGE: Final[dict[str, float]] = {
|
||||
"A": 2.30, "B": 2.30, "C": 2.30, "D": 2.30,
|
||||
"E": 1.50, "F": 0.80, "G": 0.50, "H": 0.35,
|
||||
"I": 0.35, "J": 0.30, "K": 0.25, "L": 0.18, "M": 0.15,
|
||||
}
|
||||
_RR_ALL_ELEMENTS_SCOTLAND_OVERRIDES: Final[dict[str, float]] = {"K": 0.20}
|
||||
_RR_ALL_ELEMENTS_MID_RANGE_FALLBACK: Final[float] = 0.50
|
||||
|
||||
|
||||
_ROOF_NO_INSULATION_MARKERS: Final[tuple[str, ...]] = (
|
||||
"no insulation",
|
||||
|
|
@ -449,6 +462,30 @@ def u_roof(
|
|||
return _ROOF_BY_AGE.get(age_band.upper(), 0.4)
|
||||
|
||||
|
||||
def u_rr_default_all_elements(
|
||||
country: Optional[Country],
|
||||
age_band: Optional[str],
|
||||
) -> float:
|
||||
"""RdSAP10 Table 18 column (4) — "Room-in-roof, all elements" default
|
||||
U-value when no detailed RR lodgement is available.
|
||||
|
||||
Used as the catch-all fallback for the §3.9 Simplified RR cascade
|
||||
when assessor didn't measure individual surfaces. The spec footnote
|
||||
(1) on the table confirms: "value from the table applies for unknown
|
||||
and as built". Footnote (2) overrides age K to 0.20 W/m²K in Scotland.
|
||||
|
||||
The mid-range fallback (0.50, matching age G) fires when the cert
|
||||
lodges neither age band nor country — same robustness contract as
|
||||
`u_roof` and the other cascade helpers (never raise on missing input).
|
||||
"""
|
||||
if age_band is None:
|
||||
return _RR_ALL_ELEMENTS_MID_RANGE_FALLBACK
|
||||
band = age_band.upper()
|
||||
if country is Country.SCT and band in _RR_ALL_ELEMENTS_SCOTLAND_OVERRIDES:
|
||||
return _RR_ALL_ELEMENTS_SCOTLAND_OVERRIDES[band]
|
||||
return _RR_ALL_ELEMENTS_BY_AGE.get(band, _RR_ALL_ELEMENTS_MID_RANGE_FALLBACK)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Floor U-value (BS EN ISO 13370 + Table 19 defaults)
|
||||
# ---------------------------------------------------------------------------
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ from domain.ml.rdsap_uvalues import (
|
|||
u_floor,
|
||||
u_party_wall,
|
||||
u_roof,
|
||||
u_rr_default_all_elements,
|
||||
u_wall,
|
||||
u_window,
|
||||
)
|
||||
|
|
@ -977,3 +978,56 @@ def test_country_from_code_recognises_known_codes() -> None:
|
|||
assert Country.from_code("SCT") is Country.SCT
|
||||
assert Country.from_code("NIR") is Country.NIR
|
||||
assert Country.from_code("EAW") is Country.ENG # England-and-Wales aggregate maps to ENG
|
||||
|
||||
|
||||
def test_u_rr_default_all_elements_age_band_b_returns_table18_col4_value() -> None:
|
||||
"""RdSAP10 §5.11.4 + Table 18 column (4) — "Room-in-roof, all elements"
|
||||
as-built / unknown default. Age band B (1900-1929) → 2.30 W/m²K (the
|
||||
uninsulated row carries footnote (1): "value from the table applies
|
||||
for unknown and as built")."""
|
||||
# Arrange / Act
|
||||
result = u_rr_default_all_elements(country=Country.ENG, age_band="B")
|
||||
|
||||
# Assert
|
||||
assert result == pytest.approx(2.30, abs=0.001)
|
||||
|
||||
|
||||
def test_u_rr_default_all_elements_table18_col4_matches_spec_across_age_bands() -> None:
|
||||
"""Table 18 column (4) per RdSAP10 spec page 45:
|
||||
A-D 2.30, E 1.50, F 0.80, G 0.50, H 0.35, I 0.35, J 0.30,
|
||||
K 0.25, L 0.18, M 0.15.
|
||||
"""
|
||||
# Arrange — expected RR-all-elements U-values for England.
|
||||
expected = {
|
||||
"A": 2.30, "B": 2.30, "C": 2.30, "D": 2.30,
|
||||
"E": 1.50, "F": 0.80, "G": 0.50, "H": 0.35,
|
||||
"I": 0.35, "J": 0.30, "K": 0.25, "L": 0.18, "M": 0.15,
|
||||
}
|
||||
|
||||
# Act / Assert
|
||||
for age_band, want in expected.items():
|
||||
got = u_rr_default_all_elements(country=Country.ENG, age_band=age_band)
|
||||
assert got == pytest.approx(want, abs=0.001), (
|
||||
f"age={age_band}: got {got}, want {want}"
|
||||
)
|
||||
|
||||
|
||||
def test_u_rr_default_all_elements_scotland_age_band_k_returns_0_20_per_footnote() -> None:
|
||||
"""Table 18 footnote (2): "0.20 W/m²K in Scotland" applies to the
|
||||
age band K row of column (4). Other age bands unchanged."""
|
||||
# Arrange / Act
|
||||
result = u_rr_default_all_elements(country=Country.SCT, age_band="K")
|
||||
|
||||
# Assert
|
||||
assert result == pytest.approx(0.20, abs=0.001)
|
||||
|
||||
|
||||
def test_u_rr_default_all_elements_unknown_age_band_falls_back_to_mid_range() -> None:
|
||||
"""Robustness: no age band → return the mid-range default rather than
|
||||
raising. Picks the column (4) value at age G (0.50) as a sensible
|
||||
middle estimate, matching the cascade convention used by `u_roof`."""
|
||||
# Arrange / Act
|
||||
result = u_rr_default_all_elements(country=None, age_band=None)
|
||||
|
||||
# Assert
|
||||
assert result == pytest.approx(0.50, abs=0.001)
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue