Model/tests/domain/sap10_calculator/worksheet
Khalim Conn-Kowlessar 9694650abe fix(water-heating): derive combi keep-hot from the PCDB record, default no-keep-hot
SAP 10.2 Table 3a (PDF p.160) additional combi loss (61)m. Two coupled
defects, both surfaced by simulated case 49 (000565 + gas combi, U985
"Combi keep hot type = None") sitting at SAP 71.43 vs the worksheet's 72:

1. The cascade defaulted EVERY non-PCDB combi to the flat keep-hot
   time-clock row (600 × n/365). A combi WITHOUT a keep-hot facility uses
   row 1 (600 × fu × n/365, fu = V_d/100 when daily HW < 100 L/day) —
   over-counting (61)m for the no-keep-hot cohort. `water_heating_from_
   cert` now defaults to the "without keep-hot" row.

2. `pcdb_combi_loss_override` returned None for keep_hot_facility=1/
   timer=1, leaning on the OLD flat-600 default. So flipping the default
   silently turned 190 corpus PCDB keep-hot-time-clock combis into
   no-keep-hot. Fixed to return the flat keep-hot row EXPLICITLY.

Key insight (the Summary is the input echo; the U985 keep-hot line is a
computed OUTPUT, so it must be derivable): keep-hot rides on the PCDB
boiler record (Table 105 keep_hot_facility/timer), resolved by
`pcdb_combi_loss_override`. A generic SAP-code combi with no PCDB record
(case 49, PCDF ref 0) has no keep-hot by construction → row 1. So the
default is not a guess — it is the spec-correct value for non-PCDB combis.

Worksheet-proven: case 49 → cost £726.696, SAP 72 — matching the
accredited worksheet to the digit (continuous 71.6945 = the worksheet's
own 71.6945). 000516 (keep-hot None) also exact (£860.716, SAP 63);
000490 (PCDB 10328, keep_hot_facility=1/timer=1) keeps its flat-600 via
the PCDB path. Masked until now because every prior combi-loss worksheet
fixture was keep-hot (000490/000474/000480 time-clock) or had V_d >= 100
every month (001431, rows coincide); case 49 is the first no-keep-hot one.

Corpus within-0.5 72.7% -> 73.3%, MAE 0.781 -> 0.774, PE 3.5 -> 3.4;
ratcheted _MAX_SAP_MAE 0.785 -> 0.775, _MAX_PE_PER_M2_MAE 3.6 -> 3.5.

Note: pyright strict type gate not run locally (pyright not installed).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-23 21:22:02 +00:00
..
fixtures Move sap10_calculator tests to tests/domain/sap10_calculator/ for CI 2026-06-02 16:58:00 +00:00
__init__.py Move sap10_calculator tests to tests/domain/sap10_calculator/ for CI 2026-06-02 16:58:00 +00:00
_elmhurst_fixtures.py Move sap10_calculator tests to tests/domain/sap10_calculator/ for CI 2026-06-02 16:58:00 +00:00
_elmhurst_worksheet_000474.py Merge remote-tracking branch 'origin/main' into feature/bill-derivation 2026-06-05 11:09:00 +00:00
_elmhurst_worksheet_000477.py Merge remote-tracking branch 'origin/main' into feature/bill-derivation 2026-06-05 11:09:00 +00:00
_elmhurst_worksheet_000480.py Merge remote-tracking branch 'origin/main' into feature/bill-derivation 2026-06-05 11:09:00 +00:00
_elmhurst_worksheet_000487.py Merge remote-tracking branch 'origin/main' into feature/bill-derivation 2026-06-05 11:09:00 +00:00
_elmhurst_worksheet_000490.py fix(water-heating): derive combi keep-hot from the PCDB record, default no-keep-hot 2026-06-23 21:22:02 +00:00
_elmhurst_worksheet_000516.py Merge remote-tracking branch 'origin/main' into feature/bill-derivation 2026-06-05 11:09:00 +00:00
_elmhurst_worksheet_000565.py Move sap10_calculator tests to tests/domain/sap10_calculator/ for CI 2026-06-02 16:58:00 +00:00
_elmhurst_worksheet_001431.py S0380.191: pin simulated 001431 gas-combi end-to-end at 1e-4 (e2e harness) 2026-06-02 22:44:32 +00:00
_elmhurst_worksheet_001431_6035.py S0380.195: pin sim case 4 (6035 floor geometry) e2e at 1e-4 — 6035 +19 PE is lodged divergence 2026-06-03 09:56:39 +00:00
_elmhurst_worksheet_001431_case5.py S0380.197: simulated case 5 e2e fixture — detached sandstone RR validates S0380.196 (RdSAP 10 §3.9.1 + Table 4 p.22) 2026-06-03 11:41:16 +00:00
_elmhurst_worksheet_001431_case6.py S0380.207: promote simulated case 6 to a full SapResult e2e fixture 2026-06-03 16:14:48 +00:00
_elmhurst_worksheet_001431_case7.py S0380.208: case 7 combi e2e fixture — condensing-oil-combi path validated exact 2026-06-03 17:57:22 +00:00
_elmhurst_worksheet_001431_case20.py feat(test): case-20 cascade fixture + close its CO2 via E7 per-end-use codes 2026-06-06 11:23:10 +00:00
_elmhurst_worksheet_001431_case21.py fix(u-value): solid brick as-built U by thickness — §5.7 Table 13 2026-06-06 14:40:06 +00:00
_elmhurst_worksheet_001431_case38.py test(worksheet): pin simulated case 38 — mains-gas secondary reproduces worksheet exactly 2026-06-15 13:31:36 +00:00
_elmhurst_worksheet_001431_case39.py fix(mapper): map Elmhurst "Value known" cylinder to measured volume (code 6) 2026-06-15 23:57:25 +00:00
_elmhurst_worksheet_001431_case43.py test(worksheet): pin simulated case 43 at 1e-4 (RR + dry-line + mixed roof) 2026-06-16 08:26:05 +00:00
_elmhurst_worksheet_001431_case44.py feat(conservatory): §6.1 solar gains + TFA-occupancy (demand-side) 2026-06-16 23:21:08 +00:00
_elmhurst_worksheet_001431_case45.py fix(climate): compute EPC CO2/PE on the postcode demand cascade (SAP 10.2 Appendix U p.124) 2026-06-18 14:15:34 +00:00
_elmhurst_worksheet_001431_rr.py S0380.193: suspended-floor (12) sealed rule fires only on a SUPPLIED U-value 2026-06-03 09:16:25 +00:00
_elmhurst_worksheet_001431_rr8.py S0380.194: pin sim case 3 (near-exact 6035 replica) e2e at 1e-4 2026-06-03 09:46:56 +00:00
_xlsx_loader.py Move sap10_calculator tests to tests/domain/sap10_calculator/ for CI 2026-06-02 16:58:00 +00:00
test_appendix_h_solar.py Move sap10_calculator tests to tests/domain/sap10_calculator/ for CI 2026-06-02 16:58:00 +00:00
test_dimensions.py Move sap10_calculator tests to tests/domain/sap10_calculator/ for CI 2026-06-02 16:58:00 +00:00
test_e2e_elmhurst_sap_score.py fix(climate): compute EPC CO2/PE on the postcode demand cascade (SAP 10.2 Appendix U p.124) 2026-06-18 14:15:34 +00:00
test_energy_requirements.py S0380.200: SAP 10.2 §9a two-main-heating split (203)/(205)/(207)/(213) 2026-06-03 13:09:43 +00:00
test_fabric_energy_efficiency.py Move sap10_calculator tests to tests/domain/sap10_calculator/ for CI 2026-06-02 16:58:00 +00:00
test_fuel_cost.py Move sap10_calculator tests to tests/domain/sap10_calculator/ for CI 2026-06-02 16:58:00 +00:00
test_heat_transmission.py Merge remote-tracking branch 'origin/main' into feature/per-cert-mapper-validation 2026-06-18 15:00:32 +00:00
test_internal_gains.py Move sap10_calculator tests to tests/domain/sap10_calculator/ for CI 2026-06-02 16:58:00 +00:00
test_mean_internal_temperature.py Move sap10_calculator tests to tests/domain/sap10_calculator/ for CI 2026-06-02 16:58:00 +00:00
test_mev.py Move sap10_calculator tests to tests/domain/sap10_calculator/ for CI 2026-06-02 16:58:00 +00:00
test_photovoltaic.py Move sap10_calculator tests to tests/domain/sap10_calculator/ for CI 2026-06-02 16:58:00 +00:00
test_rating.py Move sap10_calculator tests to tests/domain/sap10_calculator/ for CI 2026-06-02 16:58:00 +00:00
test_section_cascade_pins.py fix(climate): compute EPC CO2/PE on the postcode demand cascade (SAP 10.2 Appendix U p.124) 2026-06-18 14:15:34 +00:00
test_solar_gains.py Move sap10_calculator tests to tests/domain/sap10_calculator/ for CI 2026-06-02 16:58:00 +00:00
test_space_cooling.py Move sap10_calculator tests to tests/domain/sap10_calculator/ for CI 2026-06-02 16:58:00 +00:00
test_space_heating.py Move sap10_calculator tests to tests/domain/sap10_calculator/ for CI 2026-06-02 16:58:00 +00:00
test_utilisation_factor.py Move sap10_calculator tests to tests/domain/sap10_calculator/ for CI 2026-06-02 16:58:00 +00:00
test_ventilation.py Move sap10_calculator tests to tests/domain/sap10_calculator/ for CI 2026-06-02 16:58:00 +00:00
test_water_heating.py fix(water-heating): derive combi keep-hot from the PCDB record, default no-keep-hot 2026-06-23 21:22:02 +00:00