From 4f3f285518ada6469a19c22e4c3ed0ca3f225372 Mon Sep 17 00:00:00 2001 From: Charlotte Avery Date: Mon, 18 Nov 2024 15:24:11 +0000 Subject: [PATCH 1/4] Log HP awareness in the model over time --- simulation/agents.py | 3 +++ simulation/collectors.py | 5 +++++ simulation/model.py | 6 ++++++ 3 files changed, 14 insertions(+) diff --git a/simulation/agents.py b/simulation/agents.py index 276c130..08e37f5 100644 --- a/simulation/agents.py +++ b/simulation/agents.py @@ -686,3 +686,6 @@ def make_decisions(self, model): self.heating_system_costs_subsidies = costs_subsidies self.heating_system_costs_insulation = costs_insulation self.insulation_element_upgrade_costs = chosen_insulation_costs + + if self.is_heat_pump_aware: + model.households_heat_pump_aware_at_current_step += 1 diff --git a/simulation/collectors.py b/simulation/collectors.py index 1f09ff0..866eb2b 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(model) -> int: + return model.heat_pump_awareness + + def is_first_timestep(model: "DomesticHeatingABM") -> bool: return model.current_datetime == model.start_datetime + model.step_interval @@ -352,6 +356,7 @@ def get_model_collectors( model_heat_pump_installers, model_heat_pump_installation_capacity_per_step, model_heat_pump_installations_at_current_step, + model_heat_pump_awareness, 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), diff --git a/simulation/model.py b/simulation/model.py index d4c0e2a..e518a91 100644 --- a/simulation/model.py +++ b/simulation/model.py @@ -73,6 +73,7 @@ def __init__( heat_pump_installer_annual_growth_rate ) self.heat_pump_installations_at_current_step = 0 + self.households_heat_pump_aware_at_current_step = 0 self.annual_new_builds = annual_new_builds super().__init__(UnorderedSpace()) @@ -81,6 +82,10 @@ def __init__( def household_count(self) -> int: return len(self.space.agents) + @property + def heat_pump_awareness(self) -> float: + return self.households_heat_pump_aware_at_current_step / self.household_count + @property def heat_pump_installers(self) -> int: @@ -199,6 +204,7 @@ def increment_timestep(self): self.boiler_upgrade_scheme_spend_gbp ) self.heat_pump_installations_at_current_step = 0 + self.households_heat_pump_aware_at_current_step = 0 def create_household_agents( From 5a860c4fd1a53e145d82f8907f98f3d9d774ea4c Mon Sep 17 00:00:00 2001 From: Charlotte Avery Date: Tue, 19 Nov 2024 12:46:35 +0000 Subject: [PATCH 2/4] Add HP awareness campaign intervention * Changes allow for a step up in HP awareness as specified in new user inputs. --- simulation/__main__.py | 14 +++ simulation/agents.py | 11 ++ simulation/collectors.py | 7 ++ simulation/constants.py | 1 + simulation/model.py | 78 ++++++++++++- simulation/tests/common.py | 2 + simulation/tests/test_agents.py | 2 + simulation/tests/test_main.py | 3 + simulation/tests/test_model.py | 199 +++++++++++++++++++++++--------- 9 files changed, 259 insertions(+), 58 deletions(-) diff --git a/simulation/__main__.py b/simulation/__main__.py index ff7d1b0..21903c7 100644 --- a/simulation/__main__.py +++ b/simulation/__main__.py @@ -172,6 +172,18 @@ def check_string_is_isoformat_datetime(string) -> str: type=convert_to_datetime, ) + parser.add_argument( + "--heat-pump-awareness-campaign-date", + default=datetime.datetime(2028, 1, 1), + type=convert_to_datetime, + ) + + parser.add_argument( + "--heat-pump-awareness-due-to-campaign", + default=0.8, + type=float_between_0_and_1, + ) + # SOURCE: Default values from https://energysavingtrust.org.uk/about-us/our-data/ (England, Scotland and Wales) # These fuel prices were last updated in November 2021, based on predicted fuel prices for 2022 parser.add_argument("--price-gbp-per-kwh-gas", type=float, default=0.062) @@ -226,6 +238,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.heat_pump_awareness_campaign_date, + args.heat_pump_awareness_due_to_campaign, ) with smart_open.open(args.history_file, "w") as file: diff --git a/simulation/agents.py b/simulation/agents.py index 08e37f5..7b4dcd0 100644 --- a/simulation/agents.py +++ b/simulation/agents.py @@ -123,6 +123,7 @@ def __init__( roof_energy_efficiency: int, is_heat_pump_suitable_archetype: bool, is_heat_pump_aware: bool, + is_heat_pump_aware_after_campaign: bool, ): self.id = id # Property / tenure attributes @@ -150,6 +151,9 @@ def __init__( self.is_heat_pump_aware = ( self.heating_system in HEAT_PUMPS or is_heat_pump_aware ) + self.is_heat_pump_aware_after_campaign = ( + self.heating_system in HEAT_PUMPS or is_heat_pump_aware_after_campaign + ) # Household investment decision attributes self.is_renovating = False @@ -416,6 +420,12 @@ def get_proba_rule_out_banned_heating_systems(self, model): return reverse_sigmoid(years_to_ban) + def update_heat_pump_awareness(self, model) -> None: + # If heat pump awareness campaign has been done, update awareness in response to campaign + if InterventionType.HEAT_PUMP_CAMPAIGN in model.interventions: + if model.current_datetime >= model.heat_pump_awareness_campaign_date: + self.is_heat_pump_aware = self.is_heat_pump_aware_after_campaign + def get_heating_system_options( self, model: "DomesticHeatingABM", event_trigger: EventTrigger ) -> Set[HeatingSystem]: @@ -613,6 +623,7 @@ def compute_heat_pump_capacity_kw(self, heat_pump_type: HeatingSystem) -> int: 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 866eb2b..23bc00f 100644 --- a/simulation/collectors.py +++ b/simulation/collectors.py @@ -97,6 +97,10 @@ def household_is_heat_pump_aware(household) -> bool: return household.is_heat_pump_aware +def household_is_heat_pump_aware_after_campaign(household) -> bool: + return household.is_heat_pump_aware_after_campaign + + def household_is_renovating(household) -> bool: return household.is_renovating @@ -308,6 +312,9 @@ def get_agent_collectors( 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), + collect_when(model, is_first_timestep)( + household_is_heat_pump_aware_after_campaign + ), household_heating_system, household_heating_system_previous, household_heating_functioning, 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 e518a91..ec14f9d 100644 --- a/simulation/model.py +++ b/simulation/model.py @@ -46,6 +46,7 @@ def __init__( heat_pump_installer_count: int, heat_pump_installer_annual_growth_rate: float, annual_new_builds: Optional[Dict[int, int]], + heat_pump_awareness_campaign_date: datetime.datetime, ): self.start_datetime = start_datetime self.step_interval = step_interval @@ -75,6 +76,7 @@ def __init__( self.heat_pump_installations_at_current_step = 0 self.households_heat_pump_aware_at_current_step = 0 self.annual_new_builds = annual_new_builds + self.heat_pump_awareness_campaign_date = heat_pump_awareness_campaign_date super().__init__(UnorderedSpace()) @@ -207,13 +209,74 @@ def increment_timestep(self): self.households_heat_pump_aware_at_current_step = 0 +def construct_population_awareness( + household_population: pd.DataFrame, heat_pump_awareness: float +) -> List[bool]: + """Construct a list assigning heat pump awareness to each agent in the population""" + # Randomly assign heat pump awareness to the agent population with a probability given by `heat_pump_awareness` + return [ + random.random() < heat_pump_awareness for _ in range(len(household_population)) + ] + + +def construct_population_awareness_due_to_campaign( + population_heat_pump_awareness: List[bool], + heat_pump_awareness_due_to_campaign: float, + heat_pump_awareness: float, +) -> List[bool]: + """Construct a list assigning heat pump awareness to each agent in the population after the campaign""" + + # Awareness cannot decrease over time + # Ensure that the campaign target awareness is at least the current awareness + heat_pump_awareness_due_to_campaign = max( + heat_pump_awareness_due_to_campaign, heat_pump_awareness + ) + + # A fraction of the population are always heat pump aware, as defined by `heat_pump_awareness` + # To reach the target awareness for the campaign, we need to increase the awareness of the remaning unaware population + # Here, the fraction of the remaining population which needs to become aware to reach the campaign target is defined + unaware_population_fraction = 1 - heat_pump_awareness + if unaware_population_fraction == 0.0: + # If all agents aware already, target awareness is zero + target_heat_pump_awareness_for_campaign = 0.0 + else: + target_heat_pump_awareness_for_campaign = ( + heat_pump_awareness_due_to_campaign - heat_pump_awareness + ) / unaware_population_fraction + + population_heat_pump_awareness_after_campaign = ( + population_heat_pump_awareness.copy() + ) + # Randomly assign heat pump awareness to the initially non-aware agent population + for i in range(len(population_heat_pump_awareness_after_campaign)): + if not population_heat_pump_awareness_after_campaign[i]: + rand_gen = random.random() + if rand_gen < target_heat_pump_awareness_for_campaign: + population_heat_pump_awareness_after_campaign[i] = True + + return population_heat_pump_awareness_after_campaign + + def create_household_agents( household_population: pd.DataFrame, - heat_pump_awareness: float, simulation_start_datetime: datetime.datetime, all_agents_heat_pump_suitable: bool, + heat_pump_awareness: float, + heat_pump_awareness_due_to_campaign: float, ) -> Iterator[Household]: - for household in household_population.itertuples(): + + population_heat_pump_awareness = construct_population_awareness( + household_population, heat_pump_awareness + ) + population_heat_pump_awareness_after_campaign = ( + construct_population_awareness_due_to_campaign( + population_heat_pump_awareness, + heat_pump_awareness_due_to_campaign, + heat_pump_awareness, + ) + ) + + for i, household in enumerate(household_population.itertuples()): yield Household( id=household.id, location=household.location, @@ -242,7 +305,10 @@ 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], + is_heat_pump_aware_after_campaign=population_heat_pump_awareness_after_campaign[ + i + ], ) @@ -269,6 +335,8 @@ def create_and_run_simulation( heat_pump_installer_count: int, heat_pump_installer_annual_growth_rate: float, annual_new_builds: Dict[int, int], + heat_pump_awareness_campaign_date: datetime.datetime, + heat_pump_awareness_due_to_campaign: float, ): model = DomesticHeatingABM( @@ -288,13 +356,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_campaign_date=heat_pump_awareness_campaign_date, ) households = create_household_agents( household_population, - heat_pump_awareness, model.start_datetime, all_agents_heat_pump_suitable, + heat_pump_awareness, + heat_pump_awareness_due_to_campaign, ) model.add_agents(households) diff --git a/simulation/tests/common.py b/simulation/tests/common.py index f79d42b..190bb5a 100644 --- a/simulation/tests/common.py +++ b/simulation/tests/common.py @@ -35,6 +35,7 @@ def household_factory(**agent_attributes): "roof_energy_efficiency": 3, "is_heat_pump_suitable_archetype": True, "is_heat_pump_aware": True, + "is_heat_pump_aware_after_campaign": True, } return Household(**{**default_values, **agent_attributes}) @@ -57,6 +58,7 @@ 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_campaign_date": datetime.datetime(2025, 2, 1), } return DomesticHeatingABM(**{**default_values, **model_attributes}) diff --git a/simulation/tests/test_agents.py b/simulation/tests/test_agents.py index fabc19a..abdb320 100644 --- a/simulation/tests/test_agents.py +++ b/simulation/tests/test_agents.py @@ -47,6 +47,7 @@ def test_create_household_factory(self) -> None: roof_energy_efficiency=2, is_heat_pump_suitable_archetype=True, is_heat_pump_aware=True, + is_heat_pump_aware_after_campaign=True, ) assert household.id == 1 assert household.location == "London" @@ -68,6 +69,7 @@ def test_create_household_factory(self) -> None: assert household.is_heat_pump_suitable_archetype assert household.heating_fuel == HeatingFuel.ELECTRICITY assert household.is_heat_pump_aware + assert household.is_heat_pump_aware_after_campaign assert household.is_renovating is not None def test_household_renovation_budget_increases_with_property_value(self) -> None: diff --git a/simulation/tests/test_main.py b/simulation/tests/test_main.py index da62c41..1dbdea9 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): diff --git a/simulation/tests/test_model.py b/simulation/tests/test_model.py index eb40441..ada6040 100644 --- a/simulation/tests/test_model.py +++ b/simulation/tests/test_model.py @@ -245,63 +245,154 @@ 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 TestCreateHouseholdAgents: + 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], + "location": ["Birmingham", "London"], + "property_value_gbp": [264_000, 500_000], + "total_floor_area_m2": [82, 120], + "is_off_gas_grid": [False, False], + "construction_year_band": ["BUILT_2007_ONWARDS", "BUILT_2007_ONWARDS"], + "property_type": ["house", "house"], + "built_form": ["mid_terrace", "detached"], + "heating_system": ["boiler_gas", "boiler_gas"], + "epc_rating": ["C", "D"], + "potential_epc_rating": ["B", "B"], + "occupant_type": ["owner_occupied", "owner_occupied"], + "is_solid_wall": [False, False], + "walls_energy_efficiency": [3, 3], + "windows_energy_efficiency": [3, 3], + "roof_energy_efficiency": [3, 3], + "is_heat_pump_suitable_archetype": [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): - next(household_agents) + + def test_create_household_agents(self) -> None: + heat_pump_awareness = 0.4 + heat_pump_awareness_due_to_campaign = 0.8 + + # population_heat_pump_awareness = construct_population_awareness(household_population, heat_pump_awareness) + # population_heat_pump_awareness_after_campaign = construct_population_awareness_due_to_campaign(household_population, heat_pump_awareness, heat_pump_awareness_due_to_campaign) + + household_agents = create_household_agents( + self.household_population, + self.simulation_start_datetime, + self.all_agents_heat_pump_suitable, + heat_pump_awareness, + heat_pump_awareness_due_to_campaign, + # population_heat_pump_awareness, + # population_heat_pump_awareness_after_campaign + ) + 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 + assert household.is_heat_pump_aware_after_campaign is not None + + household = next(household_agents) + + with pytest.raises(StopIteration): + next(household_agents) + + def test_all_household_agents_are_heat_pump_aware_with_maximum_awareness( + self, + ) -> None: + heat_pump_awareness = 1.0 + heat_pump_awareness_due_to_campaign = 1.0 + + household_agents = create_household_agents( + self.household_population, + self.simulation_start_datetime, + self.all_agents_heat_pump_suitable, + heat_pump_awareness, + heat_pump_awareness_due_to_campaign, + ) + + for household in household_agents: + assert household.is_heat_pump_aware + assert household.is_heat_pump_aware_after_campaign + + def test_all_household_agents_become_heat_pump_aware_with_100_per_cent_campaign_success( + self, + ) -> None: + heat_pump_awareness = 0.5 + heat_pump_awareness_due_to_campaign = 1.0 + + household_agents = create_household_agents( + self.household_population, + self.simulation_start_datetime, + self.all_agents_heat_pump_suitable, + heat_pump_awareness, + heat_pump_awareness_due_to_campaign, + ) + + for household in household_agents: + assert household.is_heat_pump_aware_after_campaign + + def test_household_agents_do_not_change_awareness_with_no_increase_in_awareness_due_to_campaign( + self, + ) -> None: + heat_pump_awareness = 0.5 + heat_pump_awareness_due_to_campaign = 0.5 + + household_agents = create_household_agents( + self.household_population, + self.simulation_start_datetime, + self.all_agents_heat_pump_suitable, + heat_pump_awareness, + heat_pump_awareness_due_to_campaign, + ) + + for household in household_agents: + assert ( + household.is_heat_pump_aware + == household.is_heat_pump_aware_after_campaign + ) + + def test_household_agents_awareness_cannot_decrease_due_to_campaign(self) -> None: + # Even if `heat_pump_awareness_due_to_campaign` is less than `heat_pump_awareness`, the awareness cannot decrease + heat_pump_awareness = 0.5 + heat_pump_awareness_due_to_campaign = 0.0 + + household_agents = create_household_agents( + self.household_population, + self.simulation_start_datetime, + self.all_agents_heat_pump_suitable, + heat_pump_awareness, + heat_pump_awareness_due_to_campaign, + ) + + for household in household_agents: + assert ( + household.is_heat_pump_aware + == household.is_heat_pump_aware_after_campaign + ) From 283302a1306dad54a310f68c77ee265406d80195 Mon Sep 17 00:00:00 2001 From: Charlotte Avery Date: Tue, 19 Nov 2024 12:47:20 +0000 Subject: [PATCH 3/4] Only get 100% awareness after ban is in place Before, 100% awareness was assume at the ban announcement date --- simulation/agents.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/simulation/agents.py b/simulation/agents.py index 7b4dcd0..ec30161 100644 --- a/simulation/agents.py +++ b/simulation/agents.py @@ -450,8 +450,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 From 46c3b159216e7ff673d54227e9d6be9319c2d67c Mon Sep 17 00:00:00 2001 From: Charlotte Avery Date: Wed, 20 Nov 2024 10:49:28 +0000 Subject: [PATCH 4/4] Make functionality clearer --- simulation/model.py | 72 +++++++++++++++++++++++----------- simulation/tests/test_model.py | 5 --- 2 files changed, 50 insertions(+), 27 deletions(-) diff --git a/simulation/model.py b/simulation/model.py index ec14f9d..11a0d3d 100644 --- a/simulation/model.py +++ b/simulation/model.py @@ -209,25 +209,55 @@ def increment_timestep(self): self.households_heat_pump_aware_at_current_step = 0 -def construct_population_awareness( +def population_heat_pump_awareness( household_population: pd.DataFrame, heat_pump_awareness: float ) -> List[bool]: - """Construct a list assigning heat pump awareness to each agent in the population""" - # Randomly assign heat pump awareness to the agent population with a probability given by `heat_pump_awareness` + """Randomly assign heat pump awareness to each agent in the population + with a probability given by input param `heat_pump_awareness` + + Args: + household_population (pd.DataFrame): household population + heat_pump_awareness (float): probability of household becoming heat + pump aware + + Returns: + List[bool]: list of length equal to the number of households in the + population of True/False values depending on whether the household + is heat pump aware or not + """ return [ random.random() < heat_pump_awareness for _ in range(len(household_population)) ] -def construct_population_awareness_due_to_campaign( - population_heat_pump_awareness: List[bool], +def population_heat_pump_awareness_due_to_campaign( + agents_heat_pump_awareness: List[bool], heat_pump_awareness_due_to_campaign: float, heat_pump_awareness: float, ) -> List[bool]: - """Construct a list assigning heat pump awareness to each agent in the population after the campaign""" - - # Awareness cannot decrease over time - # Ensure that the campaign target awareness is at least the current awareness + """If heat pump campaign is set as intervention in input params to the ABM, + then some agents who are not initially heat pump aware will later become + heat pump aware at the time of the campaign. + This function randomly designates agents to become heat pump aware due to + the campaign. + The probability of an agent becoming heat pump aware due to the campaign + is given by the input param `heat_pump_awareness_due_to_campaign`. + + Args: + agents_heat_pump_awareness (List[bool]): agent awareness of heat pumps + at the beginning of the simulation + heat_pump_awareness_due_to_campaign (float): probability of household + becoming heat pump aware due to campaign intervention + heat_pump_awareness (float): probability of a household being + initially heat pump aware irrespecictive the campaign intervention + + Returns: + List[bool]: list of length equal to the number of households in the + population of True/False values depending on whether the household + will become heat pump aware or not due to the campaign intervention + """ + # Ensure that the campaign target awareness is at least the initial awareness + # If a user sets a lower target awareness than the initial awareness this is the same as the campaign having no effect on heat pump awareness heat_pump_awareness_due_to_campaign = max( heat_pump_awareness_due_to_campaign, heat_pump_awareness ) @@ -244,17 +274,15 @@ def construct_population_awareness_due_to_campaign( heat_pump_awareness_due_to_campaign - heat_pump_awareness ) / unaware_population_fraction - population_heat_pump_awareness_after_campaign = ( - population_heat_pump_awareness.copy() - ) + agents_heat_pump_awareness_after_campaign = agents_heat_pump_awareness.copy() # Randomly assign heat pump awareness to the initially non-aware agent population - for i in range(len(population_heat_pump_awareness_after_campaign)): - if not population_heat_pump_awareness_after_campaign[i]: + for i in range(len(agents_heat_pump_awareness_after_campaign)): + if not agents_heat_pump_awareness_after_campaign[i]: rand_gen = random.random() if rand_gen < target_heat_pump_awareness_for_campaign: - population_heat_pump_awareness_after_campaign[i] = True + agents_heat_pump_awareness_after_campaign[i] = True - return population_heat_pump_awareness_after_campaign + return agents_heat_pump_awareness_after_campaign def create_household_agents( @@ -265,12 +293,12 @@ def create_household_agents( heat_pump_awareness_due_to_campaign: float, ) -> Iterator[Household]: - population_heat_pump_awareness = construct_population_awareness( + agents_heat_pump_awareness = population_heat_pump_awareness( household_population, heat_pump_awareness ) - population_heat_pump_awareness_after_campaign = ( - construct_population_awareness_due_to_campaign( - population_heat_pump_awareness, + agents_heat_pump_awareness_after_campaign = ( + population_heat_pump_awareness_due_to_campaign( + agents_heat_pump_awareness, heat_pump_awareness_due_to_campaign, heat_pump_awareness, ) @@ -305,8 +333,8 @@ 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=population_heat_pump_awareness[i], - is_heat_pump_aware_after_campaign=population_heat_pump_awareness_after_campaign[ + is_heat_pump_aware=agents_heat_pump_awareness[i], + is_heat_pump_aware_after_campaign=agents_heat_pump_awareness_after_campaign[ i ], ) diff --git a/simulation/tests/test_model.py b/simulation/tests/test_model.py index ada6040..8a46cbb 100644 --- a/simulation/tests/test_model.py +++ b/simulation/tests/test_model.py @@ -276,17 +276,12 @@ def test_create_household_agents(self) -> None: heat_pump_awareness = 0.4 heat_pump_awareness_due_to_campaign = 0.8 - # population_heat_pump_awareness = construct_population_awareness(household_population, heat_pump_awareness) - # population_heat_pump_awareness_after_campaign = construct_population_awareness_due_to_campaign(household_population, heat_pump_awareness, heat_pump_awareness_due_to_campaign) - household_agents = create_household_agents( self.household_population, self.simulation_start_datetime, self.all_agents_heat_pump_suitable, heat_pump_awareness, heat_pump_awareness_due_to_campaign, - # population_heat_pump_awareness, - # population_heat_pump_awareness_after_campaign ) household = next(household_agents)