mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-06-30 13:10:47 +00:00
fix(uvalues): cap as-built stone U at age E only, not A-E; drop insulation-state gate (RdSAP 10 Tables 6-7 footnote a, PDF p.33-35)
Commit 034d4b7c corrected two stone cases but via the wrong mechanism: it
removed the 1.7 cap for ALL age bands and gated the §5.6 formula on
`wall_insulation_type is not None`. Both diverge from RdSAP 10 Tables 6-10:
- The stone rows read "According to 5.6" for bands A-D (uncapped formula)
and "1.7 a" at band E, where footnote (a) = "Or from equations in 5.6 if
the calculated U-value is less than 1.7" → U_E = min(formula, 1.7). The
cap belongs ONLY at age E. Removing it for E let an as-built 50 mm granite
age-E wall return the formula's 6.09 instead of 1.70.
- Scotland sandstone/limestone age E defaults to 1.5 (Table 7, PDF p.35);
granite/whinstone stays 1.7.
- The insulation-state gate is not a spec rule. It sent age-A-D stone with
"insulation Unknown" (wall_insulation_type None) to the flat-1.7 table
instead of the §5.6 formula; it only "worked" for fixture 000565 Ext1
because that wall is age E, where 1.7 is correct anyway. Removed: the
age-E cap, not the gate, is what produces 000565 Ext1's worksheet 1.70.
Bands A-D stay uncapped (sandstone 400 mm → 1.90, granite 120 mm → 3.89).
Unknown-thickness handling unchanged in this commit (still flat-table; the
§3.5 Table-3 default follows separately).
pyright not installed in this container — strict type gate not run locally.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
034d4b7cd5
commit
7e187078b9
2 changed files with 126 additions and 26 deletions
|
|
@ -567,30 +567,43 @@ def u_wall(
|
|||
ctry = country if country is not None else Country.ENG
|
||||
age_idx = _age_index(age_band)
|
||||
band = _AGE_BANDS[age_idx]
|
||||
# RdSAP 10 §5.6 (PDF p.40) — uninsulated stone wall formula, age
|
||||
# bands A-E. Fires only when (a) a documentary wall thickness is lodged
|
||||
# (per §5.3 documentary-evidence rule) AND (b) the insulation STATE is
|
||||
# known (`wall_insulation_type` not None — As Built / external / internal).
|
||||
# When either is absent the cascade falls through to the Table-6
|
||||
# typical-thickness default (1.7) below: an "insulation Unknown" lodgement
|
||||
# is NOT treated as bare stone of the lodged thickness (cert 000565 Ext1:
|
||||
# granite 50 mm + insulation Unknown → Table-6 1.70 in the worksheet, NOT
|
||||
# the §5.6 formula's 6.09), and the footnote (a) "use §5.6 if the
|
||||
# calculated U-value is less than 1.7" clause governs the unknown path. When the thickness IS
|
||||
# lodged the raw §5.6 U is the spec target — it is NOT capped at 1.7,
|
||||
# because a thin/standard solid stone wall genuinely loses more than the
|
||||
# typical-thickness default (sandstone 400 mm → 1.90, granite 120 mm →
|
||||
# 3.89). §5.8 + Table 14 insulation / dry-line adjustments apply on top
|
||||
# of the raw §5.6 U₀ (cert 000565 Main alt_wall_1: granite W=120 mm
|
||||
# dry-lined → U₀=3.88 + dry-line → 2.34, matches worksheet).
|
||||
# RdSAP 10 Tables 6-10 stone rows + footnote (a) (PDF p.33-39), §5.6
|
||||
# formula (PDF p.40), §5.8 + Table 14 (PDF p.41-42). A documentary wall
|
||||
# thickness (per §5.3) routes stone in age bands A-E off the §5.6 formula,
|
||||
# NOT the flat Table-6 typical-thickness default:
|
||||
# - Bands A-D: pure §5.6 formula, UNCAPPED. The stone rows read
|
||||
# "According to 5.6" with NO 1.7 entry, because a thin/standard solid
|
||||
# stone wall genuinely loses more than the typical default (sandstone
|
||||
# 400 mm → 1.90, granite 120 mm → 3.89).
|
||||
# - Band E: the stone row reads "1.7 a"; footnote (a) = "Or from
|
||||
# equations in 5.6 if the calculated U-value is less than 1.7" →
|
||||
# U_E = min(formula, 1.7). Scotland sandstone/limestone age E defaults
|
||||
# to 1.5 (Table 7), granite/whinstone stays 1.7. The 1.7 (1.5) cap
|
||||
# belongs ONLY at age E, never A-D.
|
||||
# The insulation STATE is NOT a gate: an "as built / insulation Unknown"
|
||||
# lodgement (`wall_insulation_type` None or 4) takes the formula too. Cert
|
||||
# 000565 Ext1 (granite 50 mm, age E, insulation Unknown) → min(6.09, 1.7)
|
||||
# = 1.70, matching the U985 worksheet WITHOUT a flat-table detour — the
|
||||
# age-E cap, not an insulation gate, is what produces the 1.70.
|
||||
if (
|
||||
wall_thickness_mm is not None
|
||||
and wall_insulation_type is not None
|
||||
and band in _STONE_AGE_A_TO_E
|
||||
and construction in (WALL_STONE_GRANITE, WALL_STONE_SANDSTONE)
|
||||
):
|
||||
u0 = _u_stone_thin_wall_age_a_to_e(construction, wall_thickness_mm)
|
||||
if u0 is not None:
|
||||
# Footnote (a) cap is age-E only: clamp the as-built U to the
|
||||
# Table-6/7 age-E default (1.7, or 1.5 for Scotland sandstone/
|
||||
# limestone) when the §5.6 formula exceeds it. A-D stay uncapped.
|
||||
if band == "E":
|
||||
e_default = (
|
||||
1.5
|
||||
if ctry == Country.SCT
|
||||
and construction == WALL_STONE_SANDSTONE
|
||||
else 1.7
|
||||
)
|
||||
if u0 >= e_default:
|
||||
u0 = e_default
|
||||
# RdSAP 10 §5.8 + Table 14 (PDF p.41-42) — added External/Internal
|
||||
# insulation on a stone wall: U = 1/(1/U₀ + R_ins), with U₀ the
|
||||
# RAW §5.6 stone result (the Table-6 footnote (a) 1.7 cap does NOT
|
||||
|
|
@ -630,15 +643,12 @@ def u_wall(
|
|||
Decimal("0.01"), rounding=ROUND_HALF_UP
|
||||
)
|
||||
)
|
||||
# As-built (uninsulated, not dry-lined) stone wall of KNOWN
|
||||
# thickness, age A-E: return the raw §5.6 result. The Table-6
|
||||
# footnote (a) "< 1.7" clause governs the UNKNOWN-thickness path
|
||||
# (which falls through to the Table-6 typical 1.7 default below) —
|
||||
# NOT a documentary-thickness lodgement. A thin solid stone wall
|
||||
# genuinely has U > 1.7 (e.g. sandstone 400 mm = 1.90, granite
|
||||
# 120 mm = 3.89); capping it to 1.7 under-counts fabric loss and
|
||||
# over-rates. Confirmed against Elmhurst (age-B sandstone 400 mm
|
||||
# → 1.9) and the §5.6 Table-12 formula tests.
|
||||
# As-built (uninsulated, not dry-lined) stone wall, age A-E:
|
||||
# return the §5.6 result — uncapped for A-D, age-E-capped above.
|
||||
# A thin solid stone wall genuinely has U > 1.7 (sandstone 400 mm
|
||||
# = 1.90, granite 120 mm = 3.89); capping A-D to 1.7 under-counts
|
||||
# fabric loss and over-rates. Confirmed against Elmhurst (age-B
|
||||
# sandstone 400 mm → 1.90) and the §5.6 Table-12 formula tests.
|
||||
return u0
|
||||
known_types = {
|
||||
WALL_STONE_GRANITE, WALL_STONE_SANDSTONE, WALL_SOLID_BRICK, WALL_CAVITY,
|
||||
|
|
|
|||
|
|
@ -849,6 +849,96 @@ def test_u_wall_stone_granite_age_a_without_wall_thickness_returns_table_6_age_a
|
|||
assert abs(result - 1.7) <= 1e-3
|
||||
|
||||
|
||||
def test_u_wall_stone_granite_age_e_50mm_caps_at_table6_default_1_7() -> None:
|
||||
# Arrange — RdSAP 10 Table 6 (England, PDF p.33-34) stone row reads
|
||||
# "1.7 a" at age E, footnote (a) = "Or from equations in 5.6 if the
|
||||
# calculated U-value is less than 1.7". A 50 mm granite wall's §5.6
|
||||
# formula gives U = 45.315 × 50^(-0.513) = 6.09 (> 1.7), so the age-E
|
||||
# default 1.7 stands → min(6.09, 1.7) = 1.70. The cap is age-E ONLY:
|
||||
# bands A-D are uncapped (120 mm age-A granite = 3.89). Insulation is
|
||||
# Unknown (wall_insulation_type None) — no longer a gate. Cert 000565
|
||||
# BP Ext1 is this fixture (U985 worksheet U = 1.70).
|
||||
|
||||
# Act
|
||||
result = u_wall(
|
||||
country=Country.ENG,
|
||||
age_band="E",
|
||||
construction=WALL_STONE_GRANITE,
|
||||
insulation_thickness_mm=None,
|
||||
insulation_present=False,
|
||||
wall_insulation_type=None,
|
||||
dry_lined=False,
|
||||
wall_thickness_mm=50,
|
||||
)
|
||||
|
||||
# Assert
|
||||
assert abs(result - 1.70) <= 1e-3
|
||||
|
||||
|
||||
def test_u_wall_stone_sandstone_age_e_thick_wall_uses_5_6_formula_below_1_7() -> None:
|
||||
# Arrange — footnote (a) at age E: use the §5.6 formula when it gives
|
||||
# < 1.7. A 600 mm sandstone wall → U = 54.876 × 600^(-0.561) = 1.5165
|
||||
# (< 1.7), so the formula value is used, NOT the 1.7 default.
|
||||
|
||||
# Act
|
||||
result = u_wall(
|
||||
country=Country.ENG,
|
||||
age_band="E",
|
||||
construction=WALL_STONE_SANDSTONE,
|
||||
insulation_thickness_mm=None,
|
||||
insulation_present=False,
|
||||
wall_insulation_type=None,
|
||||
dry_lined=False,
|
||||
wall_thickness_mm=600,
|
||||
)
|
||||
|
||||
# Assert
|
||||
assert abs(result - 1.5165) <= 1e-3
|
||||
|
||||
|
||||
def test_u_wall_stone_sandstone_scotland_age_e_caps_at_1_5_not_1_7() -> None:
|
||||
# Arrange — RdSAP 10 Table 7 (Scotland, PDF p.35) sandstone/limestone
|
||||
# age E default is "1.5 a" (granite/whinstone stays 1.7). A 500 mm
|
||||
# sandstone wall's §5.6 formula = 54.876 × 500^(-0.561) = 1.68 (> 1.5),
|
||||
# so the Scotland age-E default 1.5 stands → min(1.68, 1.5) = 1.50.
|
||||
|
||||
# Act
|
||||
result = u_wall(
|
||||
country=Country.SCT,
|
||||
age_band="E",
|
||||
construction=WALL_STONE_SANDSTONE,
|
||||
insulation_thickness_mm=None,
|
||||
insulation_present=False,
|
||||
wall_insulation_type=None,
|
||||
dry_lined=False,
|
||||
wall_thickness_mm=500,
|
||||
)
|
||||
|
||||
# Assert
|
||||
assert abs(result - 1.50) <= 1e-3
|
||||
|
||||
|
||||
def test_u_wall_stone_granite_scotland_age_e_50mm_stays_capped_at_1_7() -> None:
|
||||
# Arrange — Scotland granite/whinstone age E default is 1.7 (only
|
||||
# sandstone/limestone drops to 1.5, Table 7 PDF p.35). A 50 mm granite
|
||||
# wall's formula 6.09 (> 1.7) → min(6.09, 1.7) = 1.70, NOT 1.5.
|
||||
|
||||
# Act
|
||||
result = u_wall(
|
||||
country=Country.SCT,
|
||||
age_band="E",
|
||||
construction=WALL_STONE_GRANITE,
|
||||
insulation_thickness_mm=None,
|
||||
insulation_present=False,
|
||||
wall_insulation_type=None,
|
||||
dry_lined=False,
|
||||
wall_thickness_mm=50,
|
||||
)
|
||||
|
||||
# Assert
|
||||
assert abs(result - 1.70) <= 1e-3
|
||||
|
||||
|
||||
def test_u_wall_curtain_wall_missing_age_lodgement_defaults_to_pre_2023_u_2p0_per_rdsap_5_18() -> None:
|
||||
# Arrange — when the cert lodges `Type: CW Curtain Wall` but no
|
||||
# `Curtain Wall Age` line (older Elmhurst Summary PDFs, or API EPCs
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue