slice 12: ventilation flat features

Four ventilation features: mechanical_ventilation (categorical
SAP10 code, 0=natural through 6=positive-input-from-outside per
epc_codes.csv mechanical_ventilation enum), mechanical_vent_duct_type
(categorical), blocked_chimneys_count (int), and pressure_test
(int — air-tightness SAP10 code).

Pulled from top-level EpcPropertyData fields; ventilation on SAP10
API EPCs sits on the certificate directly, not on the
sap_ventilation block (which is site-notes-only).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Khalim Conn-Kowlessar 2026-05-16 16:09:53 +00:00
parent 559a2128b9
commit ebceb4bf2b
3 changed files with 77 additions and 0 deletions

View file

@ -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,

View file

@ -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 = [

View file

@ -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,