diff --git a/packages/domain/src/domain/sap/worksheet/internal_gains.py b/packages/domain/src/domain/sap/worksheet/internal_gains.py index aefbf5c6..5d0e8f35 100644 --- a/packages/domain/src/domain/sap/worksheet/internal_gains.py +++ b/packages/domain/src/domain/sap/worksheet/internal_gains.py @@ -33,6 +33,7 @@ from typing import Final, Optional _MONTHS_IN_YEAR: Final[int] = 12 _METABOLIC_GAIN_W_PER_OCCUPANT_COL_A: Final[float] = 60.0 +_LOSSES_W_PER_OCCUPANT_COL_A: Final[float] = -40.0 def metabolic_monthly_w(*, n_occupants: float) -> tuple[float, ...]: @@ -45,6 +46,17 @@ def metabolic_monthly_w(*, n_occupants: float) -> tuple[float, ...]: for _ in range(_MONTHS_IN_YEAR)) +def losses_monthly_w(*, n_occupants: float) -> tuple[float, ...]: + """SAP 10.2 §5 line (71) — internal-gain losses in watts per month. + + Table 5: losses = -40 × N watts (constant year-round). Comprises heat + sunk to incoming cold water + evaporation from washing/showering. Sign + is negative — these reduce the dwelling's net internal gains. + """ + return tuple(_LOSSES_W_PER_OCCUPANT_COL_A * n_occupants + for _ in range(_MONTHS_IN_YEAR)) + + _METABOLIC_W_PER_OCCUPANT: Final[float] = 60.0 diff --git a/packages/domain/src/domain/sap/worksheet/tests/test_internal_gains.py b/packages/domain/src/domain/sap/worksheet/tests/test_internal_gains.py index ae7b46c4..851da9ef 100644 --- a/packages/domain/src/domain/sap/worksheet/tests/test_internal_gains.py +++ b/packages/domain/src/domain/sap/worksheet/tests/test_internal_gains.py @@ -12,7 +12,10 @@ Table 5a + Appendix L (lighting/appliances/cooking) + Appendix J Table 1b import pytest -from domain.sap.worksheet.internal_gains import metabolic_monthly_w +from domain.sap.worksheet.internal_gains import ( + losses_monthly_w, + metabolic_monthly_w, +) def test_metabolic_gains_are_60w_per_occupant_constant_across_months() -> None: @@ -34,3 +37,28 @@ def test_metabolic_gains_are_60w_per_occupant_constant_across_months() -> None: assert len(monthly) == 12 for m, value in enumerate(monthly): assert value == pytest.approx(60.0 * n, abs=1e-9), f"month {m+1}" + + +def test_losses_are_negative_40n_constant_across_months() -> None: + """SAP 10.2 Table 5 row "Losses": -40 × N watts year-round. + + The losses comprise heat lost to incoming cold water + evaporation + (washing, showering, etc.). The sign is negative — internal gains + are reduced. + + Verified against the Elmhurst U985-0001-000490 worksheet (71)m row, + which shows -85.8725 W flat across all months. For 000490's N=2.1468: + -40 × 2.1468 = -85.872 W + matches the worksheet to within 4-d.p. display rounding. + """ + # Arrange + n = 2.1468 + + # Act + monthly = losses_monthly_w(n_occupants=n) + + # Assert + assert len(monthly) == 12 + for m, value in enumerate(monthly): + assert value == pytest.approx(-40.0 * n, abs=1e-9), f"month {m+1}" + assert value < 0, "Losses must be negative (heat-loss convention)"