mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-06-30 13:10:47 +00:00
fix(heat-transmission): apply dry-lining Table 14 R=0.17 to the main wall
The main-wall `u_wall(...)` call dropped the `dry_lined` kwarg, so the RdSAP 10 §5.7/§5.8 (PDF p.40-41) Table 14 dry-lining adjustment — U_adj = 1/(1/U₀ + 0.17) for a dry-lined (incl. lath-and-plaster) uninsulated wall — was never applied to any main wall, even when the cert lodged `wall_dry_lined=Y`. The ALTERNATIVE-wall path already passes `dry_lined` (line 1367); this one-sided omission billed every dry-lined main wall at the un-adjusted (too-high) U → wall heat loss too high → SAP under-rated. Per-cert: a solid-brick (construction 3) band-A 230 mm main wall computes U₀=1.70; dry-lined it is 1/(1/1.70+0.17)=1.32 — we were 22% too high. Across the API gov-EPC sample the dry-lined `wall_construction=3` (solid brick) sub-cohort sat at 10% within-0.5 / signed -1.33. Fix: pass `dry_lined=bool(part.wall_dry_lined)` to the main-wall `u_wall` call, mirroring the alt-wall path. `part.wall_dry_lined` is already plumbed (Optional[bool], None → False). The three dry-lining branches in `u_wall` (stone §5.6, solid-brick-by-thickness §5.7, generic uninsulated bucket §5.8) are all spec-correct and already worksheet-validated (the bucket-0 cavity case against cert 7700 age-C → 1.20). Worksheet harness UNAFFECTED (47/47, 0 divergers): the Elmhurst/Summary extractor only captures dry-lining for ALTERNATIVE walls (Summary §7), never the main wall, so `part.wall_dry_lined` stays None on that path — this is a pure API-path improvement. API gauge: within-0.5 60.1% -> 64.4% (mean|err| 1.163 -> 1.085, signed -0.097 -> +0.049). Both affected buckets improved with no overshoot: solid brick (wc=3) 50% -> 57% within-0.5; cavity (wc=4, dry-lined via the §5.8 bucket-0 path) 68% -> 72%. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
b7d283cd3a
commit
781efd75c0
2 changed files with 50 additions and 0 deletions
|
|
@ -812,6 +812,12 @@ def heat_transmission_from_cert(
|
|||
# code feeds the documentary-evidence R-value calc when a
|
||||
# measured wall thickness is also present (else ignored).
|
||||
wall_insulation_thermal_conductivity=part.wall_insulation_thermal_conductivity,
|
||||
# RdSAP 10 §5.7/§5.8 (PDF p.40-41), Table 14 — a dry-lined
|
||||
# (incl. lath-and-plaster) uninsulated wall adds R=0.17.
|
||||
# The alt-wall path already passes this; the main wall must
|
||||
# too, else every lodged `wall_dry_lined=Y` main wall is
|
||||
# billed at the un-adjusted U.
|
||||
dry_lined=bool(part.wall_dry_lined),
|
||||
)
|
||||
# When the per-bp `roof_insulation_thickness` is explicitly lodged
|
||||
# as 0 (uninsulated — e.g. cert 001479 Ext2 PS sloping ceiling
|
||||
|
|
|
|||
|
|
@ -1275,6 +1275,50 @@ def test_corridor_door_on_sheltered_alt_wall_uses_table26_u_1p4() -> None:
|
|||
assert with_corridor.fabric_heat_loss_w_per_k < no_corridor.fabric_heat_loss_w_per_k
|
||||
|
||||
|
||||
def test_main_wall_dry_lining_applies_table_14_resistance() -> None:
|
||||
# Arrange — RdSAP 10 §5.7/§5.8 (PDF p.40-41), Table 14: a dry-lined
|
||||
# (including lath-and-plaster) uninsulated wall adds R=0.17 m²K/W:
|
||||
# U_adj = 1/(1/U₀ + 0.17). A solid-brick (construction 3) age-A wall
|
||||
# with a measured 230 mm thickness has U₀=1.70 (Table 13, 200-280 mm
|
||||
# band) → dry-lined U=1/(1/1.70+0.17)=1.32 (2 d.p.). The alt-wall path
|
||||
# already applies this; the MAIN wall dropped the `dry_lined` kwarg, so
|
||||
# every lodged `wall_dry_lined=Y` main wall was billed at the un-adjusted
|
||||
# U — under-rating solid-brick stock (API wall_construction=3 cohort:
|
||||
# 48 dry-lined certs at 10% within-0.5, signed -1.33).
|
||||
from dataclasses import replace
|
||||
|
||||
base_part = make_building_part(
|
||||
construction_age_band="A",
|
||||
wall_construction=3, # solid brick
|
||||
wall_insulation_type=0, # uninsulated (as-built)
|
||||
floor_dimensions=[
|
||||
make_floor_dimension(
|
||||
total_floor_area_m2=50.0, room_height_m=2.5,
|
||||
party_wall_length_m=0.0, heat_loss_perimeter_m=28.0, floor=0,
|
||||
),
|
||||
],
|
||||
)
|
||||
not_dry = replace(base_part, wall_thickness_mm=230, wall_dry_lined=False)
|
||||
dry = replace(base_part, wall_thickness_mm=230, wall_dry_lined=True)
|
||||
epc_not_dry = make_minimal_sap10_epc(
|
||||
total_floor_area_m2=50.0, country_code="ENG", sap_building_parts=[not_dry],
|
||||
)
|
||||
epc_dry = make_minimal_sap10_epc(
|
||||
total_floor_area_m2=50.0, country_code="ENG", sap_building_parts=[dry],
|
||||
)
|
||||
|
||||
# Act
|
||||
ht_not_dry = heat_transmission_from_cert(epc_not_dry, door_count=0)
|
||||
ht_dry = heat_transmission_from_cert(epc_dry, door_count=0)
|
||||
|
||||
# Assert — same net wall area, so the W/K ratio is the U ratio: the
|
||||
# dry-lined wall is 1.32/1.70 = 0.776× the as-built wall.
|
||||
assert ht_not_dry.walls_w_per_k > 0.0
|
||||
expected_ratio = 1.32 / 1.70
|
||||
assert abs(ht_dry.walls_w_per_k / ht_not_dry.walls_w_per_k - expected_ratio) <= 0.005
|
||||
assert ht_dry.fabric_heat_loss_w_per_k < ht_not_dry.fabric_heat_loss_w_per_k
|
||||
|
||||
|
||||
def test_window_uses_effective_u_value_with_curtain_resistance_per_sap10_2_section_3_2() -> None:
|
||||
"""SAP10.2 §3.2: the window U-value used for heat-transmission is the
|
||||
effective form `U_eff = 1/(1/U_raw + 0.04)` — the 0.04 m²K/W is the
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue