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 `max_output_kw` (spec §A.23 field 30) is the PSR-denominator per
PDF p.100 line 5946 ("maximum nominal output of the package"). 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 `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) 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 (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_loss_kwh_per_day: Optional[float]
vessel_heat_exchanger_area_m2: Optional[float] vessel_heat_exchanger_area_m2: Optional[float]
max_output_kw: Optional[float] max_output_kw: Optional[float]
heating_duration_code: Optional[str]
psr_groups: tuple[PsrEfficiencyGroup, ...] psr_groups: tuple[PsrEfficiencyGroup, ...]
raw: tuple[str, ...] 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_LOSS_KWH_PER_DAY: Final[int] = 25
_HP_IDX_VESSEL_HEAT_EXCHANGER_AREA_M2: Final[int] = 26 _HP_IDX_VESSEL_HEAT_EXCHANGER_AREA_M2: Final[int] = 26
_HP_IDX_MAX_OUTPUT_KW: Final[int] = 47 _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 # 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 # 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: def at(idx: int) -> str:
return raw[idx] if idx < len(raw) else "" return raw[idx] if idx < len(raw) else ""
duration_raw = at(_HP_IDX_HEATING_DURATION_CODE).strip()
return HeatPumpRecord( return HeatPumpRecord(
pcdb_id=int(raw[0]), pcdb_id=int(raw[0]),
brand_name=at(_HP_IDX_BRAND_NAME), 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) at(_HP_IDX_VESSEL_HEAT_EXCHANGER_AREA_M2)
), ),
max_output_kw=_parse_optional_float(at(_HP_IDX_MAX_OUTPUT_KW)), 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), psr_groups=_parse_psr_groups(raw),
raw=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 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: 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 """An index number not in Table 362 returns None so callers can fall
back to a Table 4a heat-pump category default.""" back to a Table 4a heat-pump category default."""