From 9ce9d5f6ab8ad7ec9d24b457dc261fa2eaa71aec Mon Sep 17 00:00:00 2001 From: Charlotte Avery <143102500+charlotte-avery@users.noreply.github.com> Date: Thu, 21 Nov 2024 13:20:33 +0000 Subject: [PATCH] Update household heat pump awareness over time (#149) * Add user inputs for heat pump awareness campaign * Log heat pump awareness over time in model * Update HP awareness on campaign date * Agents become HP aware when ban in place All agents do not necessarily become HP aware at the ban announcement. --- k8s/job.jsonnet | 82 ++-------------- simulation/__main__.py | 19 ++++ simulation/agents.py | 44 ++++++++- simulation/collectors.py | 7 +- simulation/constants.py | 1 + simulation/model.py | 37 ++++++- simulation/tests/common.py | 4 + simulation/tests/test_agents.py | 76 +++++++++++++++ simulation/tests/test_main.py | 18 ++++ simulation/tests/test_model.py | 164 +++++++++++++++++++++----------- 10 files changed, 318 insertions(+), 134 deletions(-) diff --git a/k8s/job.jsonnet b/k8s/job.jsonnet index 1d5214d..4ee9a67 100644 --- a/k8s/job.jsonnet +++ b/k8s/job.jsonnet @@ -40,80 +40,15 @@ local job(name, args_excl_output) = { [ // scenarios - job('03g-%s-extended-bus-policy-very-high-awareness' % std.extVar('SHORT_SHA'), [ + job('01a-%s-max-policy-extended-bus' % std.extVar('SHORT_SHA'), [ '--intervention', 'extended_boiler_upgrade_scheme', - '--heat-pump-awareness', - '0.7', - '--air-source-heat-pump-price-discount-date', - '2026-01-01:0.3', - '--price-gbp-per-kwh-gas', - '0.0682', - '--price-gbp-per-kwh-electricity', - '0.182', - ]), - job('03h-%s-extended-bus-policy-extremely-awareness' % std.extVar('SHORT_SHA'), [ '--intervention', - 'extended_boiler_upgrade_scheme', - '--heat-pump-awareness', - '0.9', - '--air-source-heat-pump-price-discount-date', - '2026-01-01:0.3', - '--price-gbp-per-kwh-gas', - '0.0682', - '--price-gbp-per-kwh-electricity', - '0.182', - ]), - job('03i-%s-ext-bus-policy-high-awareness-unlimited-installers' % std.extVar('SHORT_SHA'), [ - '--intervention', - 'extended_boiler_upgrade_scheme', - '--heat-pump-awareness', - '0.5', - '--air-source-heat-pump-price-discount-date', - '2026-01-01:0.3', - '--price-gbp-per-kwh-gas', - '0.0682', - '--price-gbp-per-kwh-electricity', - '0.182', - '--heat-pump-installer-count', - '10000000000', - ]), - job('03j-%s-ext-bus-high-awareness-unltd-installers-new-builds' % std.extVar('SHORT_SHA'), [ - '--intervention', - 'extended_boiler_upgrade_scheme', - '--heat-pump-awareness', - '0.5', - '--air-source-heat-pump-price-discount-date', - '2026-01-01:0.3', - '--price-gbp-per-kwh-gas', - '0.0682', - '--price-gbp-per-kwh-electricity', - '0.182', - '--heat-pump-installer-count', - '10000000000', - '--include-new-builds', - ]), - job('04d-%s-max-policy-wo-new-builds' % std.extVar('SHORT_SHA'), [ - '--intervention', - 'boiler_upgrade_scheme', - '--intervention', - 'gas_oil_boiler_ban', - '--gas-oil-boiler-ban-date', - '2035-01-01', - '--gas-oil-boiler-ban-announce-date', - '2025-01-01', - '--heat-pump-awareness', - '0.5', - '--air-source-heat-pump-price-discount-date', - '2026-01-01:0.3', - '--price-gbp-per-kwh-gas', - '0.0682', - '--price-gbp-per-kwh-electricity', - '0.182', - ]), - job('04e-%s-max-policy-all-suitable' % std.extVar('SHORT_SHA'), [ - '--intervention', - 'boiler_upgrade_scheme', + 'heat_pump_campaign', + '--campaign-target-heat-pump-awareness', + '0.8', + '--heat-pump-awareness-campaign-date', + '2028-01-01', '--intervention', 'gas_oil_boiler_ban', '--gas-oil-boiler-ban-date', @@ -122,12 +57,11 @@ local job(name, args_excl_output) = { '2025-01-01', '--heat-pump-awareness', '0.5', - '--air-source-heat-pump-price-discount-date', - '2026-01-01:0.3', '--price-gbp-per-kwh-gas', '0.0682', '--price-gbp-per-kwh-electricity', '0.182', - '--all-agents-heat-pump-suitable', + '--heat-pump-installer-count', + '10000000000' ]), ] diff --git a/simulation/__main__.py b/simulation/__main__.py index ff7d1b0..ff19543 100644 --- a/simulation/__main__.py +++ b/simulation/__main__.py @@ -178,6 +178,18 @@ def check_string_is_isoformat_datetime(string) -> str: parser.add_argument("--price-gbp-per-kwh-electricity", type=float, default=0.245) parser.add_argument("--price-gbp-per-kwh-oil", type=float, default=0.068) + parser.add_argument( + "--heat-pump-awareness-campaign-date", + default=datetime.datetime(2028, 1, 1), + type=convert_to_datetime, + ) + + parser.add_argument( + "--campaign-target-heat-pump-awareness", + default=0.8, + type=float_between_0_and_1, + ) + return parser.parse_args(args) @@ -187,6 +199,11 @@ def validate_args(args): f"Boiler ban announcement date must be on or before ban date, got gas_oil_boiler_ban_date:{args.gas_oil_boiler_ban_date}, gas_oil_boiler_ban_announce_date:{args.gas_oil_boiler_ban_announce_date}" ) + if args.campaign_target_heat_pump_awareness < args.heat_pump_awareness: + raise ValueError( + f"Campaign target awareness must be greater than or equal to the population heat pump awareness, got campaign_target_heat_pump_awareness:{args.campaign_target_heat_pump_awareness}, heat_pump_awareness:{args.heat_pump_awareness}" + ) + if __name__ == "__main__": @@ -226,6 +243,8 @@ def validate_args(args): args.heat_pump_installer_count, args.heat_pump_installer_annual_growth_rate, ENGLAND_WALES_ANNUAL_NEW_BUILDS if args.include_new_builds else None, + args.campaign_target_heat_pump_awareness, + args.heat_pump_awareness_campaign_date, ) with smart_open.open(args.history_file, "w") as file: diff --git a/simulation/agents.py b/simulation/agents.py index 276c130..ea9b631 100644 --- a/simulation/agents.py +++ b/simulation/agents.py @@ -440,8 +440,12 @@ def get_heating_system_options( [HeatingSystem.BOILER_GAS, HeatingSystem.BOILER_OIL] ) - if not is_gas_oil_boiler_ban_announced: - # if a gas/boiler ban is announced, we assume all households are aware of heat pumps + is_gas_oil_boiler_ban_in_place = ( + InterventionType.GAS_OIL_BOILER_BAN in model.interventions + and model.current_datetime >= model.gas_oil_boiler_ban_datetime + ) + if not is_gas_oil_boiler_ban_in_place: + # if a gas/boiler ban is in place, we assume all households are aware of heat pumps if not self.is_heat_pump_aware: heating_system_options -= HEAT_PUMPS @@ -611,8 +615,44 @@ def compute_heat_pump_capacity_kw(self, heat_pump_type: HeatingSystem) -> int: ) ) + def proba_of_becoming_heat_pump_aware_required_to_reach_campaign_target( + self, model + ) -> float: + heat_pump_awareness_at_previous_timestep = ( + model.heat_pump_awareness_at_timestep + - ( + model.num_households_switching_to_heat_pump_aware_at_current_timestep + / model.household_count + ) + ) + return ( + model.campaign_target_heat_pump_awareness + - heat_pump_awareness_at_previous_timestep + ) / (1 - heat_pump_awareness_at_previous_timestep) + + def update_heat_pump_awareness(self, model) -> None: + if ( + InterventionType.HEAT_PUMP_CAMPAIGN in model.interventions + and model.current_datetime >= model.heat_pump_awareness_campaign_date + and model.heat_pump_awareness_at_timestep + < model.campaign_target_heat_pump_awareness + and not self.is_heat_pump_aware + ): + proba_to_become_heat_pump_aware = self.proba_of_becoming_heat_pump_aware_required_to_reach_campaign_target( + model + ) + self.is_heat_pump_aware = true_with_probability( + proba_to_become_heat_pump_aware + ) + if self.is_heat_pump_aware: + model.num_households_switching_to_heat_pump_aware += 1 + model.num_households_switching_to_heat_pump_aware_at_current_timestep += ( + 1 + ) + def make_decisions(self, model): + self.update_heat_pump_awareness(model) self.update_heating_status(model) self.evaluate_renovation(model) diff --git a/simulation/collectors.py b/simulation/collectors.py index 1f09ff0..c21555d 100644 --- a/simulation/collectors.py +++ b/simulation/collectors.py @@ -277,6 +277,10 @@ def model_heat_pump_installations_at_current_step(model) -> int: return model.heat_pump_installations_at_current_step +def model_heat_pump_awareness_at_timestep(model) -> float: + return model.heat_pump_awareness_at_timestep + + def is_first_timestep(model: "DomesticHeatingABM") -> bool: return model.current_datetime == model.start_datetime + model.step_interval @@ -303,7 +307,6 @@ def get_agent_collectors( collect_when(model, is_first_timestep)(household_discount_rate), collect_when(model, is_first_timestep)(household_renovation_budget), collect_when(model, is_first_timestep)(household_is_heat_pump_suitable), - collect_when(model, is_first_timestep)(household_is_heat_pump_aware), household_heating_system, household_heating_system_previous, household_heating_functioning, @@ -340,6 +343,7 @@ def get_agent_collectors( household_heating_system_costs_insulation_heat_pump_air_source, household_heating_system_costs_insulation_heat_pump_ground_source, household_boiler_upgrade_grant_used, + household_is_heat_pump_aware, ] @@ -355,4 +359,5 @@ def get_model_collectors( collect_when(model, is_first_timestep)(model_price_gbp_per_kwh_gas), collect_when(model, is_first_timestep)(model_price_gbp_per_kwh_electricity), collect_when(model, is_first_timestep)(model_price_gbp_per_kwh_oil), + model_heat_pump_awareness_at_timestep, ] diff --git a/simulation/constants.py b/simulation/constants.py index 6335079..12b73d7 100644 --- a/simulation/constants.py +++ b/simulation/constants.py @@ -206,6 +206,7 @@ class InterventionType(enum.Enum): BOILER_UPGRADE_SCHEME = 1 GAS_OIL_BOILER_BAN = 2 EXTENDED_BOILER_UPGRADE_SCHEME = 3 + HEAT_PUMP_CAMPAIGN = 4 # Source: https://www.ons.gov.uk/peoplepopulationandcommunity/birthsdeathsandmarriages/families/datasets/householdsbytypeofhouseholdandfamilyregionsofenglandandukconstituentcountries diff --git a/simulation/model.py b/simulation/model.py index d4c0e2a..7a5c4e3 100644 --- a/simulation/model.py +++ b/simulation/model.py @@ -46,6 +46,10 @@ def __init__( heat_pump_installer_count: int, heat_pump_installer_annual_growth_rate: float, annual_new_builds: Optional[Dict[int, int]], + heat_pump_awareness: float, + campaign_target_heat_pump_awareness: float, + heat_pump_awareness_campaign_date: datetime.datetime, + population_heat_pump_awareness: List[bool], ): self.start_datetime = start_datetime self.step_interval = step_interval @@ -74,6 +78,13 @@ def __init__( ) self.heat_pump_installations_at_current_step = 0 self.annual_new_builds = annual_new_builds + self.heat_pump_awareness = heat_pump_awareness + self.campaign_target_heat_pump_awareness = campaign_target_heat_pump_awareness + self.heat_pump_awareness_campaign_date = heat_pump_awareness_campaign_date + + self.population_heat_pump_awareness = population_heat_pump_awareness + self.num_households_heat_pump_aware = sum(population_heat_pump_awareness) + self.num_households_switching_to_heat_pump_aware = 0 super().__init__(UnorderedSpace()) @@ -193,21 +204,29 @@ def boiler_upgrade_scheme_spend_gbp(self) -> int: ] ) + @property + def heat_pump_awareness_at_timestep(self) -> float: + return ( + self.num_households_heat_pump_aware + + self.num_households_switching_to_heat_pump_aware + ) / self.household_count + def increment_timestep(self): self.current_datetime += self.step_interval self.boiler_upgrade_scheme_cumulative_spend_gbp += ( self.boiler_upgrade_scheme_spend_gbp ) self.heat_pump_installations_at_current_step = 0 + self.num_households_switching_to_heat_pump_aware_at_current_timestep = 0 def create_household_agents( household_population: pd.DataFrame, - heat_pump_awareness: float, + population_heat_pump_awareness: List[bool], simulation_start_datetime: datetime.datetime, all_agents_heat_pump_suitable: bool, ) -> Iterator[Household]: - for household in household_population.itertuples(): + for i, household in enumerate(household_population.itertuples()): yield Household( id=household.id, location=household.location, @@ -236,7 +255,7 @@ def create_household_agents( is_heat_pump_suitable_archetype=True if all_agents_heat_pump_suitable else household.is_heat_pump_suitable_archetype, - is_heat_pump_aware=random.random() < heat_pump_awareness, + is_heat_pump_aware=population_heat_pump_awareness[i], ) @@ -263,8 +282,14 @@ def create_and_run_simulation( heat_pump_installer_count: int, heat_pump_installer_annual_growth_rate: float, annual_new_builds: Dict[int, int], + campaign_target_heat_pump_awareness: float, + heat_pump_awareness_campaign_date: datetime.datetime, ): + population_heat_pump_awareness = [ + random.random() < heat_pump_awareness for _ in range(len(household_population)) + ] + model = DomesticHeatingABM( start_datetime=start_datetime, step_interval=step_interval, @@ -282,11 +307,15 @@ def create_and_run_simulation( heat_pump_installer_count=heat_pump_installer_count, heat_pump_installer_annual_growth_rate=heat_pump_installer_annual_growth_rate, annual_new_builds=annual_new_builds, + heat_pump_awareness=heat_pump_awareness, + campaign_target_heat_pump_awareness=campaign_target_heat_pump_awareness, + heat_pump_awareness_campaign_date=heat_pump_awareness_campaign_date, + population_heat_pump_awareness=population_heat_pump_awareness, ) households = create_household_agents( household_population, - heat_pump_awareness, + population_heat_pump_awareness, model.start_datetime, all_agents_heat_pump_suitable, ) diff --git a/simulation/tests/common.py b/simulation/tests/common.py index f79d42b..a27ae6c 100644 --- a/simulation/tests/common.py +++ b/simulation/tests/common.py @@ -57,6 +57,10 @@ def model_factory(**model_attributes): "heat_pump_installer_count": 2_800, "heat_pump_installer_annual_growth_rate": 0, "annual_new_builds": None, + "heat_pump_awareness": 0.5, + "campaign_target_heat_pump_awareness": 0.8, + "heat_pump_awareness_campaign_date": datetime.datetime(2028, 1, 1), + "population_heat_pump_awareness": [], } return DomesticHeatingABM(**{**default_values, **model_attributes}) diff --git a/simulation/tests/test_agents.py b/simulation/tests/test_agents.py index fabc19a..56cc183 100644 --- a/simulation/tests/test_agents.py +++ b/simulation/tests/test_agents.py @@ -697,6 +697,82 @@ def test_household_ability_to_choose_heat_pump_as_option_depends_on_model_heat_p assert all(heat_pump in heating_system_options for heat_pump in HEAT_PUMPS) + def test_heat_pump_awareness_updated_after_successful_campaign( + self, + ): + + model = model_factory( + start_datetime=datetime.datetime(2025, 1, 1), + step_interval=relativedelta(months=1), + interventions=[InterventionType.HEAT_PUMP_CAMPAIGN], + heat_pump_awareness_campaign_date=datetime.datetime(2025, 3, 1), + campaign_target_heat_pump_awareness=1.0, + ) + agent = household_factory(is_heat_pump_aware=False) + model.add_agents([agent]) + + agent_awarness_before_campaign = agent.is_heat_pump_aware + model_awarness_before_campaign = model.heat_pump_awareness_at_timestep + + # Expect heat pump awareness to be 0 for first two timesteps, increasing to 1 after campaign date + assert not agent_awarness_before_campaign + assert pytest.approx(model_awarness_before_campaign) == 0.0 + + model.increment_timestep() + agent.make_decisions(model) + agent_awarness_before_campaign = agent.is_heat_pump_aware + model_awarness_before_campaign = model.heat_pump_awareness_at_timestep + + assert not agent_awarness_before_campaign + assert pytest.approx(model_awarness_before_campaign) == 0.0 + + model.increment_timestep() + agent.make_decisions(model) + agent_awarness_after_campaign = agent.is_heat_pump_aware + model_awarness_after_campaign = model.heat_pump_awareness_at_timestep + + assert agent_awarness_after_campaign + assert pytest.approx(model_awarness_after_campaign) == 1.0 + + # Check agent stays aware for another timestep... + model.increment_timestep() + agent.make_decisions(model) + agent_awarness_after_campaign = agent.is_heat_pump_aware + model_awarness_after_campaign = model.heat_pump_awareness_at_timestep + + assert agent_awarness_after_campaign + assert pytest.approx(model_awarness_after_campaign) == 1.0 + + def test_heat_pump_awareness_does_not_increase_when_campaign_target_is_same_as_current_awareness( + self, + ): + + model = model_factory( + start_datetime=datetime.datetime(2025, 1, 1), + step_interval=relativedelta(months=1), + interventions=[InterventionType.HEAT_PUMP_CAMPAIGN], + heat_pump_awareness_campaign_date=datetime.datetime(2025, 2, 1), + heat_pump_awareness=0.0, + campaign_target_heat_pump_awareness=0.0, + population_heat_pump_awareness=[False], + ) + agent = household_factory(is_heat_pump_aware=False) + model.add_agents([agent]) + + agent_awarness_before_campaign = agent.is_heat_pump_aware + model_awarness_before_campaign = model.heat_pump_awareness_at_timestep + + assert not agent_awarness_before_campaign + assert pytest.approx(model_awarness_before_campaign) == 0.0 + + model.increment_timestep() + agent.make_decisions(model) + agent_awarness_after_campaign = agent.is_heat_pump_aware + model_awarness_after_campaign = model.heat_pump_awareness_at_timestep + + assert not agent_awarness_after_campaign + assert pytest.approx(model_awarness_after_campaign) == 0.0 + class TestAgentsWithBoilerBan: def test_households_increasingly_likely_to_rule_out_heating_systems_that_will_be_banned_as_time_to_ban_decreases( diff --git a/simulation/tests/test_main.py b/simulation/tests/test_main.py index da62c41..b3e0067 100644 --- a/simulation/tests/test_main.py +++ b/simulation/tests/test_main.py @@ -158,6 +158,8 @@ def test_intervention_argument(self, mandatory_local_args): "boiler_upgrade_scheme", "--intervention", "extended_boiler_upgrade_scheme", + "--intervention", + "heat_pump_campaign", ] ) @@ -165,6 +167,7 @@ def test_intervention_argument(self, mandatory_local_args): InterventionType.RHI, InterventionType.BOILER_UPGRADE_SCHEME, InterventionType.EXTENDED_BOILER_UPGRADE_SCHEME, + InterventionType.HEAT_PUMP_CAMPAIGN, ] def test_gas_oil_boiler_ban_date_returns_datetime(self, mandatory_local_args): @@ -289,3 +292,18 @@ def test_ban_date_before_announcement_date_raises_value_error( ) with pytest.raises(ValueError): validate_args(args) + + def test_campaign_target_less_than_heat_pump_awareness_raises_value_error( + self, mandatory_local_args + ): + args = parse_args( + [ + *mandatory_local_args, + "--campaign-target-heat-pump-awareness", + "0.1", + "--heat-pump-awareness", + "0.5", + ] + ) + with pytest.raises(ValueError): + validate_args(args) diff --git a/simulation/tests/test_model.py b/simulation/tests/test_model.py index eb40441..a50e54c 100644 --- a/simulation/tests/test_model.py +++ b/simulation/tests/test_model.py @@ -12,6 +12,7 @@ ConstructionYearBand, EPCRating, HeatingSystem, + InterventionType, OccupantType, PropertyType, ) @@ -245,63 +246,120 @@ def test_model_installs_heat_pumps_in_existing_builds_when_there_is_capacity(sel assert model.has_heat_pump_installation_capacity -def test_create_household_agents() -> None: +class test_household_agents: + household_population = pd.DataFrame( { - "id": [1], - "location": ["Birmingham"], - "property_value_gbp": [264_000], - "total_floor_area_m2": [82], - "is_off_gas_grid": [False], - "construction_year_band": ["BUILT_2007_ONWARDS"], - "property_type": ["house"], - "built_form": ["mid_terrace"], - "heating_system": ["boiler_gas"], - "epc_rating": ["C"], - "potential_epc_rating": ["B"], - "occupant_type": ["owner_occupied"], - "is_solid_wall": [False], - "walls_energy_efficiency": [3], - "windows_energy_efficiency": [3], - "roof_energy_efficiency": [3], - "is_heat_pump_suitable_archetype": [True], + "id": [1, 2, 3, 4], + "location": ["Birmingham", "London", "Manchester", "Bristol"], + "property_value_gbp": [264_000, 700_000, 300_000, 350_000], + "total_floor_area_m2": [82, 100, 90, 95], + "is_off_gas_grid": [False, False, False, False], + "construction_year_band": [ + "BUILT_2007_ONWARDS", + "BUILT_2007_ONWARDS", + "BUILT_2007_ONWARDS", + "BUILT_2007_ONWARDS", + ], + "property_type": ["house", "house", "house", "house"], + "built_form": ["mid_terrace", "detached", "semi_detached", "end_terrace"], + "heating_system": ["boiler_gas", "boiler_gas", "boiler_gas", "boiler_gas"], + "epc_rating": ["C", "D", "C", "D"], + "potential_epc_rating": ["B", "C", "B", "B"], + "occupant_type": [ + "owner_occupied", + "owner_occupied", + "owner_occupied", + "owner_occupied", + ], + "is_solid_wall": [False, False, False, False], + "walls_energy_efficiency": [3, 3, 3, 3], + "windows_energy_efficiency": [3, 3, 3, 3], + "roof_energy_efficiency": [3, 3, 3, 3], + "is_heat_pump_suitable_archetype": [True, True, True, False], } ) - heat_pump_awareness = 0.4 + simulation_start_datetime = datetime.datetime.now() all_agents_heat_pump_suitable = False - household_agents = create_household_agents( - household_population, - heat_pump_awareness, - simulation_start_datetime, - all_agents_heat_pump_suitable, - ) - household = next(household_agents) - - assert household.id == 1 - assert household.location == "Birmingham" - assert household.property_value_gbp == 264_000 - assert household.total_floor_area_m2 == 82 - assert not household.is_off_gas_grid - assert household.construction_year_band == ConstructionYearBand.BUILT_2007_ONWARDS - assert household.property_type == PropertyType.HOUSE - assert household.built_form == BuiltForm.MID_TERRACE - assert household.heating_system == HeatingSystem.BOILER_GAS - assert ( - simulation_start_datetime.date() - - datetime.timedelta(days=365 * HEATING_SYSTEM_LIFETIME_YEARS) - <= household.heating_system_install_date - <= simulation_start_datetime.date() - ) - assert household.epc_rating == EPCRating.C - assert household.potential_epc_rating == EPCRating.B - assert household.occupant_type == OccupantType.OWNER_OCCUPIED - assert not household.is_solid_wall - assert household.walls_energy_efficiency == 3 - assert household.windows_energy_efficiency == 3 - assert household.roof_energy_efficiency == 3 - assert household.is_heat_pump_suitable_archetype - assert household.is_heat_pump_aware is not None - - with pytest.raises(StopIteration): + + def test_create_household_agents(self) -> None: + population_heat_pump_awareness = [True, True, True, True] + household_agents = create_household_agents( + self.household_population, + population_heat_pump_awareness, + self.simulation_start_datetime, + self.all_agents_heat_pump_suitable, + ) + household = next(household_agents) + + assert household.id == 1 + assert household.location == "Birmingham" + assert household.property_value_gbp == 264_000 + assert household.total_floor_area_m2 == 82 + assert not household.is_off_gas_grid + assert ( + household.construction_year_band == ConstructionYearBand.BUILT_2007_ONWARDS + ) + assert household.property_type == PropertyType.HOUSE + assert household.built_form == BuiltForm.MID_TERRACE + assert household.heating_system == HeatingSystem.BOILER_GAS + assert ( + self.simulation_start_datetime.date() + - datetime.timedelta(days=365 * HEATING_SYSTEM_LIFETIME_YEARS) + <= household.heating_system_install_date + <= self.simulation_start_datetime.date() + ) + assert household.epc_rating == EPCRating.C + assert household.potential_epc_rating == EPCRating.B + assert household.occupant_type == OccupantType.OWNER_OCCUPIED + assert not household.is_solid_wall + assert household.walls_energy_efficiency == 3 + assert household.windows_energy_efficiency == 3 + assert household.roof_energy_efficiency == 3 + assert household.is_heat_pump_suitable_archetype + assert household.is_heat_pump_aware is not None + + next(household_agents) + next(household_agents) next(household_agents) + + with pytest.raises(StopIteration): + next(household_agents) + + def test_all_household_agents_become_heat_pump_aware_with_100_per_cent_campaign_success( + self, + ) -> None: + population_heat_pump_awareness = [False, False, False, False] + campaign_target_heat_pump_awareness = 1.0 + + household_agents = create_household_agents( + self.household_population, + population_heat_pump_awareness, + self.simulation_start_datetime, + self.all_agents_heat_pump_suitable, + ) + + model = model_factory( + start_datetime=datetime.datetime(2025, 1, 1), + step_interval=relativedelta(months=1), + interventions=[InterventionType.HEAT_PUMP_CAMPAIGN], + heat_pump_awareness_campaign_date=datetime.datetime(2025, 2, 1), + heat_pump_awareness=0.0, + campaign_target_heat_pump_awareness=campaign_target_heat_pump_awareness, + population_heat_pump_awareness=population_heat_pump_awareness, + ) + model.add_agents([household_agents]) + assert model.heat_pump_awareness_at_timestep == 0.0 + + model.increment_timestep() + for household in household_agents: + household.make_decisions(model) + assert household.is_heat_pump_aware + + assert model.heat_pump_awareness_at_timestep == 1.0 + + model.increment_timestep() + for household in household_agents: + household.make_decisions(model) + assert household.is_heat_pump_aware