diff --git a/packages/domain/src/domain/ml/tests/_fixtures.py b/packages/domain/src/domain/ml/tests/_fixtures.py index df64c43d..0d139839 100644 --- a/packages/domain/src/domain/ml/tests/_fixtures.py +++ b/packages/domain/src/domain/ml/tests/_fixtures.py @@ -222,6 +222,10 @@ def make_minimal_sap10_epc( pv_battery_count: int = 0, pv_battery_capacity_per_unit_kwh: Optional[float] = None, wind_turbines_count: int = 0, + mechanical_ventilation: Optional[int] = None, + mechanical_vent_duct_type: Optional[int] = None, + blocked_chimneys_count: Optional[int] = None, + pressure_test: Optional[int] = None, ) -> EpcPropertyData: """Construct a minimal valid SAP10 EpcPropertyData with parametrisable targets.""" return EpcPropertyData( @@ -304,6 +308,10 @@ def make_minimal_sap10_epc( built_form=built_form, region_code=region_code, country_code=country_code, + mechanical_ventilation=mechanical_ventilation, + mechanical_vent_duct_type=mechanical_vent_duct_type, + blocked_chimneys_count=blocked_chimneys_count, + pressure_test=pressure_test, renewable_heat_incentive=RenewableHeatIncentive( space_heating_kwh=space_heating_kwh, water_heating_kwh=water_heating_kwh, diff --git a/packages/domain/src/domain/ml/tests/test_transform.py b/packages/domain/src/domain/ml/tests/test_transform.py index 3853a051..66116fbe 100644 --- a/packages/domain/src/domain/ml/tests/test_transform.py +++ b/packages/domain/src/domain/ml/tests/test_transform.py @@ -1040,6 +1040,53 @@ def test_to_row_extracts_energy_source_booleans() -> None: assert row["is_dwelling_export_capable"] is True +_VENTILATION_FEATURES_NULLABLE: dict[str, tuple[type, bool, bool]] = { + "mechanical_ventilation": (int, True, True), + "mechanical_vent_duct_type": (int, True, True), + "blocked_chimneys_count": (int, True, False), + "pressure_test": (int, True, False), +} + + +def test_schema_advertises_ventilation_features() -> None: + # Arrange + transform = EpcMlTransform() + + # Act + schema = transform.schema() + + # Assert + for name, (expected_dtype, expected_nullable, expected_categorical) in ( + _VENTILATION_FEATURES_NULLABLE.items() + ): + assert name in schema.feature_columns, name + column = schema.feature_columns[name] + assert column.dtype is expected_dtype, name + assert column.nullable is expected_nullable, name + assert column.categorical is expected_categorical, name + + +def test_to_row_extracts_ventilation_features() -> None: + # Arrange — MVHR (mechanical_ventilation code 4), duct type 3 + epc = make_minimal_sap10_epc( + energy_rating_current=82, + mechanical_ventilation=4, + mechanical_vent_duct_type=3, + blocked_chimneys_count=1, + pressure_test=4, + ) + transform = EpcMlTransform() + + # Act + row = transform.to_row(epc) + + # Assert + assert row["mechanical_ventilation"] == 4 + assert row["mechanical_vent_duct_type"] == 3 + assert row["blocked_chimneys_count"] == 1 + assert row["pressure_test"] == 4 + + def test_to_row_area_weights_window_u_value_and_solar_transmittance() -> None: # Arrange — two windows with transmission details; one without. sap_windows = [ diff --git a/packages/domain/src/domain/ml/transform.py b/packages/domain/src/domain/ml/transform.py index eeef45d4..9e1fc662 100644 --- a/packages/domain/src/domain/ml/transform.py +++ b/packages/domain/src/domain/ml/transform.py @@ -417,6 +417,23 @@ _FEATURE_COLUMNS: dict[str, ColumnSpec] = { dtype=bool, nullable=False, description="Dwelling has an export-capable connection (eligible for SEG).", ), + # Ventilation — flat fields direct off EpcPropertyData + "mechanical_ventilation": ColumnSpec( + dtype=int, nullable=True, categorical=True, + description="Mechanical ventilation SAP10 code (0=natural, 1-6 per epc_codes.csv enum).", + ), + "mechanical_vent_duct_type": ColumnSpec( + dtype=int, nullable=True, categorical=True, + description="Mechanical ventilation duct type SAP10 code.", + ), + "blocked_chimneys_count": ColumnSpec( + dtype=int, nullable=True, + description="Number of blocked / capped-off chimneys.", + ), + "pressure_test": ColumnSpec( + dtype=int, nullable=True, + description="Air-tightness pressure-test SAP10 code.", + ), } @@ -536,6 +553,11 @@ class EpcMlTransform: **pv_aggregates, # Features — battery, wind turbine, mains gas + smart meter flags **energy_source_other, + # Features — ventilation + "mechanical_ventilation": epc.mechanical_ventilation, + "mechanical_vent_duct_type": epc.mechanical_vent_duct_type, + "blocked_chimneys_count": epc.blocked_chimneys_count, + "pressure_test": epc.pressure_test, # Targets "sap_score": epc.energy_rating_current, "co2_emissions": epc.co2_emissions_current,