Charge the C6 half heat-network standing charge on a water-only dwelling's fuel cost worksheet 🟥

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Daniel Roth 2026-07-15 13:37:19 +00:00
parent cc84a091e6
commit bd3a7ac317
2 changed files with 70 additions and 0 deletions

41
c6_cohort.py Normal file
View file

@ -0,0 +1,41 @@
"""Which 21.0.1-corpus certs are in #1606's C6 half-charge cohort?
Cohort = WHC in {950,951,952} AND space main is NOT a heat network.
"""
import json
from pathlib import Path
from datatypes.epc.domain.mapper import EpcPropertyDataMapper
from datatypes.epc.domain.field_mappings import is_heat_network_main
from domain.sap10_calculator.rdsap.cert_to_inputs import (
_first_main_heating, _heat_network_standing_charge_gbp,
_WATER_HEAT_NETWORK_ONLY_CODES,
)
path = Path("backend/epc_api/json_samples/RdSAP-Schema-21.0.1/corpus.jsonl")
certs = [json.loads(l) for l in path.read_text().splitlines() if l.strip()]
hits = []
for c in certs:
epc = EpcPropertyDataMapper.from_api_response(c)
whc = epc.sap_heating.water_heating_code if epc.sap_heating else None
if whc not in _WATER_HEAT_NETWORK_ONLY_CODES:
continue
m1 = _first_main_heating(epc)
net = is_heat_network_main(m1)
hits.append({
"rrn": c.get("rrn") or c.get("lmk_key"),
"uprn": c.get("uprn"),
"addr": (c.get("address1") or "")[:34],
"whc": whc,
"main_code": m1.sap_main_heating_code if m1 else None,
"main_is_network": net,
"net_standing": _heat_network_standing_charge_gbp(epc, m1),
"in_C6_half_cohort": not net,
})
print(f"WHC 950/951/952 certs in 21.0.1 corpus: {len(hits)}\n")
for h in hits:
flag = " <== C6 HALF COHORT" if h["in_C6_half_cohort"] else ""
print(f" rrn={h['rrn']} uprn={h['uprn']}")
print(f" addr={h['addr']!r} whc={h['whc']} main={h['main_code']} "
f"network_main={h['main_is_network']} net_standing={h['net_standing']}{flag}")
print(f"\nIn C6 half cohort: {sum(h['in_C6_half_cohort'] for h in hits)}")

View file

@ -68,6 +68,7 @@ from domain.sap10_calculator.rdsap.cert_to_inputs import (
_mev_default_data_iuf, # pyright: ignore[reportPrivateUsage]
_TABLE_4G_DEFAULT_MEV_SFP_W_PER_L_PER_S, # pyright: ignore[reportPrivateUsage]
dimensions_from_cert,
fuel_cost_section_from_cert,
_table_12_factor_fuel_code, # pyright: ignore[reportPrivateUsage]
_table_12a_system_for_main, # pyright: ignore[reportPrivateUsage]
_is_electric_main, # pyright: ignore[reportPrivateUsage]
@ -9160,6 +9161,34 @@ def test_water_only_heat_network_half_charge_adds_to_the_dwellings_fuel_standing
assert abs(total - 84.0) <= 1e-9
def test_water_only_heat_network_half_charge_reaches_the_fuel_cost_worksheet() -> None:
# Arrange — the §C6 half charge must reach worksheet (251), not just the
# helper. A gas-boiler main (SAP 102, not a network) with WHC 950 keeps the
# dwelling on the STANDARD tariff, so `_fuel_cost` returns the real
# (240)..(255) result rather than the off-peak zero sentinel.
#
# Table 12 note (a): "The standing charge for gas fuels is added to space
# and water heating costs where the gas fuel is used for space heating" →
# Table 32 code 1 = £120. Plus the C6 half (£60) = £180.
from dataclasses import replace
base = _typical_semi_detached_epc()
epc = replace(
base,
sap_heating=make_sap_heating(
main_heating_details=[_gas_boiler_detail(sap_main_heating_code=102)],
water_heating_code=950, # DHW-only heat network (boilers)
),
)
# Act
fc = fuel_cost_section_from_cert(epc)
# Assert
assert fc is not None
assert abs(fc.additional_standing_charges_gbp - 180.0) <= 1e-9
def test_mev_default_data_iuf_is_table_329_system_type_10_value() -> None:
# Arrange / Act — SAP 10.2 Table 4g note 3 (PDF p.176) directs the
# default SFP to "the appropriate in-use factor for default data from