diff --git a/packages/domain/src/domain/sap/worksheet/internal_gains.py b/packages/domain/src/domain/sap/worksheet/internal_gains.py index 5d0e8f35..2f5fb0fb 100644 --- a/packages/domain/src/domain/sap/worksheet/internal_gains.py +++ b/packages/domain/src/domain/sap/worksheet/internal_gains.py @@ -34,6 +34,8 @@ 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 +_COOKING_W_BASE_COL_A: Final[float] = 35.0 +_COOKING_W_PER_OCCUPANT_COL_A: Final[float] = 7.0 def metabolic_monthly_w(*, n_occupants: float) -> tuple[float, ...]: @@ -57,6 +59,17 @@ def losses_monthly_w(*, n_occupants: float) -> tuple[float, ...]: for _ in range(_MONTHS_IN_YEAR)) +def cooking_monthly_w(*, n_occupants: float) -> tuple[float, ...]: + """SAP 10.2 §5 line (69) — cooking gains in watts per month. + + Table 5 Column A: G_C = 35 + 7 × N watts, constant year-round. + Fuel-agnostic (gas vs electric cooker has same heat gain; only the + §12 cost calc differentiates). + """ + gain = _COOKING_W_BASE_COL_A + _COOKING_W_PER_OCCUPANT_COL_A * n_occupants + return tuple(gain 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 851da9ef..f6573452 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 @@ -13,6 +13,7 @@ Table 5a + Appendix L (lighting/appliances/cooking) + Appendix J Table 1b import pytest from domain.sap.worksheet.internal_gains import ( + cooking_monthly_w, losses_monthly_w, metabolic_monthly_w, ) @@ -62,3 +63,27 @@ def test_losses_are_negative_40n_constant_across_months() -> None: 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)" + + +def test_cooking_gains_match_table_5_col_a_formula() -> None: + """SAP 10.2 Table 5 Column A row "Cooking": G_C = 35 + 7 × N watts, + constant year-round. + + Cooking gains are fuel-agnostic — the formula applies whether the + cooker is gas or electric (fuel matters only for the §12 cost calc, + not the internal-gain heat contribution). + + Verified against Elmhurst U985-0001-000490 worksheet (69)m row, + which shows 50.0277 W flat. For 000490's N=2.1468: + 35 + 7 × 2.1468 = 50.0276 W → agrees to 4 d.p. + """ + # Arrange + n = 2.1468 + + # Act + monthly = cooking_monthly_w(n_occupants=n) + + # Assert + assert len(monthly) == 12 + for m, value in enumerate(monthly): + assert value == pytest.approx(35.0 + 7.0 * n, abs=1e-9), f"month {m+1}"