mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-06-08 11:17:27 +00:00
§4 slice 2: hot_water_other_uses_monthly_l_per_day (line (42c)m)
Appendix J equation J11 — daily hot water use for non-shower / non-bath purposes (sinks, dishwashers, etc.) is annual-avg V_d,other,ave = 9.8 × N + 14, modulated month-by-month by the Table J2 monthly factors and reduced by 5% when the dwelling meets the 125 L/person/day water-use target. Validated against both Elmhurst non-RR fixtures to better than 1e-3 L: - 000490 N=2.1468 → V_d,other,ave ≈ 35.04, Jan = 38.5426 - 000474 N=1.8896 → V_d,other,ave ≈ 32.52, Jan = 35.7697 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
aff678e8eb
commit
5cc68ab3fd
2 changed files with 93 additions and 1 deletions
|
|
@ -15,7 +15,10 @@ from domain.sap.worksheet.tests import (
|
||||||
_elmhurst_worksheet_000490 as _w000490,
|
_elmhurst_worksheet_000490 as _w000490,
|
||||||
)
|
)
|
||||||
from domain.sap.worksheet.tests._xlsx_loader import load_cells
|
from domain.sap.worksheet.tests._xlsx_loader import load_cells
|
||||||
from domain.sap.worksheet.water_heating import assumed_occupancy
|
from domain.sap.worksheet.water_heating import (
|
||||||
|
assumed_occupancy,
|
||||||
|
hot_water_other_uses_monthly_l_per_day,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def test_assumed_occupancy_matches_canonical_xlsx_worked_example() -> None:
|
def test_assumed_occupancy_matches_canonical_xlsx_worked_example() -> None:
|
||||||
|
|
@ -60,6 +63,65 @@ def test_assumed_occupancy_matches_elmhurst_worksheet_000490() -> None:
|
||||||
assert n == pytest.approx(_w000490.LINE_42_OCCUPANCY, abs=1e-4)
|
assert n == pytest.approx(_w000490.LINE_42_OCCUPANCY, abs=1e-4)
|
||||||
|
|
||||||
|
|
||||||
|
def test_hot_water_other_uses_matches_elmhurst_worksheet_000490() -> None:
|
||||||
|
"""SAP10.2 §4 line (42c)m via Appendix J equation J11:
|
||||||
|
V_d,other,ave = 9.8 × N + 14
|
||||||
|
V_d,other[m] = V_d,other,ave × J2_monthly_factor[m]
|
||||||
|
|
||||||
|
For 000490 (N=2.1468) the worksheet (42c)m values follow the Table J2
|
||||||
|
monthly factor pattern of (1.10, 1.06, 1.02, 0.98, 0.94, 0.90, 0.90,
|
||||||
|
0.94, 0.98, 1.02, 1.06, 1.10) applied to V_d,other,ave ≈ 35.04 L/day.
|
||||||
|
"""
|
||||||
|
# Arrange — expected values copied from the Elmhurst worksheet
|
||||||
|
expected = (
|
||||||
|
38.5426, 37.1411, 35.7395, 34.3380, 32.9364, 31.5349,
|
||||||
|
31.5349, 32.9364, 34.3380, 35.7395, 37.1411, 38.5426,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Act
|
||||||
|
monthly = hot_water_other_uses_monthly_l_per_day(
|
||||||
|
n_occupants=_w000490.LINE_42_OCCUPANCY,
|
||||||
|
low_water_use=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Assert
|
||||||
|
for m, (actual, exp) in enumerate(zip(monthly, expected)):
|
||||||
|
assert actual == pytest.approx(exp, abs=1e-3), f"month {m+1}"
|
||||||
|
|
||||||
|
|
||||||
|
def test_hot_water_other_uses_matches_elmhurst_worksheet_000474() -> None:
|
||||||
|
"""Same (42c)m formula for 000474 (N=1.8896): V_d,other,ave ≈ 32.52
|
||||||
|
L/day; Jan = 32.52 × 1.10 ≈ 35.77."""
|
||||||
|
# Arrange
|
||||||
|
expected = (
|
||||||
|
35.7697, 34.4690, 33.1682, 31.8675, 30.5668, 29.2661,
|
||||||
|
29.2661, 30.5668, 31.8675, 33.1682, 34.4690, 35.7697,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Act
|
||||||
|
monthly = hot_water_other_uses_monthly_l_per_day(
|
||||||
|
n_occupants=_w000474.LINE_42_OCCUPANCY,
|
||||||
|
low_water_use=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Assert
|
||||||
|
for m, (actual, exp) in enumerate(zip(monthly, expected)):
|
||||||
|
assert actual == pytest.approx(exp, abs=1e-3), f"month {m+1}"
|
||||||
|
|
||||||
|
|
||||||
|
def test_hot_water_other_uses_low_water_use_target_reduces_by_5pct() -> None:
|
||||||
|
"""Appendix J J11 footnote: dwellings designed for ≤125 L/person/day
|
||||||
|
total water use get the V_d,other,ave reduced by 5%. Monthly factors
|
||||||
|
apply after the reduction."""
|
||||||
|
# Arrange / Act
|
||||||
|
normal = hot_water_other_uses_monthly_l_per_day(n_occupants=2.0, low_water_use=False)
|
||||||
|
lwu = hot_water_other_uses_monthly_l_per_day(n_occupants=2.0, low_water_use=True)
|
||||||
|
|
||||||
|
# Assert
|
||||||
|
for m, (n, l) in enumerate(zip(normal, lwu)):
|
||||||
|
assert l == pytest.approx(n * 0.95, abs=1e-9), f"month {m+1}"
|
||||||
|
|
||||||
|
|
||||||
def test_assumed_occupancy_floor_at_n_eq_1_for_small_dwellings() -> None:
|
def test_assumed_occupancy_floor_at_n_eq_1_for_small_dwellings() -> None:
|
||||||
"""Appendix J piecewise definition: TFA ≤ 13.9 m² → N=1 exactly. A
|
"""Appendix J piecewise definition: TFA ≤ 13.9 m² → N=1 exactly. A
|
||||||
tiny studio flat at the boundary is the most common trigger."""
|
tiny studio flat at the boundary is the most common trigger."""
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,16 @@ from typing import Final
|
||||||
|
|
||||||
_OCCUPANCY_TFA_FLOOR_M2: Final[float] = 13.9
|
_OCCUPANCY_TFA_FLOOR_M2: Final[float] = 13.9
|
||||||
|
|
||||||
|
# Table J2 — monthly factors for hot water use (also used by Appendix J
|
||||||
|
# equation J11 for "other uses"). Symmetric about the year midpoint.
|
||||||
|
_TABLE_J2_MONTHLY_FACTORS: Final[tuple[float, ...]] = (
|
||||||
|
1.10, 1.06, 1.02, 0.98, 0.94, 0.90, 0.90, 0.94, 0.98, 1.02, 1.06, 1.10,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Appendix J J11 footnote: -5% reduction in V_d,other,ave for dwellings
|
||||||
|
# designed to ≤125 L/person/day total water use.
|
||||||
|
_LOW_WATER_USE_REDUCTION: Final[float] = 0.05
|
||||||
|
|
||||||
|
|
||||||
def assumed_occupancy(total_floor_area_m2: float) -> float:
|
def assumed_occupancy(total_floor_area_m2: float) -> float:
|
||||||
"""SAP 10.2 §4 line (42) / Appendix J Table 1b.
|
"""SAP 10.2 §4 line (42) / Appendix J Table 1b.
|
||||||
|
|
@ -42,3 +52,23 @@ def assumed_occupancy(total_floor_area_m2: float) -> float:
|
||||||
return 1.0
|
return 1.0
|
||||||
x = total_floor_area_m2 - _OCCUPANCY_TFA_FLOOR_M2
|
x = total_floor_area_m2 - _OCCUPANCY_TFA_FLOOR_M2
|
||||||
return 1.0 + 1.76 * (1.0 - exp(-0.000349 * x * x)) + 0.0013 * x
|
return 1.0 + 1.76 * (1.0 - exp(-0.000349 * x * x)) + 0.0013 * x
|
||||||
|
|
||||||
|
|
||||||
|
def hot_water_other_uses_monthly_l_per_day(
|
||||||
|
*, n_occupants: float, low_water_use: bool
|
||||||
|
) -> tuple[float, ...]:
|
||||||
|
"""SAP 10.2 §4 line (42c)m via Appendix J equation J11.
|
||||||
|
|
||||||
|
Annual-average daily hot water use for "other purposes" (i.e. not
|
||||||
|
showers, not baths — sinks, dishwashers, etc.):
|
||||||
|
|
||||||
|
V_d,other,ave = 9.8 × N + 14
|
||||||
|
|
||||||
|
reduced by 5% if `low_water_use` is True (dwelling designed for ≤125
|
||||||
|
L/person/day total water use). The monthly array applies Table J2's
|
||||||
|
factor sequence so each entry is daily L for that month.
|
||||||
|
"""
|
||||||
|
annual_average = 9.8 * n_occupants + 14.0
|
||||||
|
if low_water_use:
|
||||||
|
annual_average *= 1.0 - _LOW_WATER_USE_REDUCTION
|
||||||
|
return tuple(annual_average * f for f in _TABLE_J2_MONTHLY_FACTORS)
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue