mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-06-08 11:17:27 +00:00
Slice 102b: cylinder storage loss via SAP 10.2 Tables 2/2a/2b
SAP 10.2 §4 line 7690 (full spec PDF p.136) defines the cylinder storage
loss cascade for any cert with a hot water cylinder lodged:
(54) = V × L × VF × TF (Table 2 absence-of-declared-loss branch)
(55) = (54) (no manufacturer's declared loss)
(56)m = (55) × n_m (per spec, n_m = days in month)
where
L = Table 2 (PDF p.158) Note 1 formula for the lodged insulation type
(factory-insulated cylinders: 0.005 + 0.55/(t+4.0); loose jacket:
0.005 + 1.76/(t+12.8))
VF = Table 2a (PDF p.158) Note 2 closed form (120/V)^(1/3)
TF = Table 2b (PDF p.159) base 0.60 for indirect / electric-immersion
cylinders, × 1.3 if no thermostat, × 0.9 if DHW separately timed
Prior, `water_heating_from_cert` hard-coded `solar_storage_monthly_kwh
= zero12` and `_water_heating_worksheet_and_gains` had no path to
populate it. The new `cylinder_storage_loss_monthly_kwh` helper in
`worksheet/water_heating.py` exposes Tables 2 / 2a / 2b as small typed
functions plus a composite; the cert-side orchestrator in
`rdsap/cert_to_inputs.py::_cylinder_storage_loss_override` resolves
the lodged cylinder fields and injects the override.
Code → litres mapping ground-truthed against worksheet (47) line refs
in /sap worksheets/Additional data with api/<cert>/dr87-*.pdf for the
7-cert ASHP cohort: code 3 → 160 L (Medium, 6 certs) and code 4 →
210 L (Large, cert 9418). Codes 2 / 5 / 6 (Normal / Inaccessible /
Exact) absent from the cohort and not yet mapped.
Cylinder insulation type code → "factory_insulated" mapping
(_CYLINDER_INSULATION_TYPE_FACTORY = 1) ground-truthed against all 7
ASHP cohort worksheets ("Foam" lodgement → SAP 10.2 Table 2 Note 2
"factory-insulated cylinder where the insulation is applied in the
course of manufacture irrespective of the insulation material used").
RdSAP §3 default table (PDF p.57) — "Hot water separately timed:
Post-1998 boiler: Yes" — applied to heat-pump main heating systems
(cat 4) per the cohort worksheet evidence.
Cert 0380 (Mitsubishi ASHP, 160 L factory 50 mm, thermostat + separately
timed) lands the spec formula at worksheet (56) Jan = 36.9530 kWh/month
(test pinned at 1e-4); HW kWh/yr 242.21 → 431.38, recovering ~189 kWh/yr
of cylinder loss the cascade was previously dropping.
Cohort regression: cert 0390-2954 (oil boiler + 160 L cylinder) tightens
PE residual -28.6783 → -27.5026 kWh/m² and CO2 residual -2.7640 →
-2.6570 t/yr — both move closer to the lodged values (improvement).
Re-pinned with a slice-102b note.
Closed boiler chain tests (001479, 0330, 9501) unaffected: those certs
lodge has_hot_water_cylinder=false so the override stays None and the
existing zero-storage-loss default fires.
This commit is contained in:
parent
4d3a0e9549
commit
76fdab42de
5 changed files with 252 additions and 9 deletions
|
|
@ -147,6 +147,7 @@ from domain.sap10_calculator.worksheet.water_heating import (
|
|||
WaterHeatingResult,
|
||||
combi_loss_monthly_kwh_table_3b_row_1_instantaneous,
|
||||
combi_loss_monthly_kwh_table_3c_two_profile_instantaneous,
|
||||
cylinder_storage_loss_monthly_kwh,
|
||||
water_efficiency_monthly_via_equation_d1,
|
||||
water_heating_from_cert,
|
||||
)
|
||||
|
|
@ -1849,6 +1850,37 @@ _TABLE_3A_COMBI_LOSS_MAIN_HEATING_CATEGORIES: Final[frozenset[int]] = frozenset(
|
|||
{1, 2, 3, 6}
|
||||
)
|
||||
|
||||
# RdSAP 10 §10.5 Table 28: lodged "Cylinder size" descriptors → SAP
|
||||
# calculation litres. The Open EPC API encodes the descriptor as an
|
||||
# integer per the cohort below (ground-truthed against worksheet (47)
|
||||
# line refs in /sap worksheets/Additional data with api/<cert>/dr87-*.pdf):
|
||||
# code 1 → no cylinder (gated via `has_hot_water_cylinder`)
|
||||
# code 3 → Medium (160 litres) (certs 0350, 0380, 2225, 2636,
|
||||
# 3800, 9285)
|
||||
# code 4 → Large (210 litres) (cert 9418)
|
||||
# Codes 2 / 5 / 6 (Normal / Inaccessible / Exact) not yet observed.
|
||||
_CYLINDER_SIZE_CODE_TO_LITRES: Final[dict[int, float]] = {3: 160.0, 4: 210.0}
|
||||
|
||||
# RdSAP 10 §10.5 code 7-11: cylinder insulation type. Empirical mapping
|
||||
# from the ASHP cohort (all 7 certs lodge code 1, worksheet shows
|
||||
# "Foam" → factory-applied per SAP 10.2 Table 2 Note 2).
|
||||
_CYLINDER_INSULATION_TYPE_FACTORY: Final[int] = 1
|
||||
|
||||
|
||||
def _separately_timed_dhw(main: Optional[MainHeatingDetail]) -> bool:
|
||||
"""RdSAP §3 default table (PDF p.57): "Hot water separately timed —
|
||||
Post-1998 boiler: Yes". Heat pumps (cat 4) and heat networks (cat 3,
|
||||
6) always have programmer-driven DHW timing, so default to True for
|
||||
those mains. For boiler-family mains (cat 1, 2) the cohort closes
|
||||
via the heuristic that age band K, L, M (post-2007) → True; older
|
||||
bands keep the spec's no-programmer default of False.
|
||||
"""
|
||||
if main is None:
|
||||
return False
|
||||
if main.main_heating_category == 4:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def _table_3a_combi_loss_default_applies(main: Optional[MainHeatingDetail]) -> bool:
|
||||
"""Gate for the Table 3a keep-hot 600 kWh/yr fall-through per SAP 10.2
|
||||
|
|
@ -1896,14 +1928,20 @@ def _water_heating_worksheet_and_gains(
|
|||
energy_content_monthly_kwh=bootstrap.energy_content_monthly_kwh,
|
||||
daily_hot_water_monthly_l_per_day=bootstrap.daily_hot_water_l_per_day_monthly,
|
||||
)
|
||||
main = _first_main_heating(epc)
|
||||
# SAP 10.2 §4 line 7702: non-combi main heating → (61)m = 0. Without
|
||||
# this gate the cascade falls through to `combi_loss_monthly_kwh_table_
|
||||
# 3a_keep_hot_time_clock()` (600 kWh/yr) on every cert lacking a PCDB
|
||||
# Table 105 boiler record — including all heat pump certs.
|
||||
if combi_loss_override is None and not _table_3a_combi_loss_default_applies(
|
||||
_first_main_heating(epc)
|
||||
main
|
||||
):
|
||||
combi_loss_override = zero_monthly
|
||||
# SAP 10.2 §4 lines 7670-7693 + Tables 2/2a/2b — cylinder storage loss
|
||||
# (56)m. Spec p.135 instructs entering 0 in (47) for instantaneous /
|
||||
# combi systems, so the override is only built when the cert explicitly
|
||||
# lodges a cylinder.
|
||||
storage_loss_override = _cylinder_storage_loss_override(epc, main)
|
||||
wh_result = water_heating_from_cert(
|
||||
epc=epc,
|
||||
mixer_shower_flow_rates_l_per_min=_mixer_shower_flow_rates_from_cert(epc),
|
||||
|
|
@ -1911,12 +1949,47 @@ def _water_heating_worksheet_and_gains(
|
|||
cold_water_temps_c=TABLE_J1_TCOLD_FROM_MAINS_C,
|
||||
low_water_use=False,
|
||||
combi_loss_monthly_kwh_override=combi_loss_override,
|
||||
solar_storage_monthly_kwh_override=storage_loss_override,
|
||||
has_electric_shower=has_electric_shower,
|
||||
electric_shower_count=electric_shower_count,
|
||||
)
|
||||
return wh_result, wh_result.heat_gains_monthly_kwh
|
||||
|
||||
|
||||
def _cylinder_storage_loss_override(
|
||||
epc: EpcPropertyData,
|
||||
main: Optional[MainHeatingDetail],
|
||||
) -> Optional[tuple[float, ...]]:
|
||||
"""Resolve (56)m for `water_heating_from_cert` from the cert's lodged
|
||||
cylinder fields. Returns None when no cylinder is lodged so the
|
||||
cascade keeps its existing zero-storage-loss default for combi /
|
||||
instantaneous systems. Per SAP 10.2 §4 line 7693 the (57)m solar
|
||||
adjustment equals (56)m when no dedicated solar storage volume is
|
||||
present (cohort certs have none).
|
||||
"""
|
||||
if not epc.has_hot_water_cylinder:
|
||||
return None
|
||||
sh = epc.sap_heating
|
||||
size_code = _int_or_none(sh.cylinder_size)
|
||||
if size_code is None:
|
||||
return None
|
||||
volume_l = _CYLINDER_SIZE_CODE_TO_LITRES.get(size_code)
|
||||
if volume_l is None:
|
||||
return None
|
||||
if sh.cylinder_insulation_type != _CYLINDER_INSULATION_TYPE_FACTORY:
|
||||
return None
|
||||
thickness_mm = sh.cylinder_insulation_thickness_mm
|
||||
if thickness_mm is None:
|
||||
return None
|
||||
return cylinder_storage_loss_monthly_kwh(
|
||||
volume_l=volume_l,
|
||||
insulation_type="factory_insulated",
|
||||
thickness_mm=float(thickness_mm),
|
||||
has_cylinder_thermostat=sh.cylinder_thermostat == "Y",
|
||||
separately_timed_dhw=_separately_timed_dhw(main),
|
||||
)
|
||||
|
||||
|
||||
def _apply_water_efficiency(
|
||||
*,
|
||||
wh_output_monthly_kwh: tuple[float, ...],
|
||||
|
|
|
|||
|
|
@ -1016,6 +1016,76 @@ def test_pcdb_combi_loss_override_returns_none_for_untested_or_storage_combis()
|
|||
)
|
||||
|
||||
|
||||
def test_cert_with_hot_water_cylinder_computes_storage_loss_56m_from_sap_tables_2_2a_2b() -> None:
|
||||
"""SAP 10.2 §4 line 7690 worksheet defines
|
||||
(56)m = (55) × n_m where (55) = (47) × (51) × (52) × (53)
|
||||
i.e. storage loss = volume × Table 2 loss factor × Table 2a volume
|
||||
factor × Table 2b temperature factor, scaled by days in month.
|
||||
|
||||
Cert 0380 worksheet (dr87-0001-000899.pdf) pins for the Mitsubishi
|
||||
ASHP + 160 L factory-insulated 50 mm cylinder with thermostat and
|
||||
separately-timed DHW:
|
||||
(51) L = 0.0152 kWh/litre/day (Table 2, factory, 50 mm)
|
||||
(52) VF = 0.9086 (Table 2a, V=160)
|
||||
(53) TF = 0.5400 (Table 2b, indirect × 0.9 timing)
|
||||
(55) combined = 1.1920 (V × L × VF × TF)
|
||||
(56)m Jan = 36.9530 kWh/month ((55) × 31)
|
||||
|
||||
Pre-fix, `_water_heating_worksheet_and_gains` passes a zero12 tuple
|
||||
as `solar_storage_monthly_kwh` to `water_heating_from_cert`, so the
|
||||
(62)m total demand is missing ~432 kWh/yr of cylinder storage loss
|
||||
that the spec explicitly accounts for.
|
||||
"""
|
||||
# Arrange — synthetic semi-detached, ASHP main, 160 L factory-
|
||||
# insulated cylinder (cylinder_size=3 = Medium per RdSAP §10.5 Table
|
||||
# 28; cylinder_insulation_type=1 = factory-applied; thickness 50 mm;
|
||||
# thermostat lodged; separately-timed DHW lodged via WHS code 901).
|
||||
hp_main = MainHeatingDetail(
|
||||
has_fghrs=False,
|
||||
main_fuel_type=29, # electricity
|
||||
heat_emitter_type=1,
|
||||
emitter_temperature=1,
|
||||
main_heating_control=2206,
|
||||
main_heating_category=4, # heat pump
|
||||
sap_main_heating_code=None,
|
||||
)
|
||||
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=[make_building_part()],
|
||||
sap_heating=make_sap_heating(
|
||||
main_heating_details=[hp_main],
|
||||
water_heating_code=901,
|
||||
cylinder_size=3, # Medium → 160 L per RdSAP §10.5 Table 28
|
||||
cylinder_insulation_type=1, # factory-applied
|
||||
cylinder_insulation_thickness_mm=50,
|
||||
cylinder_thermostat="Y",
|
||||
),
|
||||
)
|
||||
|
||||
# Act
|
||||
wh_result, _ = _water_heating_worksheet_and_gains(
|
||||
epc=epc,
|
||||
water_efficiency_pct=1.7,
|
||||
is_instantaneous=False,
|
||||
primary_age="D",
|
||||
pcdb_record=None,
|
||||
)
|
||||
|
||||
# Assert — (56)m Jan matches worksheet at 1e-4. Solar storage on the
|
||||
# WaterHeatingResult carries the (57)m tuple — for cert 0380 there
|
||||
# is no dedicated solar storage so (57)m = (56)m per spec line 7693.
|
||||
assert wh_result is not None
|
||||
expected_jan_kwh = 36.9530
|
||||
got_jan_kwh = wh_result.solar_storage_monthly_kwh[0]
|
||||
assert abs(got_jan_kwh - expected_jan_kwh) < 1e-4, (
|
||||
f"(56)Jan: got {got_jan_kwh!r}, want {expected_jan_kwh!r} per "
|
||||
f"SAP 10.2 §4 line 7690 + Tables 2/2a/2b"
|
||||
)
|
||||
|
||||
|
||||
def test_air_source_heat_pump_main_heating_zeroes_table_3a_combi_loss_per_sap_4_line_7702() -> None:
|
||||
"""SAP 10.2 §4 line 7702 worksheet defines (61)m as 'Combi loss for
|
||||
each month from Table 3a, 3b or 3c (enter "0" if not a combi
|
||||
|
|
|
|||
|
|
@ -121,14 +121,15 @@ _EXPECTATIONS: tuple[_GoldenExpectation, ...] = (
|
|||
cert_number="0390-2954-3640-2196-4175",
|
||||
actual_sap=60,
|
||||
expected_sap_resid=-6,
|
||||
expected_pe_resid_kwh_per_m2=-28.6783,
|
||||
expected_co2_resid_tonnes_per_yr=-2.7640,
|
||||
expected_pe_resid_kwh_per_m2=-27.5026,
|
||||
expected_co2_resid_tonnes_per_yr=-2.6570,
|
||||
notes=(
|
||||
"Large detached, TFA 360, age F, oil PCDB-listed. Cert lodges "
|
||||
"has_draught_lobby=true. Slice 97 added glazing_type=2 — "
|
||||
"windows now drop to spec U=2.0, widening PE -26.46 → -28.68 "
|
||||
"and CO2 -2.56 → -2.76 (the cert's lodged U for this glazing "
|
||||
"type appears to be higher than the spec's table-24 default)."
|
||||
"has_draught_lobby=true and a 160 L factory-insulated cylinder. "
|
||||
"Slice 97 added glazing_type=2 — windows now drop to spec U=2.0, "
|
||||
"widening PE → -28.68 and CO2 → -2.76. Slice 102b then applied "
|
||||
"SAP 10.2 Tables 2/2a/2b cylinder storage loss (~432 kWh/yr), "
|
||||
"tightening PE -28.68 → -27.50 and CO2 -2.76 → -2.66."
|
||||
),
|
||||
),
|
||||
_GoldenExpectation(
|
||||
|
|
|
|||
|
|
@ -58,6 +58,7 @@ class WaterHeatingResult:
|
|||
daily_hot_water_l_per_day_monthly: tuple[float, ...]
|
||||
energy_content_monthly_kwh: tuple[float, ...]
|
||||
distribution_loss_monthly_kwh: tuple[float, ...]
|
||||
solar_storage_monthly_kwh: tuple[float, ...] # (57)m — Tables 2/2a/2b
|
||||
combi_loss_monthly_kwh: tuple[float, ...]
|
||||
total_demand_monthly_kwh: tuple[float, ...]
|
||||
output_monthly_kwh: tuple[float, ...]
|
||||
|
|
@ -429,6 +430,93 @@ def combi_loss_monthly_kwh_table_3a_keep_hot_time_clock() -> tuple[float, ...]:
|
|||
return tuple(600.0 * n / _DAYS_IN_YEAR for n in _DAYS_IN_MONTH)
|
||||
|
||||
|
||||
# SAP 10.2 Table 2 (PDF p.158) hot water storage loss factor L kWh/litre/day.
|
||||
# Note 1 gives the smooth formulae the cascade uses (rather than the discrete
|
||||
# thickness rows) so any positive thickness resolves deterministically.
|
||||
_CYLINDER_INSULATION_FACTORY = "factory_insulated"
|
||||
_CYLINDER_INSULATION_LOOSE_JACKET = "loose_jacket"
|
||||
|
||||
|
||||
def cylinder_storage_loss_factor_table_2(
|
||||
*,
|
||||
insulation_type: Literal["factory_insulated", "loose_jacket"],
|
||||
thickness_mm: float,
|
||||
) -> float:
|
||||
"""SAP 10.2 Table 2 (PDF p.158) — hot water storage loss factor L
|
||||
in kWh/litre/day. Note 1 supplies the smooth formula:
|
||||
Cylinder, factory insulated: L = 0.005 + 0.55 / (t + 4.0)
|
||||
Cylinder, loose jacket: L = 0.005 + 1.76 / (t + 12.8)
|
||||
where t is the insulation thickness in mm. Note 2 applies the
|
||||
factory-insulated row to "all cases other than an electric CPSU
|
||||
where the insulation is applied in the course of manufacture
|
||||
irrespective of the insulation material used" — so foam, mineral
|
||||
wool, polyurethane and similar factory-applied insulations all
|
||||
resolve via the factory branch.
|
||||
"""
|
||||
if insulation_type == _CYLINDER_INSULATION_FACTORY:
|
||||
return 0.005 + 0.55 / (thickness_mm + 4.0)
|
||||
return 0.005 + 1.76 / (thickness_mm + 12.8)
|
||||
|
||||
|
||||
def cylinder_volume_factor_table_2a(volume_l: float) -> float:
|
||||
"""SAP 10.2 Table 2a (PDF p.158) — volume factor VF using Note 2's
|
||||
closed form `VF = (120 / Vc)^(1/3)`. The closed form matches the
|
||||
tabulated rows to 4 d.p. (V=160 → VF=0.9086 in the worksheet vs the
|
||||
table's 0.908 — Elmhurst computes via formula).
|
||||
"""
|
||||
return (120.0 / volume_l) ** (1.0 / 3.0)
|
||||
|
||||
|
||||
def cylinder_temperature_factor_table_2b(
|
||||
*,
|
||||
has_cylinder_thermostat: bool,
|
||||
separately_timed_dhw: bool,
|
||||
) -> float:
|
||||
"""SAP 10.2 Table 2b (PDF p.159) — temperature factor for a
|
||||
"Cylinder, indirect" or "Cylinder, electric immersion" lodgement
|
||||
(both base 0.60 in the "loss from Table 2" column). Multipliers per
|
||||
Notes a) / b):
|
||||
× 1.3 if cylinder thermostat is absent
|
||||
× 0.9 if domestic hot water is separately timed
|
||||
"""
|
||||
factor = 0.60
|
||||
if not has_cylinder_thermostat:
|
||||
factor *= 1.3
|
||||
if separately_timed_dhw:
|
||||
factor *= 0.9
|
||||
return factor
|
||||
|
||||
|
||||
def cylinder_storage_loss_monthly_kwh(
|
||||
*,
|
||||
volume_l: float,
|
||||
insulation_type: Literal["factory_insulated", "loose_jacket"],
|
||||
thickness_mm: float,
|
||||
has_cylinder_thermostat: bool,
|
||||
separately_timed_dhw: bool,
|
||||
) -> tuple[float, ...]:
|
||||
"""SAP 10.2 §4 line (56)m water storage loss per spec (PDF p.136):
|
||||
(54) = V × L × VF × TF (Table 2 absence-of-declared-loss branch)
|
||||
(55) = (54) (no manufacturer's declared loss)
|
||||
(56)m = (55) × n_m (n_m = days in month)
|
||||
|
||||
Returns 12 monthly values in calendar order Jan..Dec. The cert's
|
||||
"(57)m = (56)m" identity (spec line 7693) applies when no dedicated
|
||||
solar storage is present in the vessel — callers handling solar
|
||||
storage must adjust further per `(57)m = (56)m × [(47) - Vs] / (47)`.
|
||||
"""
|
||||
L = cylinder_storage_loss_factor_table_2(
|
||||
insulation_type=insulation_type, thickness_mm=thickness_mm,
|
||||
)
|
||||
VF = cylinder_volume_factor_table_2a(volume_l)
|
||||
TF = cylinder_temperature_factor_table_2b(
|
||||
has_cylinder_thermostat=has_cylinder_thermostat,
|
||||
separately_timed_dhw=separately_timed_dhw,
|
||||
)
|
||||
combined_55 = volume_l * L * VF * TF
|
||||
return tuple(combined_55 * n for n in _DAYS_IN_MONTH)
|
||||
|
||||
|
||||
def total_water_heating_demand_monthly_kwh(
|
||||
*,
|
||||
energy_content_monthly_kwh: tuple[float, ...],
|
||||
|
|
@ -639,6 +727,7 @@ def water_heating_from_cert(
|
|||
cold_water_temps_c: tuple[float, ...],
|
||||
low_water_use: bool,
|
||||
combi_loss_monthly_kwh_override: Optional[tuple[float, ...]] = None,
|
||||
solar_storage_monthly_kwh_override: Optional[tuple[float, ...]] = None,
|
||||
electric_shower_monthly_kwh_override: Optional[tuple[float, ...]] = None,
|
||||
has_electric_shower: bool = False,
|
||||
electric_shower_count: int = 0,
|
||||
|
|
@ -719,10 +808,15 @@ def water_heating_from_cert(
|
|||
else combi_loss_monthly_kwh_table_3a_keep_hot_time_clock()
|
||||
)
|
||||
zero12 = (0.0,) * 12
|
||||
solar_storage = (
|
||||
solar_storage_monthly_kwh_override
|
||||
if solar_storage_monthly_kwh_override is not None
|
||||
else zero12
|
||||
)
|
||||
total_demand = total_water_heating_demand_monthly_kwh(
|
||||
energy_content_monthly_kwh=energy_content,
|
||||
distribution_loss_monthly_kwh=distribution,
|
||||
solar_storage_monthly_kwh=zero12,
|
||||
solar_storage_monthly_kwh=solar_storage,
|
||||
primary_loss_monthly_kwh=zero12,
|
||||
combi_loss_monthly_kwh=combi,
|
||||
)
|
||||
|
|
@ -750,7 +844,7 @@ def water_heating_from_cert(
|
|||
gains = heat_gains_from_water_heating_monthly_kwh(
|
||||
energy_content_monthly_kwh=energy_content,
|
||||
distribution_loss_monthly_kwh=distribution,
|
||||
solar_storage_monthly_kwh=zero12,
|
||||
solar_storage_monthly_kwh=solar_storage,
|
||||
primary_loss_monthly_kwh=zero12,
|
||||
combi_loss_monthly_kwh=combi,
|
||||
electric_shower_monthly_kwh=electric_shower,
|
||||
|
|
@ -761,6 +855,7 @@ def water_heating_from_cert(
|
|||
daily_hot_water_l_per_day_monthly=daily_total,
|
||||
energy_content_monthly_kwh=energy_content,
|
||||
distribution_loss_monthly_kwh=distribution,
|
||||
solar_storage_monthly_kwh=solar_storage,
|
||||
combi_loss_monthly_kwh=combi,
|
||||
total_demand_monthly_kwh=total_demand,
|
||||
output_monthly_kwh=output,
|
||||
|
|
|
|||
|
|
@ -96,7 +96,9 @@ def make_sap_heating(
|
|||
water_heating_code: Optional[int] = 901,
|
||||
water_heating_fuel: Optional[int] = 26,
|
||||
cylinder_size: Optional[Union[int, str]] = None,
|
||||
cylinder_insulation_type: Optional[int] = None,
|
||||
cylinder_insulation_thickness_mm: Optional[int] = None,
|
||||
cylinder_thermostat: Optional[str] = None,
|
||||
secondary_fuel_type: Optional[int] = None,
|
||||
secondary_heating_type: Optional[int] = None,
|
||||
number_baths: Optional[int] = None,
|
||||
|
|
@ -113,7 +115,9 @@ def make_sap_heating(
|
|||
water_heating_code=water_heating_code,
|
||||
water_heating_fuel=water_heating_fuel,
|
||||
cylinder_size=cylinder_size,
|
||||
cylinder_insulation_type=cylinder_insulation_type,
|
||||
cylinder_insulation_thickness_mm=cylinder_insulation_thickness_mm,
|
||||
cylinder_thermostat=cylinder_thermostat,
|
||||
secondary_fuel_type=secondary_fuel_type,
|
||||
secondary_heating_type=secondary_heating_type,
|
||||
number_baths=number_baths,
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue