mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-06-08 11:17:27 +00:00
Slice 21d: §3 cascade pins + heat_transmission_section_from_cert helper
Extracts `heat_transmission_section_from_cert(epc)` wrapping the §3 inline call in cert_to_inputs (window-area/window-U/dwelling-exposure plumbing). Replaces the inline call. Adds §3 cascade pins for the four aggregate line refs: (31) total_external_element_area_m2 (33) fabric_heat_loss_w_per_k (36) thermal_bridging_w_per_k (37) total_w_per_k Results at abs=1e-4 (1/24 PASS): fixture | LINE_31 diff | LINE_33 diff | LINE_36 diff | LINE_37 diff --------|--------------|--------------|--------------|------------- 000474 | 0.0014 | 0.086 | 0.0002 | 0.086 000477 | 0.0004 | 0.105 | ✓ | 0.104 000480 | 0.006 | 0.017 | 0.0009 | 0.018 000487 | 8.82 | 37.88 | 1.32 | 39.21 000490 | 0.000 | 0.064 | 0.000 | 0.064 000516 | 0.012 | 0.183 | 0.002 | 0.184 Three buckets: - 000487 (RR fixture defect): large gaps — fixture lodges Simplified Type 1 RR but PDF has detailed §3.10 lodgement including a U=0.86 external gable. Slice 22 closes (mirrors S16a). - 000474/000477/000480/000490/000516 (precision residuals): LINE_33 drifts 0.02-0.18 W/K — sub-display-precision (PDF lodges to 4 d.p. per element, our calc combines full-precision per-storey perimeters + 4-d.p. U values). The aggregate diff of ~0.1 W/K is just over the abs=1e-4 floor but well under the worksheet's display granularity. Cascade pins now: §1 (12 PASS) + §2 (96 PASS) + §3 (1 PASS, 23 FAIL). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
5b7dbe2c21
commit
024244ec59
2 changed files with 73 additions and 15 deletions
|
|
@ -76,6 +76,7 @@ from domain.sap.worksheet.internal_gains import (
|
|||
)
|
||||
from domain.sap.worksheet.heat_transmission import (
|
||||
DwellingExposure,
|
||||
HeatTransmission,
|
||||
heat_transmission_from_cert,
|
||||
)
|
||||
from domain.sap.climate.appendix_u import external_temperature_c
|
||||
|
|
@ -690,6 +691,28 @@ def _ventilation_counts(vent: Optional[SapVentilation]) -> _VentilationCounts:
|
|||
)
|
||||
|
||||
|
||||
def heat_transmission_section_from_cert(epc: EpcPropertyData) -> HeatTransmission:
|
||||
"""SAP 10.2 §3 cert→inputs cascade for `heat_transmission_from_cert`.
|
||||
|
||||
Wraps the `_window_total_area_and_avg_u` + `_dwelling_exposure`
|
||||
derivations cert_to_inputs makes internally and returns the full
|
||||
`HeatTransmission` (every (26)..(37) line ref breakdown). Exposed
|
||||
so cascade pin tests can assert each §3 line ref against the U985
|
||||
PDF.
|
||||
"""
|
||||
window_total_area, window_avg_u = _window_total_area_and_avg_u(epc.sap_windows)
|
||||
exposure = _dwelling_exposure(epc.dwelling_type)
|
||||
return heat_transmission_from_cert(
|
||||
epc,
|
||||
window_total_area_m2=window_total_area,
|
||||
window_avg_u_value=window_avg_u,
|
||||
door_count=epc.door_count,
|
||||
insulated_door_count=epc.insulated_door_count,
|
||||
insulated_door_u_value=epc.insulated_door_u_value,
|
||||
exposure=exposure,
|
||||
)
|
||||
|
||||
|
||||
def ventilation_from_cert(epc: EpcPropertyData) -> VentilationResult:
|
||||
"""SAP 10.2 §2 cert→inputs cascade for `ventilation_from_inputs`.
|
||||
|
||||
|
|
@ -1050,20 +1073,9 @@ def cert_to_inputs(
|
|||
(inspection_date ≥ 2025-07-01) rather than a per-cert price
|
||||
override."""
|
||||
dim = dimensions_from_cert(epc)
|
||||
window_total_area, window_avg_u = _window_total_area_and_avg_u(epc.sap_windows)
|
||||
exposure = _dwelling_exposure(epc.dwelling_type)
|
||||
ht = heat_transmission_from_cert(
|
||||
epc,
|
||||
window_total_area_m2=window_total_area,
|
||||
window_avg_u_value=window_avg_u,
|
||||
door_count=epc.door_count,
|
||||
insulated_door_count=epc.insulated_door_count,
|
||||
insulated_door_u_value=epc.insulated_door_u_value,
|
||||
exposure=exposure,
|
||||
)
|
||||
|
||||
# SAP §2 ventilation cascade — see `ventilation_from_cert` for the
|
||||
# cert→inputs mapping rules + spec-default conventions.
|
||||
# SAP §3 heat transmission + §2 ventilation cascades — see the
|
||||
# respective `_from_cert` helpers for cert→inputs mapping rules.
|
||||
ht = heat_transmission_section_from_cert(epc)
|
||||
ventilation = ventilation_from_cert(epc)
|
||||
|
||||
main = _first_main_heating(epc)
|
||||
|
|
|
|||
|
|
@ -16,7 +16,11 @@ from typing import Final
|
|||
|
||||
import pytest
|
||||
|
||||
from domain.sap.rdsap.cert_to_inputs import cert_to_inputs, ventilation_from_cert
|
||||
from domain.sap.rdsap.cert_to_inputs import (
|
||||
cert_to_inputs,
|
||||
heat_transmission_section_from_cert,
|
||||
ventilation_from_cert,
|
||||
)
|
||||
from domain.sap.worksheet.dimensions import dimensions_from_cert
|
||||
from domain.sap.worksheet.tests import (
|
||||
_elmhurst_worksheet_000474 as _w000474,
|
||||
|
|
@ -187,3 +191,45 @@ def test_section_2_line_19_sheltered_sides_matches_pdf(fixture_name: str) -> Non
|
|||
assert vent.sheltered_sides == expected, (
|
||||
f"§2 (19) {fixture_name}: actual={vent.sheltered_sides}, expected={expected}"
|
||||
)
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# §3 Heat losses + heat loss parameter — LINE_31/33/36/37 aggregates
|
||||
# ============================================================================
|
||||
|
||||
_SECTION_3_PINS: Final[tuple[tuple[str, str], ...]] = (
|
||||
# (fixture_attr, HeatTransmission attr)
|
||||
("LINE_31_TOTAL_EXTERNAL_AREA_M2", "total_external_element_area_m2"),
|
||||
("LINE_33_FABRIC_HEAT_LOSS_W_PER_K", "fabric_heat_loss_w_per_k"),
|
||||
("LINE_36_THERMAL_BRIDGING_W_PER_K", "thermal_bridging_w_per_k"),
|
||||
("LINE_37_TOTAL_FABRIC_HEAT_LOSS_W_PER_K", "total_w_per_k"),
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"fixture_name,fixture_attr,result_attr",
|
||||
[
|
||||
(fix, line, attr)
|
||||
for fix in _FIXTURES
|
||||
for line, attr in _SECTION_3_PINS
|
||||
],
|
||||
ids=lambda x: x if isinstance(x, str) else None,
|
||||
)
|
||||
def test_section_3_line_refs_match_pdf(
|
||||
fixture_name: str, fixture_attr: str, result_attr: str
|
||||
) -> None:
|
||||
"""§3 cascade pins — every (31)/(33)/(36)/(37) aggregate matches the
|
||||
U985 PDF to abs=1e-4. Per-element breakdowns (26)/(27)/(28a)/(29a)/
|
||||
(30)/(32) are not currently lodged in fixture constants — they're
|
||||
asserted indirectly via the aggregates."""
|
||||
# Arrange
|
||||
mod = _FIXTURES[fixture_name]
|
||||
epc = mod.build_epc() # type: ignore[attr-defined]
|
||||
expected = getattr(mod, fixture_attr)
|
||||
|
||||
# Act
|
||||
ht = heat_transmission_section_from_cert(epc)
|
||||
actual = getattr(ht, result_attr)
|
||||
|
||||
# Assert
|
||||
_pin(actual, expected, f"§3 {fixture_attr} {fixture_name}")
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue