Model/tests/domain/modelling
Khalim Conn-Kowlessar 31c74ab500 feat(modelling): gas-boiler-upgrade-with-cylinder option in the heating rec
Add the first boiler-upgrade option to the single "Heating & Hot Water"
Recommendation (ADR-0024 expansion): a dwelling whose existing wet boiler heats
a hot-water cylinder is offered a new gas condensing boiler, with the cylinder
jacketed when under-insulated and given a thermostat when absent. One competing
Option (the Optimiser picks <=1), folded into one composite Plan line.

The end-state is read from the Elmhurst before/after re-lodgements (cert 001431,
gas boiler upgrade - with cylinder), which REVISE ADR-0024:

- Target is always a gas condensing boiler, not fuel-preserving: every after
  lodges fuel 26. Gas->gas always; a non-gas wet boiler ->gas only with a
  mains-gas connection; electric boilers are left alone (electrification is the
  upgrade path). Eligibility = wet-boiler SAP code (Table 4a/4b 101-141 /
  151-161 / 191-196) + not an electric boiler + mains gas present.
- End-state is a Table 4b SAP code, not a PCDB index: code 102 (regular boiler
  + cylinder). The calculator derives the condensing seasonal efficiency from
  the code, so no efficiency input exists or is needed.
- A modern condensing boiler has a fanned flue: the after flips
  `fan_flue_present` False->True on every cert (SAP 10.2 Table 4f flue-fan +
  the Table 4b condensing-efficiency basis). Added as a new HeatingOverlay
  field, routed to main_heating_details[0].
- Cylinder thermostat is always added when absent (user-locked); the jacket is
  the 80 mm `cylinder_insulation_type=2` end-state, applied only when the
  cylinder is below 80 mm (never downgrading a better one). Both are conditional
  per-dwelling components, not a frozen overlay.

Cascade-pinned delta-0 (SAP/CO2/PE) against the relodged after via
`_assert_overlay_reproduces_after`. NB the absolute SAP on this dwelling is
subject to a separate Summary-path mapper roof-fidelity gap (we read the roof
better-insulated than Elmhurst, scoring ~75 vs the printed 56); the gap is
identical on before+after (the boiler measure never touches the roof) so it
cancels and the pin still proves the exact heating field-delta. Tracked on the
calculator branch.

Wires the new `gas_boiler_upgrade` MeasureType through contingencies (0.26),
the offline sample catalogue, the catalogue-coverage list, and the ARA
first-run integration seed (the option fires on any mains-gas boiler+cylinder
dwelling).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-09 16:16:46 +00:00
..
fixtures feat(modelling): gas-boiler-upgrade-with-cylinder option in the heating rec 2026-06-09 16:16:46 +00:00
__init__.py feat(modelling): Overlay Applicator folds EpcSimulation onto EpcPropertyData 2026-06-02 22:13:51 +00:00
_elmhurst_recommendation.py test(modelling): Elmhurst before/after cascade pin for cavity wall (#1154) 2026-06-03 09:36:53 +00:00
test_ashp_cost_inputs.py feat(modelling): classify ASHP existing system by fuel code 2026-06-06 21:10:45 +00:00
test_considered_measures.py feat(modelling): considered_measures allowlist on the orchestrator 2026-06-08 20:32:11 +00:00
test_elmhurst_cascade_pins.py feat(modelling): gas-boiler-upgrade-with-cylinder option in the heating rec 2026-06-09 16:16:46 +00:00
test_floor_recommendation.py refactor(modelling): group domain/modelling into generators/scoring/optimisation 2026-06-03 13:48:36 +00:00
test_glazing_recommendation.py feat(modelling): detect single-glazing code 15 + glazing before/after pins 2026-06-05 11:23:57 +00:00
test_heating_recommendation.py feat(modelling): gas-boiler-upgrade-with-cylinder option in the heating rec 2026-06-09 16:16:46 +00:00
test_lighting_recommendation.py feat(modelling): recommend_lighting upgrades all non-LED bulbs to LED 2026-06-05 12:23:27 +00:00
test_measure_dependency.py refactor(modelling): ventilation_dependency delegates to the generator + wraps 2026-06-03 14:04:17 +00:00
test_measure_type.py feat(modelling): gas-boiler-upgrade-with-cylinder option in the heating rec 2026-06-09 16:16:46 +00:00
test_optimiser.py refactor(modelling): type measure_type fields as MeasureType 2026-06-08 20:13:31 +00:00
test_overlay_applicator.py feat(modelling): SolarOverlay + _fold_solar (sixth overlay surface) 2026-06-08 10:10:15 +00:00
test_package_scorer.py feat(modelling): Score carries the scored SapResult for billing 2026-06-03 17:20:45 +00:00
test_plan.py refactor(modelling): type measure_type fields as MeasureType 2026-06-08 20:13:31 +00:00
test_plan_valuation.py feat(modelling): wire Valuation Uplift onto the Plan 2026-06-04 08:59:04 +00:00
test_products.py refactor(modelling): load ASHP rates from a committed costs file 2026-06-06 23:19:53 +00:00
test_roof_recommendation.py feat(modelling): room-in-roof safety guard defers the roof generator 2026-06-05 10:06:08 +00:00
test_scoring.py refactor(modelling): type measure_type fields as MeasureType 2026-06-08 20:13:31 +00:00
test_solar_config_selection.py feat(modelling): conservative PV config selection (5-config spread) 2026-06-08 10:02:15 +00:00
test_solar_overshading.py feat(modelling): generation-calibrated PV overshading derivation 2026-06-08 09:59:48 +00:00
test_solar_potential.py feat(modelling): typed SolarPotential projection over Google buildingInsights 2026-06-08 09:55:55 +00:00
test_solar_products.py feat(modelling): Products.solar_bundle_cost + committed solar rate sheet 2026-06-08 12:10:27 +00:00
test_solar_recommendation.py feat(modelling): recommend_solar — eligibility + competing array Options 2026-06-08 12:14:24 +00:00
test_valuation.py feat(modelling): ValuationUplift domain class (percentage-primary) 2026-06-04 08:33:19 +00:00
test_ventilation_recommendation.py feat(modelling): ventilation Recommendation Generator (detect + price) 2026-06-03 14:01:14 +00:00
test_wall_recommendation.py feat(modelling): explicit park-home guard in the solid-wall generator 2026-06-05 10:49:50 +00:00
test_window_extraction_001431.py fix(extractor): capture all 17 openable §11 windows on cert 001431 2026-06-05 14:33:25 +00:00