Model/domain/modelling
Jun-te Kim 5b4a1b4966 Classify PCDB-index gas boilers with no SAP code for heating recs
Gov-API certs that lodge a gas boiler as main_heating_category=2 + a PCDB
main_heating_index_number but omit sap_main_heating_code were carried with
a null code, so the heating generators (which gated on sap_main_heating_code
alone) never evaluated them for a boiler upgrade or a system tune-up — they
silently got nothing (£0 works, plateau at D). The SAP calculator already
handles this shape (cert_to_inputs._is_wet_boiler_main), so the calculator
modelled these as wet boilers while the recommenders ignored them.

Mirror the calculator's classification in the generator:
- _is_wet_boiler / _is_electric_boiler fall back, when no SAP code is
  lodged, to a PCDB Table 105 boiler index or main_heating_category in
  {1,2} (heat pumps excluded); electric boilers stay excluded by the code
  range when lodged, else by an electricity main fuel.
- _already_condensing reads the boiler's declared PCDB winter efficiency
  when there is no Table 4b code, so a modern condensing boiler lodged this
  way still isn't offered a pointless like-for-like swap (gets a tune-up).

Verified read-only on portfolio 814 / scenario 1271: pids 742304, 742306,
742322, 742325, 742336 now get a gas_boiler_upgrade (two cross D->C on the
boiler alone) where they had £0 works; predicted properties whose heating
donor is a PCDB-shape boiler (e.g. 742095/742102/742105) also recover a
tune-up. Fixes the A#2 slice of #1388 (and unblocks 742302/742367).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 14:52:47 +00:00
..
generators Classify PCDB-index gas boilers with no SAP code for heating recs 2026-07-01 14:52:47 +00:00
optimisation refactor(modelling): type measure_type fields as MeasureType 2026-06-08 20:13:31 +00:00
scoring Rename SapEnergySource.mains_gas to gas_connection_available 🟩 2026-07-01 10:59:21 +00:00
__init__.py feat(modelling): Overlay Applicator folds EpcSimulation onto EpcPropertyData 2026-06-02 22:13:51 +00:00
ashp_rates.json refactor(modelling): load ASHP rates from a committed costs file 2026-06-06 23:19:53 +00:00
considered_measures.py feat(modelling): drive measure scoping from the Scenario's exclusions 2026-06-16 15:26:25 +00:00
contingencies.py feat(modelling): cost data for secondary-heating-removal (ADR-0028) 2026-06-11 13:51:16 +00:00
heating_rates.json feat(modelling): composite per-dwelling boiler + tune-up costing (ADR-0027) 2026-06-10 19:41:06 +00:00
measure_type.py feat(modelling): secondary-heating-removal generator + MeasureType (ADR-0028) 2026-06-11 13:35:14 +00:00
plan.py refactor(modelling): type measure_type fields as MeasureType 2026-06-08 20:13:31 +00:00
portfolio_goal.py refactor(modelling): move PortfolioGoal to domain/modelling/ 2026-06-03 22:44:48 +00:00
product.py feat(modelling): persist recommendation.material_id from the catalogue 2026-06-04 08:26:58 +00:00
products.py feat(modelling): composite per-dwelling boiler + tune-up costing (ADR-0027) 2026-06-10 19:41:06 +00:00
recommendation.py refactor(modelling): type measure_type fields as MeasureType 2026-06-08 20:13:31 +00:00
scenario.py feat(modelling): drive measure scoping from the Scenario's exclusions 2026-06-16 15:26:25 +00:00
simulation.py Rename SapEnergySource.mains_gas to gas_connection_available 🟩 2026-07-01 10:59:21 +00:00
solar_potential.py Harden Dwelling-Roof Cap on real data: positional segments, ground-floor basis 🟩 2026-06-26 12:24:52 +00:00
solar_rates.json feat(modelling): Products.solar_bundle_cost + committed solar rate sheet 2026-06-08 12:10:27 +00:00
valuation.py feat(modelling): ValuationUplift domain class (percentage-primary) 2026-06-04 08:33:19 +00:00