mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-06-08 11:17:27 +00:00
§4 slice 6: lines (45)m energy content + (46)m distribution loss
(45)m = 4.18 × V_d,m × n_m × (52 − Tcold[m]) / 3600 [kWh/month]
Appendix J equation J14
(46)m = 0.15 × (45)m spec §4 step 7 (normal systems)
= 0 (instantaneous at point of use,
hot water codes 907 / 909)
4.18 J/(g·K) is the specific heat of water; / 3600 converts to kWh. The
J14 transform converts daily L of hot water at delivery temperature into
the monthly sensible-heat requirement.
Both Elmhurst non-RR fixtures use a combi boiler from a central system
(neither 907 nor 909), so distribution loss is the full 15 % of (45)m.
Lodged LINE_45_M and LINE_46_M arrays on both fixtures for forward use.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
702b1c6ce6
commit
a3c687f1b0
4 changed files with 112 additions and 0 deletions
|
|
@ -189,3 +189,11 @@ LINE_44_M_DAILY_HW_USAGE_L: tuple[float, ...] = (
|
|||
110.1180, 107.7045, 104.8007, 100.4697, 96.9221, 93.1204,
|
||||
91.7218, 94.6138, 97.6550, 101.6380, 106.0332, 109.8446,
|
||||
)
|
||||
LINE_45_M_HW_ENERGY_CONTENT_KWH: tuple[float, ...] = (
|
||||
174.4000, 153.3698, 161.0747, 137.5380, 130.4758, 114.5024,
|
||||
110.9296, 117.1517, 120.4183, 137.9218, 151.0638, 171.9901,
|
||||
)
|
||||
LINE_46_M_DISTRIBUTION_LOSS_KWH: tuple[float, ...] = (
|
||||
26.1600, 23.0055, 24.1612, 20.6307, 19.5714, 17.1754,
|
||||
16.6394, 17.5728, 18.0628, 20.6883, 22.6596, 25.7985,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -177,3 +177,11 @@ LINE_44_M_DAILY_HW_USAGE_L: tuple[float, ...] = (
|
|||
118.6172, 116.0171, 112.8890, 108.2238, 104.4023, 100.3071,
|
||||
98.8008, 101.9162, 105.1922, 109.4827, 114.2171, 118.3228,
|
||||
)
|
||||
LINE_45_M_HW_ENERGY_CONTENT_KWH: tuple[float, ...] = (
|
||||
187.8607, 165.2069, 173.5062, 148.1530, 140.5456, 123.3393,
|
||||
119.4910, 126.1935, 129.7125, 148.5670, 162.7232, 185.2648,
|
||||
)
|
||||
LINE_46_M_DISTRIBUTION_LOSS_KWH: tuple[float, ...] = (
|
||||
28.1791, 24.7810, 26.0259, 22.2229, 21.0818, 18.5009,
|
||||
17.9237, 18.9290, 19.4569, 22.2851, 24.4085, 27.7897,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -20,6 +20,8 @@ from domain.sap.worksheet.water_heating import (
|
|||
annual_average_hot_water_l_per_day,
|
||||
annual_average_hot_water_other_uses_l_per_day,
|
||||
assumed_occupancy,
|
||||
distribution_loss_monthly_kwh,
|
||||
energy_content_of_hot_water_monthly_kwh,
|
||||
hot_water_baths_monthly_l_per_day,
|
||||
hot_water_mixer_showers_monthly_l_per_day,
|
||||
hot_water_other_uses_monthly_l_per_day,
|
||||
|
|
@ -400,6 +402,61 @@ def test_annual_average_hot_water_matches_elmhurst_line_43(fixture) -> None: #
|
|||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("fixture", (_w000474, _w000490), ids=("000474", "000490"))
|
||||
def test_energy_content_of_hot_water_matches_elmhurst_line_45(fixture) -> None: # type: ignore[no-untyped-def]
|
||||
"""SAP10.2 §4 line (45)m via Appendix J equation J14:
|
||||
(45)m = 4.18 × V_d,m × n_m × (52 − Tcold[m]) / 3600 [kWh/month]
|
||||
|
||||
4.18 J/g/K is the specific heat of water, dividing by 3600 converts
|
||||
to kWh. Both fixtures heat hot water to 52°C from mains-temperature
|
||||
cold water (Table J1 mains column).
|
||||
"""
|
||||
# Arrange / Act
|
||||
monthly_kwh = energy_content_of_hot_water_monthly_kwh(
|
||||
monthly_hot_water_l_per_day=fixture.LINE_44_M_DAILY_HW_USAGE_L,
|
||||
cold_water_temps_c=TABLE_J1_TCOLD_FROM_MAINS_C,
|
||||
)
|
||||
|
||||
# Assert
|
||||
for m, (actual, exp) in enumerate(zip(monthly_kwh, fixture.LINE_45_M_HW_ENERGY_CONTENT_KWH)):
|
||||
assert actual == pytest.approx(exp, abs=1e-2), f"month {m+1}"
|
||||
|
||||
|
||||
@pytest.mark.parametrize("fixture", (_w000474, _w000490), ids=("000474", "000490"))
|
||||
def test_distribution_loss_matches_elmhurst_line_46_for_non_instantaneous(
|
||||
fixture, # type: ignore[no-untyped-def]
|
||||
) -> None:
|
||||
"""SAP10.2 §4 line (46)m: for non-instantaneous systems
|
||||
(46)m = 0.15 × (45)m
|
||||
|
||||
Both Elmhurst fixtures run a combi boiler with hot water from a
|
||||
main central system (not codes 907/909 instantaneous), so distribution
|
||||
loss is 15% of the energy content."""
|
||||
# Arrange / Act
|
||||
monthly_loss = distribution_loss_monthly_kwh(
|
||||
monthly_energy_content_kwh=fixture.LINE_45_M_HW_ENERGY_CONTENT_KWH,
|
||||
is_instantaneous_at_point_of_use=False,
|
||||
)
|
||||
|
||||
# Assert
|
||||
for m, (actual, exp) in enumerate(zip(monthly_loss, fixture.LINE_46_M_DISTRIBUTION_LOSS_KWH)):
|
||||
assert actual == pytest.approx(exp, abs=1e-3), f"month {m+1}"
|
||||
|
||||
|
||||
def test_distribution_loss_zero_for_instantaneous_point_of_use_water_heating() -> None:
|
||||
"""SAP10.2 §4: hot water codes 907 and 909 are instantaneous at the
|
||||
point of use — no distribution pipework, so (46)m is zero for every
|
||||
month regardless of (45)m."""
|
||||
# Arrange / Act
|
||||
loss = distribution_loss_monthly_kwh(
|
||||
monthly_energy_content_kwh=(100.0,) * 12,
|
||||
is_instantaneous_at_point_of_use=True,
|
||||
)
|
||||
|
||||
# Assert
|
||||
assert all(v == pytest.approx(0.0, abs=1e-9) for v in loss)
|
||||
|
||||
|
||||
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
|
||||
tiny studio flat at the boundary is the most common trigger."""
|
||||
|
|
|
|||
|
|
@ -190,6 +190,45 @@ def total_hot_water_monthly_l_per_day(
|
|||
return tuple(s + b + o for s, b, o in zip(showers, baths, other_uses))
|
||||
|
||||
|
||||
def energy_content_of_hot_water_monthly_kwh(
|
||||
*,
|
||||
monthly_hot_water_l_per_day: tuple[float, ...],
|
||||
cold_water_temps_c: tuple[float, ...],
|
||||
) -> tuple[float, ...]:
|
||||
"""SAP 10.2 §4 line (45)m via Appendix J equation J14:
|
||||
(45)m = 4.18 × V_d,m × n_m × (52 − Tcold[m]) / 3600 [kWh/month]
|
||||
|
||||
Sensible heat to raise the monthly hot water volume from Tcold[m] to
|
||||
the 52 °C delivery temperature. 4.18 J/(g·K) is the specific heat of
|
||||
water; dividing by 3600 converts J/g to Wh/g (= kWh/kg, since 1 L of
|
||||
water ≈ 1 kg).
|
||||
"""
|
||||
return tuple(
|
||||
4.18 * vd * n * (_HOT_DELIVERY_TEMPERATURE_C - tcold) / 3600.0
|
||||
for vd, n, tcold in zip(
|
||||
monthly_hot_water_l_per_day, _DAYS_IN_MONTH, cold_water_temps_c
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def distribution_loss_monthly_kwh(
|
||||
*,
|
||||
monthly_energy_content_kwh: tuple[float, ...],
|
||||
is_instantaneous_at_point_of_use: bool,
|
||||
) -> tuple[float, ...]:
|
||||
"""SAP 10.2 §4 line (46)m.
|
||||
|
||||
Distribution loss is a flat 15% of (45)m unless the water heating is
|
||||
instantaneous at the point of use (Table 4a hot water codes 907 and
|
||||
909) — those have no distribution pipework so the loss is zero. Heat
|
||||
networks still incur the 15% loss whether or not a hot water cylinder
|
||||
is present (spec §4 step 7).
|
||||
"""
|
||||
if is_instantaneous_at_point_of_use:
|
||||
return tuple(0.0 for _ in range(12))
|
||||
return tuple(0.15 * e for e in monthly_energy_content_kwh)
|
||||
|
||||
|
||||
def _days_weighted_average(monthly: tuple[float, ...]) -> float:
|
||||
"""Σ value[m] × n_m / 365 — used by Appendix J equations J4 and J9."""
|
||||
return sum(v * d for v, d in zip(monthly, _DAYS_IN_MONTH)) / _DAYS_IN_YEAR
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue