"""Tests for the predicted_total_cost / predicted_ecf / predicted_log10_ecf features (slice 16e, ADR-0008).""" from math import log10 import pytest from domain.sap10_ml.ecf import ( predicted_ecf, predicted_log10_ecf, predicted_pv_generation_kwh, predicted_total_fuel_cost_gbp, ) def test_predicted_pv_generation_kwh_scales_linearly_with_peak_power() -> None: # Arrange — UK average yield ~ 850 kWh/kWp/yr; 4 kWp -> ~3400 kWh. # Act a = predicted_pv_generation_kwh(pv_total_peak_power_kw=4.0, region_code="1") b = predicted_pv_generation_kwh(pv_total_peak_power_kw=8.0, region_code="1") # Assert assert b == pytest.approx(2.0 * a, abs=0.01) def test_predicted_pv_generation_kwh_returns_zero_for_no_pv() -> None: # Arrange / Act / Assert assert predicted_pv_generation_kwh(pv_total_peak_power_kw=0.0, region_code="1") == 0.0 assert predicted_pv_generation_kwh(pv_total_peak_power_kw=None, region_code="1") == 0.0 def test_predicted_total_fuel_cost_subtracts_pv_credit_at_electricity_price() -> None: # Arrange — gas heat + DHW + lighting, with 3000 kWh PV generation. # Base cost: (10000*3.48 + 2500*3.48 + 600*13.19) / 100 = 514.14 # PV credit: 3000 * 13.19 / 100 = 395.70 # Net: 514.14 - 395.70 = 118.44 # Act with_pv = predicted_total_fuel_cost_gbp( predicted_space_heating_kwh=10000.0, predicted_hot_water_kwh=2500.0, predicted_lighting_kwh=600.0, main_fuel_code=1, water_heating_fuel_code=1, predicted_pv_kwh=3000.0, ) no_pv = predicted_total_fuel_cost_gbp( predicted_space_heating_kwh=10000.0, predicted_hot_water_kwh=2500.0, predicted_lighting_kwh=600.0, main_fuel_code=1, water_heating_fuel_code=1, predicted_pv_kwh=0.0, ) # Assert assert no_pv == pytest.approx(514.14, abs=0.05) assert with_pv == pytest.approx(118.44, abs=0.05) def test_predicted_total_fuel_cost_pv_kwh_defaults_to_zero_for_backwards_compatibility() -> None: # Arrange / Act — existing callers pre-17a omit predicted_pv_kwh entirely. # Act result = predicted_total_fuel_cost_gbp( predicted_space_heating_kwh=10000.0, predicted_hot_water_kwh=2500.0, predicted_lighting_kwh=600.0, main_fuel_code=1, water_heating_fuel_code=1, ) # Assert — same as predicted_pv_kwh=0. assert result == pytest.approx(514.14, abs=0.05) def test_predicted_pv_generation_kwh_scotland_lower_than_southern_england() -> None: # Arrange — Thames (region 1) vs Highland (region 17); same kWp. # Act thames = predicted_pv_generation_kwh(pv_total_peak_power_kw=4.0, region_code="1") highland = predicted_pv_generation_kwh(pv_total_peak_power_kw=4.0, region_code="17") # Assert assert highland < thames def test_predicted_total_fuel_cost_gas_heated_returns_expected_gbp() -> None: # Arrange — 12,000 kWh gas heat, 3,000 kWh gas DHW, 800 kWh lighting. # Gas (code 1) 3.48 p/kWh, electricity (30) 13.19 p/kWh. # Expected total: (12000*3.48 + 3000*3.48 + 800*13.19) / 100 = (41760 + 10440 + 10552) / 100 = 627.52 # Act result = predicted_total_fuel_cost_gbp( predicted_space_heating_kwh=12000.0, predicted_hot_water_kwh=3000.0, predicted_lighting_kwh=800.0, main_fuel_code=1, water_heating_fuel_code=1, ) # Assert assert result == pytest.approx(627.52, abs=0.05) def test_predicted_total_fuel_cost_electric_heated_higher_than_gas() -> None: # Arrange — same kWh demand on electricity vs gas. # Act gas = predicted_total_fuel_cost_gbp( predicted_space_heating_kwh=10000.0, predicted_hot_water_kwh=2500.0, predicted_lighting_kwh=600.0, main_fuel_code=1, water_heating_fuel_code=1, ) elec = predicted_total_fuel_cost_gbp( predicted_space_heating_kwh=10000.0, predicted_hot_water_kwh=2500.0, predicted_lighting_kwh=600.0, main_fuel_code=30, water_heating_fuel_code=30, ) # Assert assert elec > gas * 2.0 def test_predicted_ecf_uses_sap_deflator_and_tfa_plus_45() -> None: # Arrange — total cost 627.52, TFA 100. # ECF = 0.42 * 627.52 / (100 + 45) = 263.56 / 145 = 1.817 # Act result = predicted_ecf(predicted_total_cost_gbp=627.52, total_floor_area_m2=100.0) # Assert assert result == pytest.approx(1.817, abs=0.005) def test_predicted_ecf_returns_zero_for_unspecified_floor_area() -> None: # Arrange / Act / Assert assert predicted_ecf(predicted_total_cost_gbp=627.52, total_floor_area_m2=None) == 0.0 def test_predicted_log10_ecf_matches_log10_for_positive_input() -> None: # Arrange / Act / Assert assert predicted_log10_ecf(1.817) == pytest.approx(log10(1.817), abs=0.0001) def test_predicted_log10_ecf_returns_zero_for_nonpositive_input() -> None: # Arrange / Act / Assert assert predicted_log10_ecf(0.0) == 0.0 assert predicted_log10_ecf(-1.5) == 0.0 def test_predicted_ecf_grows_when_more_expensive_fuel() -> None: # Arrange — same kWh, different fuel; electricity ECF >> gas ECF. # Act gas_cost = predicted_total_fuel_cost_gbp( predicted_space_heating_kwh=10000.0, predicted_hot_water_kwh=2500.0, predicted_lighting_kwh=600.0, main_fuel_code=1, water_heating_fuel_code=1, ) elec_cost = predicted_total_fuel_cost_gbp( predicted_space_heating_kwh=10000.0, predicted_hot_water_kwh=2500.0, predicted_lighting_kwh=600.0, main_fuel_code=30, water_heating_fuel_code=30, ) gas_ecf = predicted_ecf(gas_cost, total_floor_area_m2=100.0) elec_ecf = predicted_ecf(elec_cost, total_floor_area_m2=100.0) # Assert — higher ECF -> worse SAP, matches intuition for resistive-electric heating. assert elec_ecf > gas_ecf