mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-07-12 13:29:04 +00:00
fix(uvalues): apply §5.8 insulation R to stone walls (RdSAP 10 p.41-42)
The §5.8 Table-14 added-insulation R-value adjustment was gated to
WALL_SOLID_BRICK, so a stone (granite/sandstone) wall lodging
wall_insulation_type 1/3 ("External"/"Internal") + a thickness fell
through the §5.6 thin-wall branch and was billed at its UNINSULATED U
(e.g. sandstone 520 mm + 100 mm internal: 1.64 instead of 0.30 → ~5×
the wall heat loss). Mirror the brick insulation branch into the stone
block, feeding the RAW §5.6 U₀ into the §5.8 chain per the same rule the
brick branch and the dry-lined granite pin 000565 already follow (the
Table-6 footnote (a) 1.7 cap does not apply on the insulated path).
Corpus cert 100052159386 (sandstone 520 mm + 100 mm internal): -26.20 ->
-4.08 SAP, walls 300 -> 55 W/K. RdSAP-21.0.1 corpus within-0.5 68.6% ->
68.8% (SAP MAE 0.942 -> 0.888; PE MAE 14.3 -> 13.9; CO2 0.27 -> 0.26);
floors/ceilings ratcheted. Unit-pinned in test_rdsap_uvalues.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
0d24f5b13a
commit
c5aa5620ca
3 changed files with 116 additions and 4 deletions
|
|
@ -589,6 +589,35 @@ def u_wall(
|
||||||
):
|
):
|
||||||
u0 = _u_stone_thin_wall_age_a_to_e(construction, wall_thickness_mm)
|
u0 = _u_stone_thin_wall_age_a_to_e(construction, wall_thickness_mm)
|
||||||
if u0 is not None:
|
if u0 is not None:
|
||||||
|
# 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
|
||||||
|
# apply to the insulated path — same rule the brick branch below
|
||||||
|
# and the dry-lined granite pin 000565 follow). λ defaults to
|
||||||
|
# 0.04 W/m·K per §5.8 final note. Mirrors the WALL_SOLID_BRICK
|
||||||
|
# insulated branch; without it a stone wall lodging code 1/3 +
|
||||||
|
# a thickness was billed at its UNINSULATED U (e.g. sandstone
|
||||||
|
# 520 mm + 100 mm internal: 1.64 → 0.30), the dominant cause of
|
||||||
|
# the wall_insulation_type=3 corpus under-rate cluster.
|
||||||
|
if (
|
||||||
|
wall_insulation_type in (
|
||||||
|
_WALL_INSULATION_EXTERNAL, _WALL_INSULATION_INTERNAL,
|
||||||
|
)
|
||||||
|
and insulation_thickness_mm is not None
|
||||||
|
and insulation_thickness_mm > 0
|
||||||
|
):
|
||||||
|
r_ins = _r_insulation_table_14(
|
||||||
|
insulation_thickness_mm,
|
||||||
|
_resolve_wall_insulation_lambda_w_per_mk(
|
||||||
|
wall_insulation_thermal_conductivity
|
||||||
|
),
|
||||||
|
)
|
||||||
|
u_unrounded = 1.0 / (1.0 / u0 + r_ins)
|
||||||
|
return float(
|
||||||
|
Decimal(str(u_unrounded)).quantize(
|
||||||
|
Decimal("0.01"), rounding=ROUND_HALF_UP
|
||||||
|
)
|
||||||
|
)
|
||||||
if dry_lined:
|
if dry_lined:
|
||||||
# Round to 2 d.p. — worksheet (29a) A×U product uses
|
# Round to 2 d.p. — worksheet (29a) A×U product uses
|
||||||
# the 2-d.p.-displayed U (cf. 000565 Main alt_wall_1:
|
# the 2-d.p.-displayed U (cf. 000565 Main alt_wall_1:
|
||||||
|
|
|
||||||
|
|
@ -725,6 +725,80 @@ def test_u_wall_stone_sandstone_thin_wall_age_a_120mm_uses_5_6_sandstone_formula
|
||||||
assert abs(result - 3.7408) <= 1e-3
|
assert abs(result - 3.7408) <= 1e-3
|
||||||
|
|
||||||
|
|
||||||
|
def test_u_wall_stone_sandstone_with_internal_insulation_applies_5_8_table_14_r_value() -> None:
|
||||||
|
# Arrange — RdSAP 10 §5.8 + Table 14 (PDF p.41-42): a stone wall lodging
|
||||||
|
# External/Internal insulation (wall_insulation_type 1/3) + a thickness
|
||||||
|
# gets the same R-value adjustment as solid brick, applied to the RAW §5.6
|
||||||
|
# U₀. Mirrors corpus cert 100052159386 (Sandstone, 520 mm, 100 mm internal):
|
||||||
|
# U₀ = 54.876 × 520^(-0.561) = 1.6433
|
||||||
|
# R = 0.025 × 100 + 0.25 = 2.75 (Table 14, λ = 0.04)
|
||||||
|
# U = 1 / (1/1.6433 + 2.75) = 0.2977 → 0.30 (2 d.p.)
|
||||||
|
# Before this branch the wall was billed at its UNINSULATED U (≈1.64),
|
||||||
|
# the dominant cause of the wall_insulation_type=3 corpus under-rate cluster.
|
||||||
|
|
||||||
|
# Act
|
||||||
|
result = u_wall(
|
||||||
|
country=Country.ENG,
|
||||||
|
age_band="A",
|
||||||
|
construction=WALL_STONE_SANDSTONE,
|
||||||
|
insulation_thickness_mm=100,
|
||||||
|
insulation_present=True,
|
||||||
|
wall_insulation_type=3,
|
||||||
|
dry_lined=False,
|
||||||
|
wall_thickness_mm=520,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Assert
|
||||||
|
assert abs(result - 0.30) <= 1e-4
|
||||||
|
|
||||||
|
|
||||||
|
def test_u_wall_stone_sandstone_insulated_feeds_raw_u0_not_table_6_cap() -> None:
|
||||||
|
# Arrange — the Table-6 footnote (a) 1.7 cap applies ONLY to the as-built
|
||||||
|
# row; the insulated §5.8 path takes the RAW §5.6 U₀ (same rule the brick
|
||||||
|
# branch and the dry-lined granite pin 000565 follow). At W=120 mm the raw
|
||||||
|
# sandstone U₀ = 3.7408 (> 1.7), so the 100 mm internal result must be
|
||||||
|
# 1 / (1/3.7408 + 2.75) = 0.331 → 0.33 (raw),
|
||||||
|
# NOT the capped 1 / (1/1.7 + 2.75) = 0.30. The 0.33 vs 0.30 split proves
|
||||||
|
# the cap is bypassed on the insulated path.
|
||||||
|
|
||||||
|
# Act
|
||||||
|
result = u_wall(
|
||||||
|
country=Country.ENG,
|
||||||
|
age_band="A",
|
||||||
|
construction=WALL_STONE_SANDSTONE,
|
||||||
|
insulation_thickness_mm=100,
|
||||||
|
insulation_present=True,
|
||||||
|
wall_insulation_type=3,
|
||||||
|
dry_lined=False,
|
||||||
|
wall_thickness_mm=120,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Assert
|
||||||
|
assert abs(result - 0.33) <= 1e-4
|
||||||
|
|
||||||
|
|
||||||
|
def test_u_wall_stone_granite_with_external_insulation_applies_5_8_table_14_r_value() -> None:
|
||||||
|
# Arrange — granite/whinstone §5.6 formula + §5.8 external insulation:
|
||||||
|
# U₀ = 45.315 × 120^(-0.513) = 3.8871
|
||||||
|
# R = 0.025 × 50 + 0.25 = 1.50 (Table 14, λ = 0.04)
|
||||||
|
# U = 1 / (1/3.8871 + 1.50) = 0.567 → 0.57 (2 d.p.)
|
||||||
|
|
||||||
|
# Act
|
||||||
|
result = u_wall(
|
||||||
|
country=Country.ENG,
|
||||||
|
age_band="A",
|
||||||
|
construction=WALL_STONE_GRANITE,
|
||||||
|
insulation_thickness_mm=50,
|
||||||
|
insulation_present=True,
|
||||||
|
wall_insulation_type=1,
|
||||||
|
dry_lined=False,
|
||||||
|
wall_thickness_mm=120,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Assert
|
||||||
|
assert abs(result - 0.57) <= 1e-4
|
||||||
|
|
||||||
|
|
||||||
def test_u_wall_stone_granite_age_g_with_wall_thickness_ignores_5_6_formula_per_age_a_to_e_gate() -> None:
|
def test_u_wall_stone_granite_age_g_with_wall_thickness_ignores_5_6_formula_per_age_a_to_e_gate() -> None:
|
||||||
# Arrange — §5.6 (PDF p.40) heading explicitly scopes the formula
|
# Arrange — §5.6 (PDF p.40) heading explicitly scopes the formula
|
||||||
# to "age bands A to E". For age F onwards Table 6 gives literal
|
# to "age bands A to E". For age F onwards Table 6 gives literal
|
||||||
|
|
|
||||||
|
|
@ -102,10 +102,19 @@ _CORPUS = Path(
|
||||||
# part → SapConservatory → §6.1 window/rooflight/floor cascade + TFA, MIRRORING
|
# part → SapConservatory → §6.1 window/rooflight/floor cascade + TFA, MIRRORING
|
||||||
# the case-44 Summary path pinned to 1e-4) -> 68.6% (MAE 0.942). 5 type-4
|
# the case-44 Summary path pinned to 1e-4) -> 68.6% (MAE 0.942). 5 type-4
|
||||||
# certs were over-rating (conservatory dropped → too little heat loss).
|
# certs were over-rating (conservatory dropped → too little heat loss).
|
||||||
_MIN_WITHIN_HALF_SAP = 0.68
|
# STONE WALL + INTERNAL/EXTERNAL INSULATION (RdSAP 10 §5.8 + Table 14, p.41-42):
|
||||||
_MAX_SAP_MAE = 0.95
|
# the §5.8 added-insulation R-value adjustment was applied ONLY to WALL_SOLID_
|
||||||
_MAX_CO2_MAE_TONNES = 0.35 # t CO2 / yr vs co2_emissions_current
|
# BRICK; a stone (granite/sandstone) wall lodging wall_insulation_type 1/3 + a
|
||||||
_MAX_PE_PER_M2_MAE = 16.0 # kWh / m2 / yr vs energy_consumption_current
|
# thickness fell through the §5.6 branch and was billed at its UNINSULATED U
|
||||||
|
# (e.g. sandstone 520 mm + 100 mm internal: 1.64 instead of 0.30 → 5× wall heat
|
||||||
|
# loss). Mirroring the brick branch into the stone block recovered the worst of
|
||||||
|
# the wall_insulation_type=3 under-rate cluster (cert 100052159386 -26.2 -> -4.1
|
||||||
|
# SAP, walls 300 -> 55 W/K). within-0.5 68.6% -> 68.8% (MAE 0.942 -> 0.888;
|
||||||
|
# PE MAE 14.3 -> 13.9; CO2 MAE 0.27 -> 0.26). Unit-pinned in test_rdsap_uvalues.
|
||||||
|
_MIN_WITHIN_HALF_SAP = 0.685
|
||||||
|
_MAX_SAP_MAE = 0.89
|
||||||
|
_MAX_CO2_MAE_TONNES = 0.30 # t CO2 / yr vs co2_emissions_current
|
||||||
|
_MAX_PE_PER_M2_MAE = 14.5 # kWh / m2 / yr vs energy_consumption_current
|
||||||
|
|
||||||
|
|
||||||
def _load_corpus() -> list[dict[str, Any]]:
|
def _load_corpus() -> list[dict[str, Any]]:
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue