diff --git a/packages/domain/src/domain/sap/worksheet/solar_gains.py b/packages/domain/src/domain/sap/worksheet/solar_gains.py index 041ba179..caca48d5 100644 --- a/packages/domain/src/domain/sap/worksheet/solar_gains.py +++ b/packages/domain/src/domain/sap/worksheet/solar_gains.py @@ -258,7 +258,12 @@ class SolarGainsResult: def _g_perpendicular(w: SapWindow) -> float: - """Table 6b g⊥ by glazing_type code, defaulting to 0.76 for unknowns.""" + """Table 6b g⊥. Prefer the cert's measured value + (`window_transmission_details.solar_transmittance`) when present — + Elmhurst lodges it for every window; Table 6b is the cascade fallback. + """ + if w.window_transmission_details is not None: + return float(w.window_transmission_details.solar_transmittance) if isinstance(w.glazing_type, int) and w.glazing_type in _G_PERPENDICULAR_BY_GLAZING_TYPE: return _G_PERPENDICULAR_BY_GLAZING_TYPE[w.glazing_type] return _G_PERPENDICULAR_DEFAULT diff --git a/packages/domain/src/domain/sap/worksheet/tests/test_solar_gains.py b/packages/domain/src/domain/sap/worksheet/tests/test_solar_gains.py index 3fa8d631..becdaa4f 100644 --- a/packages/domain/src/domain/sap/worksheet/tests/test_solar_gains.py +++ b/packages/domain/src/domain/sap/worksheet/tests/test_solar_gains.py @@ -11,7 +11,7 @@ Appendix U §U3.2 (pages 127-129), Table U4 (latitudes), Table U5 import pytest -from datatypes.epc.domain.epc_property_data import SapWindow +from datatypes.epc.domain.epc_property_data import SapWindow, WindowTransmissionDetails from domain.ml.tests._fixtures import make_minimal_sap10_epc, make_window from domain.sap.worksheet.internal_gains import OvershadingCategory from domain.sap.worksheet.solar_gains import ( @@ -63,6 +63,35 @@ def test_solar_gains_from_cert_reproduces_000490_line_83_total() -> None: ), f"(83) month {m+1}" +def test_solar_gains_from_cert_prefers_manufacturer_g_perpendicular_over_table_6b() -> None: + # Arrange — SAP 10.2 §6.1 + Table 6b note 2: when the manufacturer + # supplies a measured solar transmittance via the cert's + # `window_transmission_details`, it overrides the Table 6b code lookup. + # Elmhurst lodges this for every window (Source = "Manufacturer"). Same + # 5.52 m² SE window as 000490 SE, but glazing_type=4 (low-E soft, table + # default 0.63) with manufacturer g⊥=0.76 — the manufacturer value must + # win, reproducing the worksheet gain. + window = make_window( + orientation=4, width=5.52, height=1.0, glazing_type=4, + ) + window.window_transmission_details = WindowTransmissionDetails( + u_value=2.8, data_source=1, solar_transmittance=0.76, + ) + epc = make_minimal_sap10_epc( + total_floor_area_m2=53.0, + sap_windows=[window], + ) + + # Act + result = solar_gains_from_cert( + epc=epc, region=0, overshading=OvershadingCategory.AVERAGE, + ) + + # Assert — single SE window, Jan UK-avg flux 36.7938, gain 74.8788 W + assert result.southeast_monthly_w[0] == pytest.approx(74.8788, abs=5e-3) + assert result.total_solar_gains_monthly_w[0] == pytest.approx(74.8788, abs=5e-3) + + def test_z_solar_for_overshading_returns_table_6d_first_column() -> None: # Arrange — SAP 10.2 Table 6d "Winter solar access factor (for calculation # of solar gains for heating)" — first numeric column on p178. Used for