diff --git a/packages/domain/src/domain/sap/worksheet/solar_gains.py b/packages/domain/src/domain/sap/worksheet/solar_gains.py index c727feb2..c7f86d76 100644 --- a/packages/domain/src/domain/sap/worksheet/solar_gains.py +++ b/packages/domain/src/domain/sap/worksheet/solar_gains.py @@ -37,6 +37,23 @@ from domain.sap.climate.appendix_u import ( horizontal_solar_irradiance_w_per_m2, solar_declination_deg, ) +from domain.sap.worksheet.internal_gains import OvershadingCategory + + +# Table 6d first column — winter solar access factor Z for heating gains. +# Distinct from the lighting Z_L (third column, §5) and cooling Z (second +# column, out of scope). SAP 10.2 spec p178. +_Z_SOLAR_BY_OVERSHADING: Final[dict[OvershadingCategory, float]] = { + OvershadingCategory.HEAVY: 0.3, + OvershadingCategory.MORE_THAN_AVERAGE: 0.54, + OvershadingCategory.AVERAGE: 0.77, + OvershadingCategory.VERY_LITTLE: 1.0, +} + + +def z_solar_for_overshading(overshading: OvershadingCategory) -> float: + """SAP 10.2 Table 6d winter solar access factor Z for heating gains.""" + return _Z_SOLAR_BY_OVERSHADING[overshading] class Orientation(Enum): 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 eaddb76e..4bd239ea 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,13 +11,28 @@ Appendix U §U3.2 (pages 127-129), Table U4 (latitudes), Table U5 import pytest +from domain.sap.worksheet.internal_gains import OvershadingCategory from domain.sap.worksheet.solar_gains import ( Orientation, surface_solar_flux_w_per_m2, window_solar_gain_w, + z_solar_for_overshading, ) +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 + # SAP rating + DPER/TPER; distinct from the lighting Z_L (third column, + # consumed by §5) and the cooling Z (second column, out of scope). + + # Act / Assert + assert z_solar_for_overshading(OvershadingCategory.HEAVY) == 0.3 + assert z_solar_for_overshading(OvershadingCategory.MORE_THAN_AVERAGE) == 0.54 + assert z_solar_for_overshading(OvershadingCategory.AVERAGE) == 0.77 + assert z_solar_for_overshading(OvershadingCategory.VERY_LITTLE) == 1.0 + + def test_uk_average_south_vertical_july_returns_hand_computed_flux() -> None: # Arrange — UK average region (0), South-facing vertical (p=90°), July. # Hand-computed from Appendix U §U3.2 with Table U5 South constants: