mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-06-08 11:17:27 +00:00
Slice 102f-prep.7: Table N4 fixed durations ("24"/"16") in HP extended-heating helper
SAP 10.2 Appendix N3.5 Table N4 (PDF p.107) — heat-pump packages
with fixed daily heating durations:
- "24" → N24,9 = 365 (continuous): every day at heating temperature,
no off period → (days_in_month, 0) per month → MIT_zone = Th.
- "16" → N16,9 = 365 (unimodal, 0700-2300): every day with single
8h off → (0, days_in_month) per month → MIT_zone = Th − u1(8h).
- "9" → standard SAP schedule (bimodal 7+8 off): falls through to
`None` so the orchestrator applies the legacy bimodal path.
Cert 9418 (Daikin Altherma EDLQ05CAV3, PCDB 102421) lodges
`heating_duration_code = "24"` — worksheet (87) MIT_living = 21.0
every month (= Th1, no off period) and (90) MIT_elsewhere collapses
to Th2 directly. Pre-fix the bimodal cascade produced MIT ~17.8-19.8
(2.04°C low at Jan) and SAP was +2.20 over worksheet 84.6305.
Post-fix cert 9418 closes to SAP Δ +0.0296 (from +2.20) — the
residual is consistent with the same ~0.05 PSR-formula drift seen
in 5/7 cohort certs sharing PCDB 104568.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
143d11d39f
commit
4eacfa6296
2 changed files with 68 additions and 18 deletions
|
|
@ -678,6 +678,43 @@ def test_api_0380_heat_pump_no_pumps_fans_kwh_per_table_4f() -> None:
|
|||
assert result.pumps_fans_kwh_per_yr == 0.0
|
||||
|
||||
|
||||
_API_9418_JSON = (
|
||||
Path(__file__).parents[3]
|
||||
/ "domain/sap10_calculator/rdsap/tests/fixtures/golden"
|
||||
/ "9418-3062-8205-3566-7200.json"
|
||||
)
|
||||
|
||||
|
||||
def test_api_9418_daikin_24h_duration_mean_internal_temp_matches_worksheet_92() -> None:
|
||||
# Arrange — cert 9418 (Daikin Altherma EDLQ05CAV3, PCDB 102421)
|
||||
# lodges `heating_duration_code = "24"`. Per SAP 10.2 Table N4 (PDF
|
||||
# p.107) this means N24,9 = 365 (all days operate at 24-hour
|
||||
# heating, no off-period). Worksheet (87) MIT_living = 21.0 every
|
||||
# month (= Th1, no off period), worksheet (90) MIT_elsewhere
|
||||
# collapses to Th2 directly. Worksheet (92) blended at fLA = 0.30.
|
||||
#
|
||||
# Pre-slice-102f-prep.7 the helper's "V"-only gate returned None
|
||||
# for this duration → bimodal cascade gave MIT ~17.8-19.8 (off by
|
||||
# ~2°C). After Table N4 wiring the cascade lands at 1e-3.
|
||||
doc = json.loads(_API_9418_JSON.read_text())
|
||||
epc = EpcPropertyDataMapper.from_api_response(doc)
|
||||
|
||||
# Act
|
||||
inputs = cert_to_inputs(epc, prices=SAP_10_2_SPEC_PRICES)
|
||||
|
||||
# Assert — worksheet (92) "MIT" 12-tuple at 1e-3 per month.
|
||||
worksheet_mit_92 = (
|
||||
19.8400, 19.8445, 19.8489, 19.8697, 19.8736, 19.8920,
|
||||
19.8920, 19.8954, 19.8849, 19.8736, 19.8657, 19.8574,
|
||||
)
|
||||
for m, (cascade, ws) in enumerate(zip(
|
||||
inputs.mean_internal_temp_monthly_c, worksheet_mit_92
|
||||
)):
|
||||
assert abs(cascade - ws) < 1e-3, (
|
||||
f"month {m + 1}: cascade={cascade:.4f} vs worksheet={ws:.4f}"
|
||||
)
|
||||
|
||||
|
||||
def test_api_0380_mean_internal_temp_matches_worksheet_92_within_1e_3() -> None:
|
||||
# Arrange — SAP 10.2 Appendix N3.5 (PDF p.107) replaces Table 9c
|
||||
# steps 3-4 for heat-pump packages with PCDB data: each month
|
||||
|
|
|
|||
|
|
@ -2041,28 +2041,41 @@ def _heat_pump_extended_heating_days_per_month(
|
|||
`mean_internal_temperature_monthly` orchestrator falls through to
|
||||
the standard SAP heating schedule (bimodal 9-hour day).
|
||||
|
||||
Only the Variable heating-duration case is handled here — per
|
||||
footnote 48 (PDF p.105) "Daily heating durations of 24, 16 and 9
|
||||
hours are retained for legacy purposes" and modern PCDB Table 362
|
||||
records always lodge "V". The fixed "24" / "16" branches would
|
||||
need a different allocation path (Table N4: 365 days at one mode)
|
||||
that the cohort never exercises; deferred until a cert demands it.
|
||||
Per `heating_duration_code` from the PCDB record (SAP 10.2 PDF
|
||||
p.105 line 6099):
|
||||
- "V" (Variable, modern default per footnote 48): Table N5 PSR
|
||||
interpolation + cold-first allocation via
|
||||
`allocate_extended_heating_days_to_months`.
|
||||
- "24": Table N4 N24,9 = 365 — every day operates at 24-hour
|
||||
heating (no off period), so each month's tuple is
|
||||
(days_in_month, 0).
|
||||
- "16": Table N4 N16,9 = 365 — every day unimodal (one 8h off),
|
||||
each month's tuple is (0, days_in_month).
|
||||
- "9" or other: standard 9-hour schedule = no extended heating →
|
||||
return None so the orchestrator's bimodal fallback applies.
|
||||
"""
|
||||
if main is None or main.main_heating_category != 4:
|
||||
return None
|
||||
if hp_record is None or hp_record.heating_duration_code != "V":
|
||||
if hp_record is None:
|
||||
return None
|
||||
if hp_record.max_output_kw is None or hp_record.max_output_kw <= 0:
|
||||
return None
|
||||
if hlc_annual_avg_w_per_k <= 0:
|
||||
return None
|
||||
psr = (hp_record.max_output_kw * 1000.0) / (
|
||||
hlc_annual_avg_w_per_k * _SAP_DESIGN_HEAT_LOSS_DELTA_T_K
|
||||
)
|
||||
n24, n16 = extended_heating_days_from_psr_variable(psr=psr)
|
||||
return allocate_extended_heating_days_to_months(
|
||||
n24_9_year=n24, n16_9_year=n16,
|
||||
)
|
||||
code = hp_record.heating_duration_code
|
||||
if code == "V":
|
||||
if hp_record.max_output_kw is None or hp_record.max_output_kw <= 0:
|
||||
return None
|
||||
if hlc_annual_avg_w_per_k <= 0:
|
||||
return None
|
||||
psr = (hp_record.max_output_kw * 1000.0) / (
|
||||
hlc_annual_avg_w_per_k * _SAP_DESIGN_HEAT_LOSS_DELTA_T_K
|
||||
)
|
||||
n24, n16 = extended_heating_days_from_psr_variable(psr=psr)
|
||||
return allocate_extended_heating_days_to_months(
|
||||
n24_9_year=n24, n16_9_year=n16,
|
||||
)
|
||||
if code == "24":
|
||||
return tuple((d, 0) for d in _DAYS_IN_MONTH)
|
||||
if code == "16":
|
||||
return tuple((0, d) for d in _DAYS_IN_MONTH)
|
||||
return None
|
||||
|
||||
|
||||
def _primary_loss_applies(
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue