mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-06-30 13:10:47 +00:00
§5 slice 5: (68) appliances_monthly_w — Appendix L13/L14/L16a
E_A = 207.8 × (TFA × N)^0.4714 (L13) chained through monthly factor 1 + 0.157 × cos(2π × (m - 1.78) / 12) (L14) then watts via × 1000 / (24 × n_m) (L16a). Column A typical-gain form — 1.0× conversion. L16's 0.67× reduced form deferred (new-build DPER/TPER use). Verified against Elmhurst U985-0001-000490 (68)m row to ≤5e-2 W (display rounding from the (TFA × N)^0.4714 term). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
a4d6321f21
commit
0bc9eac34c
2 changed files with 72 additions and 0 deletions
|
|
@ -30,6 +30,12 @@ from dataclasses import dataclass
|
||||||
from math import cos, exp, pi
|
from math import cos, exp, pi
|
||||||
from typing import Final, Optional
|
from typing import Final, Optional
|
||||||
|
|
||||||
|
_DAYS_PER_YEAR: Final[float] = 365.0
|
||||||
|
_APPLIANCES_E_A_COEFF: Final[float] = 207.8
|
||||||
|
_APPLIANCES_E_A_EXPONENT: Final[float] = 0.4714
|
||||||
|
_APPLIANCES_MONTHLY_AMPLITUDE: Final[float] = 0.157
|
||||||
|
_APPLIANCES_MONTHLY_PHASE: Final[float] = 1.78
|
||||||
|
|
||||||
|
|
||||||
_MONTHS_IN_YEAR: Final[int] = 12
|
_MONTHS_IN_YEAR: Final[int] = 12
|
||||||
_DAYS_PER_MONTH: Final[tuple[int, ...]] = (31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31)
|
_DAYS_PER_MONTH: Final[tuple[int, ...]] = (31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31)
|
||||||
|
|
@ -73,6 +79,38 @@ def cooking_monthly_w(*, n_occupants: float) -> tuple[float, ...]:
|
||||||
return tuple(gain for _ in range(_MONTHS_IN_YEAR))
|
return tuple(gain for _ in range(_MONTHS_IN_YEAR))
|
||||||
|
|
||||||
|
|
||||||
|
def appliances_monthly_w(
|
||||||
|
*,
|
||||||
|
total_floor_area_m2: float,
|
||||||
|
n_occupants: float,
|
||||||
|
) -> tuple[float, ...]:
|
||||||
|
"""SAP 10.2 §5 line (68) — appliance gains in watts per month.
|
||||||
|
|
||||||
|
Appendix L equations L13, L14, L16a (Column A, typical gains):
|
||||||
|
E_A = 207.8 × (TFA × N)^0.4714 [kWh/yr]
|
||||||
|
E_A,m = E_A × [1 + 0.157 × cos(2π × (m - 1.78) / 12)] × n_m / 365 [kWh/mo]
|
||||||
|
G_A,m = E_A,m × 1000 / (24 × n_m) [W]
|
||||||
|
|
||||||
|
The cosine peaks ~end-of-January (m=1.78) and troughs ~end-of-July.
|
||||||
|
All electrical appliance energy stays as internal heat — full 1.0×
|
||||||
|
conversion. Column B's L16 (0.67×) reduced form is for new-build
|
||||||
|
DPER/TPER only.
|
||||||
|
"""
|
||||||
|
e_a_annual = (
|
||||||
|
_APPLIANCES_E_A_COEFF
|
||||||
|
* (total_floor_area_m2 * n_occupants) ** _APPLIANCES_E_A_EXPONENT
|
||||||
|
)
|
||||||
|
monthly: list[float] = []
|
||||||
|
for m_idx, days in enumerate(_DAYS_PER_MONTH):
|
||||||
|
m = m_idx + 1
|
||||||
|
factor = 1.0 + _APPLIANCES_MONTHLY_AMPLITUDE * cos(
|
||||||
|
2.0 * pi * (m - _APPLIANCES_MONTHLY_PHASE) / _MONTHS_IN_YEAR
|
||||||
|
)
|
||||||
|
e_a_m_kwh = e_a_annual * factor * days / _DAYS_PER_YEAR
|
||||||
|
monthly.append(e_a_m_kwh * _KWH_TO_WH / (_HOURS_PER_DAY * days))
|
||||||
|
return tuple(monthly)
|
||||||
|
|
||||||
|
|
||||||
def water_heating_gains_monthly_w(
|
def water_heating_gains_monthly_w(
|
||||||
*,
|
*,
|
||||||
heat_gains_from_water_heating_monthly_kwh: tuple[float, ...],
|
heat_gains_from_water_heating_monthly_kwh: tuple[float, ...],
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ Table 5a + Appendix L (lighting/appliances/cooking) + Appendix J Table 1b
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from domain.sap.worksheet.internal_gains import (
|
from domain.sap.worksheet.internal_gains import (
|
||||||
|
appliances_monthly_w,
|
||||||
cooking_monthly_w,
|
cooking_monthly_w,
|
||||||
losses_monthly_w,
|
losses_monthly_w,
|
||||||
metabolic_monthly_w,
|
metabolic_monthly_w,
|
||||||
|
|
@ -121,3 +122,36 @@ def test_water_heating_gains_bridge_converts_kwh_per_month_to_watts() -> None:
|
||||||
assert len(monthly) == 12
|
assert len(monthly) == 12
|
||||||
for m, (actual, exp) in enumerate(zip(monthly, expected_w)):
|
for m, (actual, exp) in enumerate(zip(monthly, expected_w)):
|
||||||
assert actual == pytest.approx(exp, abs=1e-3), f"month {m+1}"
|
assert actual == pytest.approx(exp, abs=1e-3), f"month {m+1}"
|
||||||
|
|
||||||
|
|
||||||
|
def test_appliances_gains_match_appendix_l13_l14_l16a_for_000490() -> None:
|
||||||
|
"""SAP 10.2 Appendix L equations L13/L14/L16a:
|
||||||
|
E_A = 207.8 × (TFA × N)^0.4714 [kWh/yr]
|
||||||
|
E_A,m = E_A × [1 + 0.157 × cos(2π × (m - 1.78) / 12)] × n_m / 365 [kWh/mo]
|
||||||
|
G_A,m = E_A,m × 1000 / (24 × n_m) [W]
|
||||||
|
|
||||||
|
Verified against U985-0001-000490 worksheet (68)m row. For
|
||||||
|
TFA=66.06, N=2.1468:
|
||||||
|
E_A = 207.8 × 141.84^0.4714 ≈ 2147.8 kWh/yr
|
||||||
|
Jan factor = 1 + 0.157 × cos(2π × -0.78/12) ≈ 1.1441
|
||||||
|
E_A,Jan ≈ 208.66 kWh → G_A,Jan ≈ 280.46 W
|
||||||
|
matches worksheet 280.4965 to ≈0.04 W (display rounding from E_A^0.4714).
|
||||||
|
|
||||||
|
Table 5 Column A applies (rating + cooling); Column B's 0.67×
|
||||||
|
reduced-gain form (L16) is deferred to a future new-build slice.
|
||||||
|
"""
|
||||||
|
# Arrange
|
||||||
|
tfa = 66.06
|
||||||
|
n = 2.1468
|
||||||
|
expected_w = (
|
||||||
|
280.4965, 283.4071, 276.0723, 260.4574, 240.7463, 222.2207,
|
||||||
|
209.8445, 206.9338, 214.2686, 229.8835, 249.5946, 268.1202,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Act
|
||||||
|
monthly = appliances_monthly_w(total_floor_area_m2=tfa, n_occupants=n)
|
||||||
|
|
||||||
|
# Assert
|
||||||
|
assert len(monthly) == 12
|
||||||
|
for m, (actual, exp) in enumerate(zip(monthly, expected_w)):
|
||||||
|
assert actual == pytest.approx(exp, abs=5e-2), f"month {m+1}"
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue