diff --git a/simulation/__main__.py b/simulation/__main__.py index a2a030a..b5f145e 100644 --- a/simulation/__main__.py +++ b/simulation/__main__.py @@ -4,7 +4,6 @@ import random import sys import uuid -import json from functools import partial import pandas as pd @@ -46,13 +45,6 @@ def map_string_to_datetime_float_tuple(date_price_discount_string): date, price_discount = date_price_discount_string.split(":") return datetime.datetime.strptime(date, "%Y-%m-%d"), float(price_discount) - def map_string_to_datetime_float_dict(date_target_awareness_string): - date_target_awareness = json.loads(date_target_awareness_string) - date_target_awareness_dict = {} - for date, awareness in date_target_awareness.items(): - date_target_awareness_dict[float(awareness)] = datetime.datetime.strptime(date, "%Y-%m-%d") - return date_target_awareness_dict - parser = argparse.ArgumentParser() households = parser.add_mutually_exclusive_group(required=True) @@ -189,9 +181,9 @@ def check_string_is_isoformat_datetime(string) -> str: parser.add_argument( "--campaign-target-heat-pump-awareness-date", action="append", - type=map_string_to_datetime_float_dict, + type=map_string_to_datetime_float_tuple, help="A factor by which heat pump awareness will increase by a specified date.", - metavar="{YYYY-MM-DD:heat_pump_awareness}", + metavar="YYYY-MM-DD:heat_pump_awareness", ) return parser.parse_args(args) @@ -203,13 +195,18 @@ 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}" ) - print(args.campaign_target_heat_pump_awareness_date) if args.campaign_target_heat_pump_awareness_date is not None: # Check that target awareness inputs increase over the model horizon - campaigns = args.campaign_target_heat_pump_awareness_date[0] - campaigns[args.heat_pump_awareness] = args.start_datetime - campaigns = dict(sorted(campaigns.items())) - increasing_awareness = sorted(list(campaigns.values())) == list(campaigns.values()) + campaigns = sorted(args.campaign_target_heat_pump_awareness_date) + _, awareness_factors = zip(*campaigns) + awareness_factors = list(awareness_factors) + awareness_factors.insert(0, args.heat_pump_awareness) + increasing_awareness = all( + [ + awareness_factors[i - 1] < awareness_factors[i] + for i in range(1, len(awareness_factors)) + ] + ) if not increasing_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_date}, heat_pump_awareness:{args.heat_pump_awareness}" diff --git a/simulation/model.py b/simulation/model.py index a89b86f..29ccb3c 100644 --- a/simulation/model.py +++ b/simulation/model.py @@ -81,9 +81,11 @@ def __init__( self.annual_new_builds = annual_new_builds self.heat_pump_awareness = heat_pump_awareness self.heat_pump_awareness_campaign_schedule = ( - heat_pump_awareness_campaign_schedule[0] if heat_pump_awareness_campaign_schedule + sorted(heat_pump_awareness_campaign_schedule) + if heat_pump_awareness_campaign_schedule else None ) + 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 @@ -217,14 +219,17 @@ def heat_pump_awareness_at_timestep(self) -> float: def campaign_target_heat_pump_awareness(self) -> float: if self.heat_pump_awareness_campaign_schedule: - first_campaign_date = self.heat_pump_awareness_campaign_schedule[min(self.heat_pump_awareness_campaign_schedule, key=self.heat_pump_awareness_campaign_schedule.get)] - current_date_precedes_first_campaign_date = self.current_datetime < first_campaign_date + + step_dates, awareness_factors = zip( + *self.heat_pump_awareness_campaign_schedule + ) + + index = bisect(step_dates, self.current_datetime) + current_date_precedes_first_campaign_date = index == 0 + if current_date_precedes_first_campaign_date: return self.heat_pump_awareness - else: - campaigns_begun = {awareness : date for (awareness, date) in self.heat_pump_awareness_campaign_schedule.items() if date <= self.current_datetime} - current_campaign = max(campaigns_begun, key=campaigns_begun.get) - return current_campaign + return awareness_factors[index - 1] return self.heat_pump_awareness diff --git a/simulation/tests/test_agents.py b/simulation/tests/test_agents.py index 8160af3..216cc05 100644 --- a/simulation/tests/test_agents.py +++ b/simulation/tests/test_agents.py @@ -706,7 +706,9 @@ def test_heat_pump_awareness_updated_after_successful_campaign( step_interval=relativedelta(months=1), interventions=[InterventionType.HEAT_PUMP_CAMPAIGN], heat_pump_awareness=0.0, - heat_pump_awareness_campaign_schedule=[{1.0: datetime.datetime(2025, 3, 1)}], + heat_pump_awareness_campaign_schedule=[ + (datetime.datetime(2025, 3, 1), 1.0) + ], ) agent = household_factory(is_heat_pump_aware=False) model.add_agents([agent]) @@ -751,7 +753,9 @@ def test_heat_pump_awareness_does_not_increase_when_campaign_target_is_same_as_c start_datetime=datetime.datetime(2025, 1, 1), step_interval=relativedelta(months=1), interventions=[InterventionType.HEAT_PUMP_CAMPAIGN], - heat_pump_awareness_campaign_schedule=[{0.0: datetime.datetime(2025, 2, 1)}], + heat_pump_awareness_campaign_schedule=[ + (datetime.datetime(2025, 2, 1), 0.0) + ], heat_pump_awareness=0.0, population_heat_pump_awareness=[False], ) diff --git a/simulation/tests/test_main.py b/simulation/tests/test_main.py index 1f44e2b..464c28e 100644 --- a/simulation/tests/test_main.py +++ b/simulation/tests/test_main.py @@ -300,7 +300,7 @@ def test_campaign_target_less_than_heat_pump_awareness_raises_value_error( [ *mandatory_local_args, "--campaign-target-heat-pump-awareness-date", - '{"2024-03-01":0.1}', + "2024-03-01:0.1", "--heat-pump-awareness", "0.5", ] diff --git a/simulation/tests/test_model.py b/simulation/tests/test_model.py index 4917525..5bdb9d4 100644 --- a/simulation/tests/test_model.py +++ b/simulation/tests/test_model.py @@ -261,7 +261,8 @@ def test_heat_pump_awareness_changes_when_crosses_campaign_schedule_date( start_datetime=datetime.datetime(2024, 2, 1), heat_pump_awareness=0.25, heat_pump_awareness_campaign_schedule=[ - {0.5: datetime.datetime(2024, 2, 2), 0.7: datetime.datetime(2024, 2, 3)} + (datetime.datetime(2024, 2, 2), 0.5), + (datetime.datetime(2024, 2, 3), 0.7), ], step_interval=datetime.timedelta(minutes=1440), ) @@ -374,7 +375,9 @@ def test_all_household_agents_become_heat_pump_aware_with_100_per_cent_campaign_ step_interval=relativedelta(months=1), interventions=[InterventionType.HEAT_PUMP_CAMPAIGN], heat_pump_awareness=0.0, - heat_pump_awareness_campaign_schedule={campaign_target_heat_pump_awareness: datetime.datetime(2025, 2, 1)}, + heat_pump_awareness_campaign_schedule=[ + (datetime.datetime(2025, 2, 1), campaign_target_heat_pump_awareness) + ], population_heat_pump_awareness=population_heat_pump_awareness, ) model.add_agents([household_agents])