diff --git a/backend/documents_parser/tests/test_summary_pdf_mapper_chain.py b/backend/documents_parser/tests/test_summary_pdf_mapper_chain.py index d1bbbd3e..65304656 100644 --- a/backend/documents_parser/tests/test_summary_pdf_mapper_chain.py +++ b/backend/documents_parser/tests/test_summary_pdf_mapper_chain.py @@ -45,7 +45,11 @@ from datatypes.epc.domain.mapper import ( _elmhurst_glazing_type_code, # pyright: ignore[reportPrivateUsage] ) from domain.sap10_calculator.calculator import calculate_sap_from_inputs -from domain.sap10_calculator.rdsap.cert_to_inputs import SAP_10_2_SPEC_PRICES, cert_to_inputs +from domain.sap10_calculator.rdsap.cert_to_inputs import ( + SAP_10_2_SPEC_PRICES, + cert_to_inputs, + heat_transmission_section_from_cert, +) from domain.sap10_ml.rdsap_uvalues import u_party_wall from tests.domain.sap10_calculator.worksheet import ( _elmhurst_worksheet_000474 as _w000474, @@ -142,6 +146,22 @@ def test_summary_001431_case20_extracts_all_five_section11_windows() -> None: assert len(survey.windows) == 5 +def test_summary_001431_case20_fabric_heat_loss_matches_worksheet_line_33() -> None: + # Arrange — sim case 20's room-in-roof (type 2, Detailed) lodges two + # "Stud Wall" surfaces at §8.1 Default U-value 0.00, which the P960 + # worksheet §3 excludes from fabric heat loss: (33) = 285.9847 W/K. + pages = _summary_pdf_to_textract_style_pages(_SUMMARY_001431_CASE20_PDF) + epc = EpcPropertyDataMapper.from_elmhurst_site_notes( + ElmhurstSiteNotesExtractor(pages).extract() + ) + + # Act + ht = heat_transmission_section_from_cert(epc) + + # Assert + assert abs(ht.fabric_heat_loss_w_per_k - 285.9847) <= 1e-4 + + def test_summary_000474_mapper_produces_three_building_parts() -> None: # Arrange — cert U985-0001-000474 is a mid-terrace with 3 building # parts (Main + 2 extensions) per the hand-built worksheet fixture diff --git a/datatypes/epc/domain/mapper.py b/datatypes/epc/domain/mapper.py index cce444ab..80b64774 100644 --- a/datatypes/epc/domain/mapper.py +++ b/datatypes/epc/domain/mapper.py @@ -3832,6 +3832,16 @@ def _map_elmhurst_rir_surface( # the same Simplified RR (scalar gable fields, no roof-going # detailed_surfaces; cert 6035) and the gables-only cert 000565. # Detailed (§3.10) assessments DO measure these surfaces — keep them. + # An RR stud wall (internal knee wall below the slope) is a heat-loss + # surface ONLY when Elmhurst lodges a positive §8.1 Default U-value + # (cert 000565 Ext2 Detailed: 0.31 / 0.10 — real exposed knee walls). + # A Default U-value of 0.00 marks an internal stud wall the P960 + # worksheet excludes from BOTH fabric heat loss (§3) and total exposed + # area (31): sim case 20's (33)=285.9847 and (31)=239.68 both omit its + # 2×4 m² studs. Drop only the U=0 (internal) ones; positive-U studs + # fall through to the Table-17 path like slopes/ceilings. + if kind == "stud_wall" and surface.default_u_value == 0.0: + return None if is_simplified and kind in ("slope", "flat_ceiling", "stud_wall"): return None u_value_override: Optional[float] = None