mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-06-08 11:17:27 +00:00
Slice S0380.31: deduct alt-wall window opening from (31) net external area — closes cert 2636 cantilever residual -0.015 → -2.4e-6
SAP 10.2 Appendix K eqn (K2) p.84:
HTB = y × Σ(Aexp)
where Aexp is "the total area of external elements calculated at
worksheet (31)". The worksheet (31) column header reads "Total NET
area of external elements" — net of openings.
Cert 2636 (dr87-0001-000898 line 187): (31) = 160.33 m² =
47.70 main net + 11.57 alt net + 42.92 roof + 39.18 ground floor
+ 3.74 cantilever + 11.52 windows + 3.70 doors.
Pre-fix cascade summed the alt-wall at its 12.76 m² gross (no
opening deduction) — (31) was 161.52, driving (36) to 24.228 vs
worksheet 24.0495 (Δ +0.1785 W/K). That drift propagated through
(39) HTC → MIT → space heating, leaving cert 2636 at Δ -0.015
SAP — the only ASHP cohort cert above the 1e-4 floor.
`alt_walls_total_area` aggregates per-alt-wall gross at line 736;
this slice subtracts `alt_window_area` from it in the (31) sum so
the alt-wall contribution is net, matching the (29a) net-area
convention already applied per-element to the A×U sums.
Cohort-1 ASHP cohort: 9/9 certs < 1e-4 Summary path (was 8/9 with
cert 2636 at -0.015). Cert 2636 API path also closes to < 1e-4 —
the bug was path-symmetric in the cascade, not in either mapper.
Cohort-2 unchanged at 33 exact + 5 ≤0.07.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
8c269dbe2d
commit
28e5265df2
3 changed files with 71 additions and 6 deletions
|
|
@ -936,6 +936,39 @@ def test_summary_2636_full_chain_sap_within_spec_floor_of_worksheet() -> None:
|
|||
assert abs(result.sap_score_continuous - worksheet_unrounded_sap) < _ASHP_COHORT_CHAIN_TOLERANCE
|
||||
|
||||
|
||||
def test_summary_2636_thermal_bridging_excludes_alt_wall_window_opening_per_sap_10_2_appendix_k() -> None:
|
||||
# Arrange — cert 2636 has BP0 with an alt-wall (gross 12.76 m²)
|
||||
# carrying one 1.19 m² alt-wall window (`window_wall_type=2`).
|
||||
#
|
||||
# SAP 10.2 Appendix K eqn (K2) p.84: HTB = y × Σ(Aexp), where
|
||||
# Aexp is "the total area of external elements calculated at
|
||||
# worksheet (31)". Worksheet line 187 (cert 2636 dr87-0001-000898)
|
||||
# labels (31) "Total NET area of external elements" — net of
|
||||
# openings. Cert 2636 worksheet (31) = 160.33 m² = 47.70 main net
|
||||
# + 11.57 alt net + 42.92 roof + 39.18 ground floor + 3.74
|
||||
# cantilever + 11.52 windows + 3.70 doors.
|
||||
#
|
||||
# Pre-S0380.31 the cascade summed the alt-wall at its 12.76 m²
|
||||
# gross (no opening deduction) — (31) was 161.52 → (36) = 24.228,
|
||||
# worksheet (36) = 24.0495, Δ +0.1785 W/K. That drift propagated
|
||||
# through (39) HTC → MIT → space heating, leaving the cert at
|
||||
# Δ -0.015 SAP — the only ASHP cohort cert above the 1e-4 floor.
|
||||
pages = _summary_pdf_to_textract_style_pages(_SUMMARY_000898_PDF)
|
||||
site_notes = ElmhurstSiteNotesExtractor(pages).extract()
|
||||
epc = EpcPropertyDataMapper.from_elmhurst_site_notes(site_notes)
|
||||
|
||||
# Act
|
||||
result = calculate_sap_from_inputs(
|
||||
cert_to_inputs(epc, prices=SAP_10_2_SPEC_PRICES)
|
||||
)
|
||||
|
||||
# Assert — worksheet (36) = 24.0495 W/K to 4 d.p.; full SAP
|
||||
# cascade lands within the 1e-4 spec-precision floor of the
|
||||
# worksheet's 86.2641.
|
||||
assert abs(result.intermediate["thermal_bridging_w_per_k"] - 24.0495) <= 1e-4
|
||||
assert abs(result.sap_score_continuous - 86.2641) <= 1e-4
|
||||
|
||||
|
||||
def test_summary_mapper_raises_on_unmapped_cylinder_size_label() -> None:
|
||||
# Arrange — start from a real cohort cert (any extracted site
|
||||
# notes) and inject an unmapped §15.1 "Cylinder Size" label
|
||||
|
|
@ -1498,6 +1531,26 @@ def test_api_2636_cantilever_floor_surfaces_as_exposed_floor() -> None:
|
|||
)
|
||||
|
||||
|
||||
def test_api_2636_thermal_bridging_excludes_alt_wall_window_opening_per_sap_10_2_appendix_k() -> None:
|
||||
# Arrange — API-path mirror of the Summary-path (31) NET pin.
|
||||
# The Summary EPC and API EPC for cert 2636 produce identical
|
||||
# cascade output once the alt-wall window opening is deducted
|
||||
# from (31) per SAP 10.2 Appendix K eqn (K2) p.84. Worksheet (36)
|
||||
# = 24.0495 W/K, worksheet "SAP value" 86.2641 — cascade closes
|
||||
# to the 1e-4 spec-precision floor on the API path too.
|
||||
doc = json.loads(_API_2636_JSON.read_text())
|
||||
epc = EpcPropertyDataMapper.from_api_response(doc)
|
||||
|
||||
# Act
|
||||
result = calculate_sap_from_inputs(
|
||||
cert_to_inputs(epc, prices=SAP_10_2_SPEC_PRICES)
|
||||
)
|
||||
|
||||
# Assert
|
||||
assert abs(result.intermediate["thermal_bridging_w_per_k"] - 24.0495) <= 1e-4
|
||||
assert abs(result.sap_score_continuous - 86.2641) <= 1e-4
|
||||
|
||||
|
||||
def test_api_2636_alt_wall_openings_deducted_from_alt_not_main() -> None:
|
||||
# Arrange — cert 2636 has BP0 with `sap_alternative_wall_1`
|
||||
# (area 12.76 m², cavity unfilled at age D → U=0.70) and 7
|
||||
|
|
|
|||
|
|
@ -298,15 +298,15 @@ _EXPECTATIONS: tuple[_GoldenExpectation, ...] = (
|
|||
cert_number="2636-0525-2600-0401-2296",
|
||||
actual_sap=86,
|
||||
expected_sap_resid=+0,
|
||||
expected_pe_resid_kwh_per_m2=-9.5780,
|
||||
expected_pe_resid_kwh_per_m2=-9.6497,
|
||||
expected_co2_resid_tonnes_per_yr=+0.2200,
|
||||
notes=(
|
||||
"Mitsubishi PUZ-WM50VHA PCDB 104568, ASHP cohort cert with "
|
||||
"PV + 3.74 m² cantilever exposed floor + 12.76 m² alt wall. "
|
||||
"Worksheet SAP 86.2641. Slice 102f-prep.9 (cantilever) and "
|
||||
"102f-prep.10 (alt-wall opening allocation per "
|
||||
"window_wall_type) brought cascade walls to spec-exact "
|
||||
"20.024 and SAP from +0.49 → +0.03."
|
||||
"Worksheet SAP 86.2641. Slice S0380.31 deducted the alt-wall "
|
||||
"window opening (1.19 m²) from (31) total external area per "
|
||||
"SAP 10.2 Appendix K eqn K2 — closed the SAP residual from "
|
||||
"-0.015 → -2.4e-6 and shifted PE -9.578 → -9.650."
|
||||
),
|
||||
),
|
||||
_GoldenExpectation(
|
||||
|
|
|
|||
|
|
@ -847,8 +847,20 @@ def heat_transmission_from_cert(
|
|||
# alongside walls + roof + floor + openings. Cantilever contributes
|
||||
# its area to (31) too (worksheet cert 2636 line 31 = 160.33
|
||||
# includes the 3.74 m² (28b) cantilever).
|
||||
#
|
||||
# SAP 10.2 Appendix K eqn (K2) p.84: HTB = y × Σ(Aexp), where
|
||||
# Aexp is "the total area of external elements calculated at
|
||||
# worksheet (31)". The worksheet (31) column header reads
|
||||
# "Total NET area of external elements" — net of openings.
|
||||
# `alt_walls_total_area` aggregates per-alt-wall gross areas
|
||||
# (line 736); the alt-wall window opening (`alt_window_area`)
|
||||
# must be deducted here so the alt-wall contribution to (31)
|
||||
# is net, matching the worksheet net-area convention (cert
|
||||
# 2636: alt-wall gross 12.76 − alt-window 1.19 = 11.57 net).
|
||||
part_external_area = (
|
||||
main_wall_area + alt_walls_total_area + roof_area + floor_area_total
|
||||
main_wall_area
|
||||
+ (alt_walls_total_area - alt_window_area)
|
||||
+ roof_area + floor_area_total
|
||||
+ w_area + d_area + rw_area_part + rr_a_rr + rr_detailed_area
|
||||
+ cantilever_area
|
||||
)
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue