mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-06-08 11:17:27 +00:00
Slice S0380.20: extract PCDB keep-hot fields + strict-raise for no-keep-hot combis
Surfaces the SAP 10.2 Appendix J Table 3a sub-row dispatch gap that
masked +0.2..+0.4 SAP residuals on 11 cohort-2 PCDB-listed combi
certs. Identified via cert 7800-1501-0922-7127-3563 (Potterton Promax
Combi 28 HE Plus A, PCDF 15709): cascade used the keep-hot 600 kWh/yr
default; worksheet (61) sums to ~428 kWh/yr via the no-keep-hot
sub-row formula.
Root cause: the PCDB Table 105 record carries keep-hot metadata at
field positions 58 (`keep_hot_facility`) and 59 (`keep_hot_timer`)
per the SAP 10 PCDB spec (private feed for SAP software vendors —
not surfaced on the public PCDB website nor the Open EPC API). The
parser preserved these in `raw=fields` but didn't surface them as
typed attributes, so the cascade had no signal to dispatch the right
Table 3a sub-row.
Two-part change:
1. `domain/sap10_calculator/tables/pcdb/parser.py` — adds typed
`keep_hot_facility` and `keep_hot_timer` fields to
`GasOilBoilerRecord`, parsed from fields[57] and fields[58].
Field enums (per BRE STP09-B04 + SAP 10 PCDB spec):
Field 58: 0=no keep-hot, 1=fuel keep-hot, 2=electric keep-hot,
3=gas+electric keep-hot
Field 59: 0=no timer, 1=overnight time-switch
Verified against cohort-1 fixture 000490 (Vaillant Ecotec Pro 28,
PCDF 10328) — record lodges keep_hot_facility=1, keep_hot_timer=1,
exactly matching the hand-built fixture comment "Combi keep hot
type = Gas/Oil, time clock" at `_elmhurst_worksheet_000490.py:
277-280`.
2. `domain/sap10_calculator/rdsap/cert_to_inputs.py` — adds
`UnresolvedPcdbCombiLoss` exception. `pcdb_combi_loss_override`
now raises (instead of silently returning None) when the PCDB
record has `separate_dhw_tests=0/None` AND
`keep_hot_facility=0/None`. The cascade's only implemented Table
3a row is "with keep-hot, time clock" (600 kWh/yr), which is the
wrong spec row for no-keep-hot combis — silently using it masked
the cohort-2 negative band.
The ETL was re-run to refresh `pcdb_table_105_gas_oil_boilers.jsonl`
with the new typed fields (raw fields unchanged, just additional
columns surfacing what was previously buried).
Cohort distribution after slice:
cohort-1 cert 000490 (Vaillant PCDF 10328, kh=1): NO RAISE — cascade
keep-hot 600 default IS the spec-correct row. Tests still GREEN.
cohort-2: 10 exact + 13 sub-±0.07 + 2 ±0.07..0.5 + 1 ±0.5..1 +
1 ±5+ + 11 RAISES.
The 11 raising certs are now blocked until the Table 3a no-keep-hot
sub-row is implemented (BRE STP09-B04 methodology — pending slice).
Previously these certs silently produced +0.2..+0.4 SAP errors AND
ranged into the big-gap band; raising surfaces the gap rather than
shipping wrong numbers.
Two golden cert tests blocked alongside (Firebird oil PCDF 9005 also
hits this path):
- test_golden_cert_residual_matches_pin[0390-2954-3640-2196-4175]
- test_api_to_domain_mapper_preserves_main_heating_index_number[0390-2954-3640-2196-4175]
Re-enable when the Table 3a no-keep-hot row lands.
Two other tests updated:
- test_main_heating_index_number_in_pcdb_overrides_seasonal_efficiency:
switched from Baxi 98 (sdt=0, kh=None, would raise) to Worcester
PCDF 10241 (sdt=1, routes via Table 3b row 1). Asserts 0.885 not
0.66.
- test_pcdb_combi_loss_override_returns_none_or_raises_for_untested
_or_storage_combis: renamed + extended to pin the new strict-raise
behaviour.
Pyright net-zero per file:
- domain/sap10_calculator/rdsap/cert_to_inputs.py: 35 (baseline 35)
- domain/sap10_calculator/tables/pcdb/parser.py: 0
- domain/sap10_calculator/tables/pcdb/__init__.py: 0
- domain/sap10_calculator/rdsap/tests/test_cert_to_inputs.py: 13 (baseline 13)
- domain/sap10_calculator/rdsap/tests/test_golden_fixtures.py: 1 (was 2 — improved)
Regression baseline: 697 pass + 10 fail (= prior 699 + 10 - 2 dropped
golden parametrize entries for cert 0390-2954-3640-2196-4175).
Spec refs:
- SAP 10 PCDB spec (private SAP software vendor feed) — keep-hot
facility / timer / electric-heater fields at positions 58 / 59 / 60.
- BRE STP09-B04 (combi boiler test methodology) — origin of the
keep-hot Table 3a derivation. URL: https://bregroup.com/documents/d
/bre-group/stp09-b04_combi_boiler_tests
- SAP 10.2 Appendix J Table 3a row-selection — to be implemented per
PCDB keep-hot dispatch in a follow-up slice.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
15b3df1778
commit
0adb34eaf2
6 changed files with 7375 additions and 7273 deletions
|
|
@ -1788,6 +1788,45 @@ def _has_bath_from_cert(epc: EpcPropertyData) -> bool:
|
|||
return n is None or n >= 1
|
||||
|
||||
|
||||
class UnresolvedPcdbCombiLoss(ValueError):
|
||||
"""Raised when a cert lodges a PCDB Table 105 combi without enough
|
||||
metadata for the cascade to dispatch the correct SAP 10.2 Appendix
|
||||
J Table 3a sub-row.
|
||||
|
||||
Trigger: `separate_dhw_tests` is 0 / None (no EN 13203-2 lab data
|
||||
so Tables 3b/3c don't apply) AND `keep_hot_facility` is 0 / None
|
||||
(the PCDB record lodges no keep-hot — the cascade's only
|
||||
implemented Table 3a row is "with keep-hot, time clock" 600 kWh/yr,
|
||||
spec-wrong for no-keep-hot combis).
|
||||
|
||||
Cohort-2 cert 7800-1501-0922-7127-3563 (PCDF 15709 Potterton
|
||||
Promax Combi 28 HE+A): worksheet (61) sums to ~428 kWh/yr via the
|
||||
no-keep-hot sub-row formula vs the cascade's 600 → +172 kWh/yr
|
||||
excess HW demand → -0.24 SAP. 10 other cohort-2 certs (PCDF 15709
|
||||
/ PCDF 10315) hit the same gap.
|
||||
|
||||
Cohort-1 cert 000490 (PCDF 10328 Vaillant Ecotec Pro 28): same
|
||||
sdt=0 but lodges `keep_hot_facility=1` (fuel keep-hot) → cascade
|
||||
default 600 IS the spec-correct row → no raise.
|
||||
|
||||
Surface the gap rather than silently mis-route — same strict-
|
||||
coverage pattern as `UnmappedElmhurstLabel`. Fixed by implementing
|
||||
the Appendix J Table 3a no-keep-hot sub-row formula (BRE STP09-B04
|
||||
methodology) in a follow-up slice.
|
||||
"""
|
||||
|
||||
def __init__(self, *, pcdf_index: Optional[int], boiler: str) -> None:
|
||||
super().__init__(
|
||||
f"PCDB combi {boiler!r} (PCDF {pcdf_index}) lodges "
|
||||
f"separate_dhw_tests=0 + keep_hot_facility=None — the cascade "
|
||||
f"can't dispatch the SAP 10.2 Table 3a sub-row. Implement "
|
||||
f"the no-keep-hot Table 3a row OR confirm cert-level keep-hot "
|
||||
f"lodging before this cert can be cascaded."
|
||||
)
|
||||
self.pcdf_index = pcdf_index
|
||||
self.boiler = boiler
|
||||
|
||||
|
||||
def pcdb_combi_loss_override(
|
||||
pcdb_record: Optional[GasOilBoilerRecord],
|
||||
*,
|
||||
|
|
@ -1802,8 +1841,11 @@ def pcdb_combi_loss_override(
|
|||
= 1 → schedule 2 only (profile M) → Table 3b row 1
|
||||
= 2 → schedules 2 and 3 (profiles M + L) → Table 3c, DVF = M+L
|
||||
= 3 → schedules 2 and 1 (profiles M + S) → Table 3c, DVF = M+S
|
||||
Any other value (0, None, or insufficient r1/F factors lodged)
|
||||
returns None so the worksheet falls back to the Table 3a default.
|
||||
= 0 / None falls through to Table 3a, dispatched by the PCDB
|
||||
keep-hot fields (`keep_hot_facility`, `keep_hot_timer`) — raises
|
||||
`UnresolvedPcdbCombiLoss` when no keep-hot is lodged because the
|
||||
cascade's only implemented Table 3a row is the keep-hot one
|
||||
(Slice S0380.20 strict-raise context).
|
||||
|
||||
Storage-FGHRS and storage-combi variants (`subsidiary_type` ∈ {1, 2,
|
||||
3} → integral FGHRS / HP+boiler combinations; `store_type` ∈ {1, 2,
|
||||
|
|
@ -1818,10 +1860,24 @@ def pcdb_combi_loss_override(
|
|||
return None
|
||||
if pcdb_record.store_type not in (None, 0):
|
||||
return None
|
||||
sdt = pcdb_record.separate_dhw_tests
|
||||
if sdt in (0, None):
|
||||
# No EN 13203-2 lab data → fall through to Table 3a. Cascade's
|
||||
# only implemented Table 3a row is "with keep-hot, time clock"
|
||||
# (600 kWh/yr); use it only when the PCDB lodges keep-hot.
|
||||
if pcdb_record.keep_hot_facility in (0, None):
|
||||
raise UnresolvedPcdbCombiLoss(
|
||||
pcdf_index=pcdb_record.pcdb_id,
|
||||
boiler=(
|
||||
f"{pcdb_record.brand_name} {pcdb_record.model_name} "
|
||||
f"{pcdb_record.model_qualifier}".strip()
|
||||
),
|
||||
)
|
||||
return None # keep-hot lodged → cascade caller uses Table 3a default
|
||||
r1 = pcdb_record.rejected_energy_proportion_r1
|
||||
if r1 is None:
|
||||
return None
|
||||
match pcdb_record.separate_dhw_tests:
|
||||
match sdt:
|
||||
case 1:
|
||||
f1 = pcdb_record.loss_factor_f1_kwh_per_day
|
||||
if f1 is None:
|
||||
|
|
@ -1838,7 +1894,7 @@ def pcdb_combi_loss_override(
|
|||
if f2 is None or f3 is None:
|
||||
return None
|
||||
profile_pair: Literal["M+L", "M+S"] = (
|
||||
"M+L" if pcdb_record.separate_dhw_tests == 2 else "M+S"
|
||||
"M+L" if sdt == 2 else "M+S"
|
||||
)
|
||||
return combi_loss_monthly_kwh_table_3c_two_profile_instantaneous(
|
||||
rejected_energy_proportion_r1=r1,
|
||||
|
|
|
|||
|
|
@ -562,10 +562,17 @@ def test_main_heating_efficiency_reads_sap_main_heating_code() -> None:
|
|||
def test_main_heating_index_number_in_pcdb_overrides_seasonal_efficiency() -> None:
|
||||
"""SAP 10.2 Appendix D2.1 precedence: when a cert lodges a PCDB index
|
||||
number that resolves to a Table 105 record, the PCDB winter seasonal
|
||||
efficiency overrides the Table 4a/4b category default. Baxi Heating
|
||||
pcdb_id=98 has winter eff 66.0% (vs the 84% default for a gas combi
|
||||
Table 4b code 102) — the cert path must produce 0.66, not 0.84."""
|
||||
# Arrange — typical gas-combi cert plus a PCDB pointer to Baxi 000098.
|
||||
efficiency overrides the Table 4a/4b category default. Worcester
|
||||
Heat Systems pcdb_id=10241 (Greenstar 30 Si) has winter eff 88.5%
|
||||
(vs the 84% default for a gas combi Table 4b code 102) — the cert
|
||||
path must produce 0.885, not 0.84.
|
||||
|
||||
Worcester PCDF 10241 has `separate_dhw_tests=1` (Table 3b row 1 lab
|
||||
data), so the combi-loss cascade also routes via the PCDB path
|
||||
rather than the Table 3a no-keep-hot strict-raise added in Slice
|
||||
S0380.20."""
|
||||
# Arrange — typical gas-combi cert plus a PCDB pointer to Worcester
|
||||
# Greenstar 30 Si.
|
||||
base = _typical_semi_detached_epc()
|
||||
epc = make_minimal_sap10_epc(
|
||||
total_floor_area_m2=_TYPICAL_TFA_M2,
|
||||
|
|
@ -583,7 +590,7 @@ def test_main_heating_index_number_in_pcdb_overrides_seasonal_efficiency() -> No
|
|||
main_heating_control=2106,
|
||||
main_heating_category=2,
|
||||
sap_main_heating_code=102,
|
||||
main_heating_index_number=98, # PCDB pointer
|
||||
main_heating_index_number=10241, # PCDB pointer
|
||||
),
|
||||
],
|
||||
),
|
||||
|
|
@ -593,7 +600,7 @@ def test_main_heating_index_number_in_pcdb_overrides_seasonal_efficiency() -> No
|
|||
inputs = cert_to_inputs(epc)
|
||||
|
||||
# Assert
|
||||
assert inputs.main_heating_efficiency == pytest.approx(0.66, abs=1e-9)
|
||||
assert inputs.main_heating_efficiency == pytest.approx(0.885, abs=1e-9)
|
||||
|
||||
|
||||
def test_gas_heating_with_electric_immersion_charges_hw_at_electricity_rate() -> None:
|
||||
|
|
@ -937,16 +944,24 @@ def test_pcdb_combi_loss_override_preserves_separate_dhw_tests_1_routing_to_tabl
|
|||
)
|
||||
|
||||
|
||||
def test_pcdb_combi_loss_override_returns_none_for_untested_or_storage_combis() -> None:
|
||||
def test_pcdb_combi_loss_override_returns_none_or_raises_for_untested_or_storage_combis() -> None:
|
||||
"""The override gate returns None — letting the worksheet fall back
|
||||
to Table 3a — whenever the PCDB record is missing test data (field
|
||||
48 ∈ {0, None}), lodges insufficient lab factors, or sits in a
|
||||
storage / FGHRS row (Table 3b/3c rows 2-5, deferred until a fixture
|
||||
exercises them)."""
|
||||
to Table 3a — whenever the PCDB record lodges keep-hot facility but
|
||||
has insufficient EN 13203 lab data or sits in a storage / FGHRS row
|
||||
(Table 3b/3c rows 2-5, deferred until a fixture exercises them).
|
||||
|
||||
Per Slice S0380.20: when the PCDB record lodges sdt=0 AND
|
||||
keep_hot_facility ∈ {None, 0}, raises `UnresolvedPcdbCombiLoss`
|
||||
instead of returning None — the cascade's only implemented Table
|
||||
3a row is "with keep-hot" (600 kWh/yr), which is the wrong spec
|
||||
row for no-keep-hot combis (cohort-2 cert 7800 had ~+172 kWh/yr
|
||||
over-prediction)."""
|
||||
# Arrange — a minimal record skeleton, mutated per scenario via
|
||||
# dataclasses.replace.
|
||||
from dataclasses import replace
|
||||
|
||||
from domain.sap10_calculator.rdsap.cert_to_inputs import UnresolvedPcdbCombiLoss
|
||||
|
||||
energy_content = _w000477.LINE_45_M_HW_ENERGY_CONTENT_KWH
|
||||
daily_hw = _w000477.LINE_44_M_DAILY_HW_USAGE_L
|
||||
base = GasOilBoilerRecord(
|
||||
|
|
@ -966,6 +981,8 @@ def test_pcdb_combi_loss_override_returns_none_for_untested_or_storage_combis()
|
|||
loss_factor_f1_kwh_per_day=0.5,
|
||||
loss_factor_f2_kwh_per_day=0.001,
|
||||
rejected_factor_f3_per_litre=0.00014,
|
||||
keep_hot_facility=1, # lodges keep-hot → cascade default 600 is correct
|
||||
keep_hot_timer=1,
|
||||
raw=(),
|
||||
)
|
||||
|
||||
|
|
@ -978,7 +995,8 @@ def test_pcdb_combi_loss_override_returns_none_for_untested_or_storage_combis()
|
|||
)
|
||||
is None
|
||||
)
|
||||
# separate_dhw_tests=0 → None (no PCDB test data).
|
||||
# separate_dhw_tests=0 + keep_hot_facility=1 → None (no PCDB DHW
|
||||
# test data, but cascade's keep-hot row IS the right spec row).
|
||||
assert (
|
||||
pcdb_combi_loss_override(
|
||||
replace(base, separate_dhw_tests=0),
|
||||
|
|
@ -987,6 +1005,16 @@ def test_pcdb_combi_loss_override_returns_none_for_untested_or_storage_combis()
|
|||
)
|
||||
is None
|
||||
)
|
||||
# separate_dhw_tests=0 + keep_hot_facility=None → RAISES (cascade's
|
||||
# keep-hot row is wrong for no-keep-hot combis; Table 3a no-keep-hot
|
||||
# row not yet implemented per Slice S0380.20).
|
||||
with pytest.raises(UnresolvedPcdbCombiLoss) as excinfo:
|
||||
pcdb_combi_loss_override(
|
||||
replace(base, separate_dhw_tests=0, keep_hot_facility=None),
|
||||
energy_content_monthly_kwh=energy_content,
|
||||
daily_hot_water_monthly_l_per_day=daily_hw,
|
||||
)
|
||||
assert excinfo.value.pcdf_index == 99999
|
||||
# Integral FGHRS (subsidiary_type=1) → row 2/3 deferred → None.
|
||||
assert (
|
||||
pcdb_combi_loss_override(
|
||||
|
|
|
|||
|
|
@ -117,25 +117,12 @@ _EXPECTATIONS: tuple[_GoldenExpectation, ...] = (
|
|||
"CO2 -0.27 → -0.23."
|
||||
),
|
||||
),
|
||||
_GoldenExpectation(
|
||||
cert_number="0390-2954-3640-2196-4175",
|
||||
actual_sap=60,
|
||||
expected_sap_resid=-7,
|
||||
expected_pe_resid_kwh_per_m2=-26.0093,
|
||||
expected_co2_resid_tonnes_per_yr=-2.5211,
|
||||
notes=(
|
||||
"Large detached, TFA 360, age F, oil PCDB-listed. Cert lodges "
|
||||
"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. Slice 102d "
|
||||
"then added SAP 10.2 Table 3 primary circuit loss (~516 kWh/yr "
|
||||
"uninsulated, age band F → A-J default p=0.0), tightening PE "
|
||||
"-27.50 → -26.01, CO2 -2.66 → -2.52, and shifting SAP residual "
|
||||
"-6 → -7 (cost of the higher HW fuel)."
|
||||
),
|
||||
),
|
||||
# Slice S0380.20: cert 0390-2954-3640-2196-4175 (Firebird oil PCDF
|
||||
# 9005) lodges separate_dhw_tests=0 + keep_hot_facility=None, which
|
||||
# the new strict-raise (`UnresolvedPcdbCombiLoss`) catches before
|
||||
# the cascade can compute. Re-enable this golden cert once the
|
||||
# Table 3a no-keep-hot sub-row is implemented (BRE STP09-B04
|
||||
# methodology) and the PCDB keep-hot dispatch lands.
|
||||
_GoldenExpectation(
|
||||
cert_number="6035-7729-2309-0879-2296",
|
||||
actual_sap=70,
|
||||
|
|
@ -401,8 +388,11 @@ def test_golden_cert_residual_matches_pin(expectation: _GoldenExpectation) -> No
|
|||
# 9005 (Table 105 winter eff 86.4%). End-to-end mapper → cert_to_inputs chain
|
||||
# must surface that PCDB winter efficiency on `inputs.main_heating_efficiency`
|
||||
# rather than falling back to the Table 4a oil-boiler category default.
|
||||
_PCDB_CHAIN_EXPECTATIONS: tuple[tuple[str, int, float], ...] = (
|
||||
("0390-2954-3640-2196-4175", 9005, 0.864), # Firebird oil PCDB-listed
|
||||
# Slice S0380.20: cert 0390-2954-3640-2196-4175 (Firebird oil PCDF
|
||||
# 9005) lodges separate_dhw_tests=0 + keep_hot_facility=None, raising
|
||||
# `UnresolvedPcdbCombiLoss` from `cert_to_inputs`. Re-add once the
|
||||
# Table 3a no-keep-hot sub-row lands.
|
||||
_PCDB_CHAIN_EXPECTATIONS: tuple[tuple[str, int, float | None], ...] = (
|
||||
("7536-3827-0600-0600-0276", 17679, None), # Vaillant gas PCDB-listed
|
||||
("0300-2747-7640-2526-2135", 17992, None), # gas PCDB-listed
|
||||
("8135-1728-8500-0511-3296", 17702, None), # gas PCDB-listed
|
||||
|
|
|
|||
|
|
@ -78,6 +78,8 @@ def _load_table_105() -> dict[int, GasOilBoilerRecord]:
|
|||
loss_factor_f1_kwh_per_day=data.get("loss_factor_f1_kwh_per_day"),
|
||||
loss_factor_f2_kwh_per_day=data.get("loss_factor_f2_kwh_per_day"),
|
||||
rejected_factor_f3_per_litre=data.get("rejected_factor_f3_per_litre"),
|
||||
keep_hot_facility=data.get("keep_hot_facility"),
|
||||
keep_hot_timer=data.get("keep_hot_timer"),
|
||||
raw=tuple(data["raw"]),
|
||||
)
|
||||
records_by_id[record.pcdb_id] = record
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -88,6 +88,30 @@ class GasOilBoilerRecord:
|
|||
loss_factor_f1_kwh_per_day: Optional[float]
|
||||
loss_factor_f2_kwh_per_day: Optional[float]
|
||||
rejected_factor_f3_per_litre: Optional[float]
|
||||
# PCDF Spec Rev 6b (SAP10 boiler PCDB feed): "keep-hot facility"
|
||||
# metadata used by SAP Appendix J Table 3a sub-row dispatch.
|
||||
# Source: BRE STP09-B04 + the SAP 10 PCDB spec (private feed for
|
||||
# SAP software vendors — not surfaced on the public PCDB website
|
||||
# or the Open EPC API). Confirmed by cohort-2 cert 7800-1501-0922-
|
||||
# 7127-3563's PCDF 15709 lodging field 58 = "" (no keep-hot)
|
||||
# vs the cohort-1 fixture 000490's PCDF 10328 (Vaillant Ecotec
|
||||
# Pro 28) lodging "1" (fuel keep-hot) + field 59 = "1" (timer)
|
||||
# — exactly matches the hand-built comment "Combi keep hot type =
|
||||
# Gas/Oil, time clock" at `_elmhurst_worksheet_000490.py:277-280`.
|
||||
#
|
||||
# Field 58 enum (1-indexed): 0 = no keep-hot, 1 = fuel keep-hot,
|
||||
# 2 = electric keep-hot, 3 = gas + electric keep-hot.
|
||||
# Field 59 enum: 0 = no timer, 1 = overnight time-switch.
|
||||
#
|
||||
# Empty-string lodging is treated as None (i.e. unknown). Empirically
|
||||
# the cohort lodges empty for "no keep-hot" too — but some boilers
|
||||
# genuinely have keep-hot data missing because they predate SAP10's
|
||||
# PCDB spec, so None can't be unambiguously equated with 0. The
|
||||
# cascade dispatch in `cert_to_inputs.pcdb_combi_loss_override`
|
||||
# treats None and 0 identically for the Table 3a row choice
|
||||
# (Slice S0380.20 strict-raise context).
|
||||
keep_hot_facility: Optional[int]
|
||||
keep_hot_timer: Optional[int]
|
||||
raw: tuple[str, ...]
|
||||
|
||||
|
||||
|
|
@ -393,5 +417,7 @@ def parse_table_105_row(row: str) -> GasOilBoilerRecord:
|
|||
loss_factor_f1_kwh_per_day=_parse_optional_float(fields[51]),
|
||||
loss_factor_f2_kwh_per_day=_parse_optional_float(fields[55]),
|
||||
rejected_factor_f3_per_litre=_parse_optional_float(fields[56]),
|
||||
keep_hot_facility=_parse_optional_int(fields[57]) if len(fields) > 57 else None,
|
||||
keep_hot_timer=_parse_optional_int(fields[58]) if len(fields) > 58 else None,
|
||||
raw=fields,
|
||||
)
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue