mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-08-02 12:58:30 +00:00
Treat water-only heat-network primary pipework as insulated 🟥
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
ef44a744a3
commit
15b78ba312
1 changed files with 68 additions and 0 deletions
|
|
@ -1611,6 +1611,74 @@ def test_water_only_heat_network_cylinder_accrues_table_3_primary_loss() -> None
|
|||
assert all(m > 0.0 for m in wh_result.primary_loss_monthly_kwh)
|
||||
|
||||
|
||||
def test_water_only_heat_network_primary_loss_matches_heat_network_main() -> None:
|
||||
# Arrange — SAP 10.2 Table 3 (PDF p.160): "For heat networks apply the
|
||||
# formula above with p = 1.0 and h = 3 for all months", and §4.3 (p.24)
|
||||
# pins the pipework literal: "Primary circuit loss for INSULATED
|
||||
# pipework". Both scope on the DHW source, so the two ways a dwelling
|
||||
# can take DHW from a heat network must accrue the SAME (59)m:
|
||||
#
|
||||
# - WHC 901 "from main" where the space main IS the network, and
|
||||
# - WHC 950, a water-only network behind a warm-air space main.
|
||||
#
|
||||
# Without this the water-only cert falls back to the RdSAP §3 age-band
|
||||
# default (band E → p = 0.0, uninsulated), billing a heat network for
|
||||
# bare primary pipework the spec says is insulated.
|
||||
part = make_building_part(construction_age_band="E")
|
||||
warm_air_main = MainHeatingDetail(
|
||||
has_fghrs=False,
|
||||
main_fuel_type=26,
|
||||
heat_emitter_type=1,
|
||||
emitter_temperature=1,
|
||||
main_heating_control=2106,
|
||||
main_heating_category=9,
|
||||
sap_main_heating_code=502,
|
||||
)
|
||||
heat_network_main = MainHeatingDetail(
|
||||
has_fghrs=False,
|
||||
main_fuel_type=20,
|
||||
heat_emitter_type=1,
|
||||
emitter_temperature=1,
|
||||
main_heating_control=2106,
|
||||
main_heating_category=6,
|
||||
sap_main_heating_code=301,
|
||||
)
|
||||
|
||||
def primary_loss_for(
|
||||
main: MainHeatingDetail, water_heating_code: int
|
||||
) -> tuple[float, ...]:
|
||||
epc = make_minimal_sap10_epc(
|
||||
total_floor_area_m2=_TYPICAL_TFA_M2,
|
||||
habitable_rooms_count=4,
|
||||
country_code="ENG",
|
||||
has_hot_water_cylinder=True,
|
||||
sap_building_parts=[part],
|
||||
sap_heating=make_sap_heating(
|
||||
main_heating_details=[main],
|
||||
water_heating_code=water_heating_code,
|
||||
cylinder_size=2, # Normal / 110 L
|
||||
cylinder_thermostat="Y",
|
||||
),
|
||||
)
|
||||
wh_result, _ = _water_heating_worksheet_and_gains(
|
||||
epc=epc,
|
||||
water_efficiency_pct=100.0,
|
||||
is_instantaneous=False,
|
||||
primary_age="E",
|
||||
pcdb_record=None,
|
||||
)
|
||||
assert wh_result is not None
|
||||
return wh_result.primary_loss_monthly_kwh
|
||||
|
||||
# Act — DHW from a water-only network vs DHW from a network space main.
|
||||
water_only = primary_loss_for(warm_air_main, 950)
|
||||
from_network_main = primary_loss_for(heat_network_main, 901)
|
||||
|
||||
# Assert — Table 3's heat-network row keys on the DHW source, not the
|
||||
# space main, so both bill the same insulated p=1.0 / h=3 loss.
|
||||
assert water_only == pytest.approx(from_network_main, abs=1e-6)
|
||||
|
||||
|
||||
def test_gas_boiler_main_efficiency_unchanged_by_dlf_override() -> None:
|
||||
# Arrange — regression check: the DLF override only fires for heat-
|
||||
# network main heating. A standard gas boiler (cat=2, code=102) must
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue