Slice 102f-prep.1: PCDB Table 362 heating_duration_code field

SAP 10.2 Appendix N3.5 (PDF p.105 line 6099) — heat-pump packages
lodge a "Daily heating duration" field encoded as "24" / "16" / "9"
/ "V" (Variable). Footnote 48 (PDF p.105): "Daily heating durations
of 24, 16 and 9 hours are retained for legacy purposes" — modern
records always lodge "V".

Format-465 position 48 holds the code; cohort ground truth: "V" on
Mitsubishi PUZ-WM50VHA (104568) and Daikin EDLQ05CAV3 (102421).
The field drives Appendix N3.5 + Table N4/N5 day allocation for the
extended-heating MIT cascade (slice 102f-prep.2 onward).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Khalim Conn-Kowlessar 2026-05-27 13:09:07 +00:00 committed by Jun-te Kim
parent ebb492c5d3
commit 74240f8c44
2 changed files with 32 additions and 0 deletions

View file

@ -182,6 +182,13 @@ class HeatPumpRecord:
`max_output_kw` (spec §A.23 field 30) is the PSR-denominator per
PDF p.100 line 5946 ("maximum nominal output of the package").
`heating_duration_code` (format-465 position 48) encodes the
package's daily heating duration per SAP 10.2 Appendix N3.5 (PDF
p.105 line 6099): "24", "16", "9", or "V" (Variable). Drives the
extended-heating-schedule day allocation via Table N4/N5. Per
footnote 48, modern records always lodge "V"; the fixed durations
are retained for legacy purposes.
`psr_groups` carries the PSR-dependent efficiency table (up to 14
rows) used by SAP 10.2 Appendix N3.6 (space heating) and N3.7(a)
(water heating), interpolated at the dwelling's PSR per spec PDF
@ -199,6 +206,7 @@ class HeatPumpRecord:
vessel_heat_loss_kwh_per_day: Optional[float]
vessel_heat_exchanger_area_m2: Optional[float]
max_output_kw: Optional[float]
heating_duration_code: Optional[str]
psr_groups: tuple[PsrEfficiencyGroup, ...]
raw: tuple[str, ...]
@ -216,6 +224,10 @@ _HP_IDX_VESSEL_VOLUME_L: Final[int] = 24
_HP_IDX_VESSEL_HEAT_LOSS_KWH_PER_DAY: Final[int] = 25
_HP_IDX_VESSEL_HEAT_EXCHANGER_AREA_M2: Final[int] = 26
_HP_IDX_MAX_OUTPUT_KW: Final[int] = 47
# Format 465 position 48 — daily heating duration code per SAP 10.2
# Appendix N3.5 (PDF p.105 line 6099). Cohort ground-truth: "V" lodged
# on Mitsubishi PUZ-WM50VHA (104568) and Daikin EDLQ05CAV3 (102421).
_HP_IDX_HEATING_DURATION_CODE: Final[int] = 48
# Format 465 PSR-group block: idx[58] is the group count; groups start
# at idx[59], 9 fields wide, with PSR / η_space,1 / η_water,3 at the
@ -315,6 +327,7 @@ def parse_heat_pump_row_raw(raw: tuple[str, ...]) -> HeatPumpRecord:
def at(idx: int) -> str:
return raw[idx] if idx < len(raw) else ""
duration_raw = at(_HP_IDX_HEATING_DURATION_CODE).strip()
return HeatPumpRecord(
pcdb_id=int(raw[0]),
brand_name=at(_HP_IDX_BRAND_NAME),
@ -331,6 +344,7 @@ def parse_heat_pump_row_raw(raw: tuple[str, ...]) -> HeatPumpRecord:
at(_HP_IDX_VESSEL_HEAT_EXCHANGER_AREA_M2)
),
max_output_kw=_parse_optional_float(at(_HP_IDX_MAX_OUTPUT_KW)),
heating_duration_code=duration_raw if duration_raw else None,
psr_groups=_parse_psr_groups(raw),
raw=raw,
)

View file

@ -57,6 +57,24 @@ def test_heat_pump_record_returns_verified_mitsubishi_ecodan_104568_header() ->
assert record.max_output_kw == 4.39
def test_heat_pump_record_heating_duration_code_for_104568_is_variable() -> None:
"""SAP 10.2 Appendix N3.5 (PDF p.106) — extended heating duration is
sourced from PCDB Table 362 field "Daily heating duration", encoded
as "24" / "16" / "9" / "V" (Variable). Per spec PDF p.105 line 6099
+ footnote 48, modern PCDB records always lodge "V"; the fixed
durations are retained for legacy purposes.
Mitsubishi PUZ-WM50VHA (104568) lodges duration "V" at format-465
position 48 confirmed by inspecting the raw row in pcdb10.dat.
"""
# Arrange / Act
record = heat_pump_record(104568)
# Assert
assert record is not None
assert record.heating_duration_code == "V"
def test_heat_pump_record_returns_none_for_unknown_pcdb_id() -> None:
"""An index number not in Table 362 returns None so callers can fall
back to a Table 4a heat-pump category default."""