mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-06-08 11:17:27 +00:00
S0380.229: primary loss applies for a dedicated water-heating boiler/circulator (WHS 911-931)
SAP 10.2 Table 3 (PDF p.160) row 1: primary circuit loss applies when "hot water is heated by a heat generator (e.g. boiler) connected to a hot water storage vessel via insulated or uninsulated pipes". The Table 4a hot-water-only codes (PDF p.166) 911 gas / 912 liquid / 913 solid boiler-circulator + 921-931 range cooker with boiler are each a heat generator feeding the cylinder through a primary loop. `_primary_loss_applies` keyed only off the resolved DHW `main` — but for these certs `_water_heating_main` returns the SPACE main (e.g. electric storage heaters, SAP code 402, which has no primary loop), so every boiler branch missed the gas water-boiler's primary circuit and (59)m went to zero. New branch keys off `water_heating_code` ∈ `_WATER_HEATING_BOILER_CIRCULATOR_CODES`. 941 (electric HP for water only) is excluded — HP DHW vessels follow the Table 3 integral-vessel rules. Simulated case 19 (electric storage main + WHS 911 + 210 L cylinder): (62)m total HW demand 2493.30 → 3169.98 kWh/yr, matching the worksheet (the missing 676.68 kWh/yr = the worksheet's (59) primary-loss annual sum, h=5/p=0). The remaining (64)/(219) gap is the PV diverter (63b), deferred to its own slice. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
4911c56200
commit
0f6b402345
2 changed files with 66 additions and 0 deletions
|
|
@ -659,6 +659,19 @@ _INSTANTANEOUS_WATER_CODES: Final[frozenset[int]] = frozenset({907, 909})
|
|||
# zero-loss list, so primary loss is zero whenever this code is lodged.
|
||||
_WHC_ELECTRIC_IMMERSION: Final[int] = 903
|
||||
|
||||
# Water-heating codes for a dedicated "boiler/circulator for water
|
||||
# heating only" — SAP 10.2 Table 4a hot-water section (PDF p.166):
|
||||
# 911 gas, 912 liquid fuel, 913 solid fuel boiler/circulator; 921-931
|
||||
# range cooker with boiler for water heating only. Each is a heat
|
||||
# generator feeding the cylinder through a primary loop, so SAP 10.2
|
||||
# Table 3 (PDF p.160) row 1 primary circuit loss applies — independent
|
||||
# of the space-heating system (which for these certs is a separate main,
|
||||
# e.g. electric storage heaters). 941 (electric HP for water only) is
|
||||
# excluded: HP DHW vessels follow the Table 3 integral-vessel rules.
|
||||
_WATER_HEATING_BOILER_CIRCULATOR_CODES: Final[frozenset[int]] = frozenset(
|
||||
{911, 912, 913} | set(range(921, 932))
|
||||
)
|
||||
|
||||
|
||||
# SAP 10.2 Appendix M equation (M1): EPV = 0.8 × kWp × S × ZPV, summed
|
||||
# per array. The module efficiency constant (0.8), orientation-dependent
|
||||
|
|
@ -5110,6 +5123,18 @@ def _primary_loss_applies(
|
|||
# kWh/yr primary loss to a system with no primary circuit at all.
|
||||
if water_heating_code == _WHC_ELECTRIC_IMMERSION:
|
||||
return False
|
||||
# SAP 10.2 Table 3 (PDF p.160) row 1 — a dedicated "boiler/circulator
|
||||
# for water heating only" (WHC 911 gas / 912 liquid / 913 solid /
|
||||
# 921-931 range cooker with boiler) is a heat generator feeding the
|
||||
# cylinder through a primary loop, so the loss applies regardless of
|
||||
# the space-heating main. Checked off `water_heating_code` (not
|
||||
# `main`) because for these certs the resolved DHW `main` is the
|
||||
# SPACE main (e.g. an electric storage heater, SAP code 402) — the
|
||||
# gas/oil water boiler isn't a `main_heating_detail`. Simulated case
|
||||
# 19 (storage main + WHS 911 + 210 L cylinder): worksheet (59) = 676.68
|
||||
# kWh/yr — zero before this branch.
|
||||
if water_heating_code in _WATER_HEATING_BOILER_CIRCULATOR_CODES:
|
||||
return True
|
||||
if main.main_heating_category == 4:
|
||||
if hp_record is None:
|
||||
# No PCDB record → assume separate-vessel (conservative; the
|
||||
|
|
|
|||
|
|
@ -60,6 +60,7 @@ from domain.sap10_calculator.rdsap.cert_to_inputs import (
|
|||
_is_off_peak_meter, # pyright: ignore[reportPrivateUsage]
|
||||
_main_floor_u_value, # pyright: ignore[reportPrivateUsage]
|
||||
_other_fuel_cost_gbp_per_kwh, # pyright: ignore[reportPrivateUsage]
|
||||
_primary_loss_applies, # pyright: ignore[reportPrivateUsage]
|
||||
_rdsap_extract_fans_default, # pyright: ignore[reportPrivateUsage]
|
||||
_pv_overshading_factor, # pyright: ignore[reportPrivateUsage]
|
||||
_pv_pitch_deg, # pyright: ignore[reportPrivateUsage]
|
||||
|
|
@ -1956,6 +1957,46 @@ def test_secondary_electric_off_peak_bills_at_table_12a_direct_acting_high_rate(
|
|||
assert abs(secondary_rate_gbp_per_kwh - 0.1529) <= 1e-6
|
||||
|
||||
|
||||
def test_sap_table_3_primary_loss_applies_to_dedicated_water_heating_boiler_circulator() -> None:
|
||||
# Arrange — SAP 10.2 Table 3 (PDF p.160) row 1: primary circuit loss
|
||||
# applies when "hot water is heated by a heat generator (e.g. boiler)
|
||||
# connected to a hot water storage vessel via insulated or
|
||||
# uninsulated pipes". The dedicated "boiler/circulator for water
|
||||
# heating only" water-heating codes (Table 4a hot-water section, PDF
|
||||
# p.166): 911 gas, 912 liquid fuel, 913 solid fuel, 921-931 range
|
||||
# cooker with boiler — each is a boiler feeding the cylinder through a
|
||||
# primary loop, so the loss applies regardless of what the SPACE
|
||||
# heating system is. Simulated case 19 pairs electric storage heaters
|
||||
# (SAP code 402) for space with a WHS 911 gas boiler/circulator for
|
||||
# water: `_water_heating_main` resolves to the code-402 storage main
|
||||
# (electric, no primary loop), so before this slice every dedicated-
|
||||
# boiler branch missed the cylinder's primary circuit and (59)m went
|
||||
# to zero — dropping the worksheet's 676.68 kWh/yr (59) and inflating
|
||||
# HW fuel (219).
|
||||
storage_heater_main = MainHeatingDetail(
|
||||
has_fghrs=False,
|
||||
main_fuel_type=30, # electricity
|
||||
heat_emitter_type="",
|
||||
emitter_temperature=1,
|
||||
main_heating_control=2402,
|
||||
main_heating_category=None,
|
||||
sap_main_heating_code=402, # electric storage heaters (space)
|
||||
)
|
||||
|
||||
# Act
|
||||
applies_with_cylinder = _primary_loss_applies(
|
||||
storage_heater_main, True, None, water_heating_code=911,
|
||||
)
|
||||
applies_without_cylinder = _primary_loss_applies(
|
||||
storage_heater_main, False, None, water_heating_code=911,
|
||||
)
|
||||
|
||||
# Assert — WHS 911 + cylinder → primary loss applies; no cylinder →
|
||||
# no primary circuit, no loss.
|
||||
assert applies_with_cylinder is True
|
||||
assert applies_without_cylinder is False
|
||||
|
||||
|
||||
def test_water_efficiency_uses_table_4a_water_column_for_heat_pumps_per_sap_10_2() -> None:
|
||||
# Arrange — SAP 10.2 Table 4a (PDF p.163-164) gives heat pumps two
|
||||
# efficiency columns: "space" and "water". For low-temperature
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue