feat(modelling): wire solid-wall insulation into the candidate pool

Slice 2e. recommend_solid_wall joins the orchestrator's fabric generator pool
(restrictions default unrestricted until slice 3 sources them); the harness
catalogue + contingencies (26%) gain external_wall_insulation /
internal_wall_insulation. run_modelling on an uninsulated solid-brick dwelling
(baseline SAP 36.6) now selects internal wall insulation into the optimised
package; the catalogue-completeness guard covers both new measure types.
Golden cohort 57/57 still error-free; IWI now fires on a real cohort cert.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Khalim Conn-Kowlessar 2026-06-04 16:15:56 +00:00
parent 0cef044503
commit 7648032d73
4 changed files with 29 additions and 2 deletions

View file

@ -11,6 +11,8 @@ _CONTINGENCY_RATES: dict[str, float] = {
"suspended_floor_insulation": 0.20,
"solid_floor_insulation": 0.26,
"mechanical_ventilation": 0.26,
"external_wall_insulation": 0.26,
"internal_wall_insulation": 0.26,
}

View file

@ -3,5 +3,7 @@
"loft_insulation": { "unit_cost_per_m2": 12.0 },
"suspended_floor_insulation": { "unit_cost_per_m2": 25.0 },
"solid_floor_insulation": { "unit_cost_per_m2": 45.0 },
"mechanical_ventilation": { "unit_cost_per_m2": 450.0 }
"mechanical_ventilation": { "unit_cost_per_m2": 450.0 },
"external_wall_insulation": { "unit_cost_per_m2": 100.0 },
"internal_wall_insulation": { "unit_cost_per_m2": 90.0 }
}

View file

@ -27,6 +27,7 @@ from domain.modelling.scoring.scoring import (
marginals_from_scores,
)
from domain.modelling.generators.wall_recommendation import recommend_cavity_wall
from domain.modelling.generators.solid_wall_recommendation import recommend_solid_wall
from domain.sap10_calculator.calculator import SapCalculator
from repositories.fuel_rates.fuel_rates_repository import FuelRatesRepository
from repositories.product.product_repository import ProductRepository
@ -194,6 +195,7 @@ def _candidate_recommendations(
"""Run every fabric Recommendation Generator; keep the ones that apply."""
generators = (
recommend_cavity_wall,
recommend_solid_wall,
recommend_loft_insulation,
recommend_floor_insulation,
)

View file

@ -10,14 +10,19 @@ from datatypes.epc.domain.epc import Epc
from datatypes.epc.domain.epc_property_data import EpcPropertyData
from harness.console import DEFAULT_CATALOGUE, run_modelling, run_one
from repositories.product.product_json_repository import ProductJsonRepository
from tests.domain.modelling._elmhurst_recommendation import (
parse_recommendation_summary,
)
from tests.domain.sap10_calculator.worksheet._elmhurst_worksheet_000490 import (
build_epc as _build_uninsulated_cavity_and_floor_epc,
)
# Every Measure Type the four fabric generators can emit; the harness catalogue
# Every Measure Type the fabric generators can emit; the harness catalogue
# must price all of them or an offline run raises mid-pipeline.
_GENERATOR_MEASURE_TYPES = (
"cavity_wall_insulation",
"external_wall_insulation",
"internal_wall_insulation",
"loft_insulation",
"suspended_floor_insulation",
"solid_floor_insulation",
@ -65,6 +70,22 @@ def test_run_modelling_inspects_a_plan_without_baseline_or_lodged_performance()
assert len(plan.measures) >= 1
def test_run_modelling_recommends_solid_wall_insulation_for_solid_brick() -> None:
# Arrange — an uninsulated solid-brick dwelling (cert 001431 before),
# which has no cavity to fill, so any wall measure must be solid-wall.
epc: EpcPropertyData = parse_recommendation_summary(
"solid_brick_ewi_001431_before.pdf"
)
# Act — Modelling only, no database.
plan = run_modelling(epc, goal_band="C", print_table=False)
# Assert — the solid-wall generator is wired into the candidate pool, so a
# solid-wall Option reaches the optimised package.
measure_types = {measure.measure_type for measure in plan.measures}
assert measure_types & {"external_wall_insulation", "internal_wall_insulation"}
def test_sample_catalogue_prices_every_generator_measure_type() -> None:
# Arrange — the default offline catalogue.
products: ProductJsonRepository = ProductJsonRepository(DEFAULT_CATALOGUE)