diff --git a/k8s/job.jsonnet b/k8s/job.jsonnet index f0f5463..c4ad549 100644 --- a/k8s/job.jsonnet +++ b/k8s/job.jsonnet @@ -109,6 +109,8 @@ local job(name, args_excl_output) = { '0.0682', '--price-gbp-per-kwh-electricity', '0.182', + '--heat-pump-awareness-intervention-factor', + '0.05', ]), job('04a-%s-max-policy' % std.extVar('SHORT_SHA'), [ '--intervention', @@ -187,6 +189,8 @@ local job(name, args_excl_output) = { '0.0682', '--price-gbp-per-kwh-electricity', '0.182', + '--heat-pump-awareness-intervention-factor', + '0.05', '--include-new-builds', ]), job('05-%s-max-industry' % std.extVar('SHORT_SHA'), [ diff --git a/simulation/__main__.py b/simulation/__main__.py index ff7d1b0..9149368 100644 --- a/simulation/__main__.py +++ b/simulation/__main__.py @@ -146,6 +146,13 @@ def format_uuid(str): help="Include new build projections (from constants.py). Installers will also build heat pumps in new builds from 2025.", ) + parser.add_argument( + "--heat-pump-awareness-intervention-factor", + type=float_between_0_and_1, + default=0.1, + help="A value between 0 and 1 which determines how quickly heat pump awareness increases over time with the heat pump awareness intervention. A value of 0 indicates no increase in heat pump awareness.", + ) + def check_string_is_isoformat_datetime(string) -> str: datetime.datetime.fromisoformat(string) return string @@ -226,6 +233,7 @@ 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_intervention_factor, ) with smart_open.open(args.history_file, "w") as file: diff --git a/simulation/agents.py b/simulation/agents.py index d67a7a3..41077ba 100644 --- a/simulation/agents.py +++ b/simulation/agents.py @@ -37,7 +37,6 @@ RETROFIT_COSTS_SMALL_PROPERTY_SQM_LIMIT, SIGMOID_K, SIGMOID_OFFSET, - HEAT_PUMP_AWARENESS_GDT, BuiltForm, ConstructionYearBand, Element, @@ -102,7 +101,7 @@ def reverse_sigmoid(x: float, k: float = SIGMOID_K, offset: float = SIGMOID_OFFS return 1 / (1 + math.exp(k * (x + offset))) -def heat_pump_awareness_intervention(x: float, m: float = HEAT_PUMP_AWARENESS_GDT): +def heat_pump_awareness_intervention(x: float, m: float): return min(m * x, 1) @@ -422,11 +421,15 @@ def get_proba_rule_out_banned_heating_systems(self, model): return reverse_sigmoid(years_to_ban) - def get_proba_becomes_heat_pump_aware(self, model): + def get_proba_becomes_heat_pump_aware( + self, model, heat_pump_awareness_intervention_factor + ): years_since_start = (model.current_datetime - model.start_datetime).days / 365 - return heat_pump_awareness_intervention(years_since_start) + return heat_pump_awareness_intervention( + years_since_start, heat_pump_awareness_intervention_factor + ) def get_heating_system_options( self, model: "DomesticHeatingABM", event_trigger: EventTrigger @@ -456,7 +459,9 @@ def get_heating_system_options( # if awareness intervention used, allow for more agents to become aware of heat pumps if not self.is_heat_pump_aware: self.is_heat_pump_aware = true_with_probability( - self.get_proba_becomes_heat_pump_aware(model) + self.get_proba_becomes_heat_pump_aware( + model, model.heat_pump_awareness_intervention_factor + ) ) if not is_gas_oil_boiler_ban_announced: diff --git a/simulation/constants.py b/simulation/constants.py index 315321d..59eec29 100644 --- a/simulation/constants.py +++ b/simulation/constants.py @@ -62,7 +62,6 @@ class HeatingFuel(enum.Enum): MAX_BAN_LEAD_TIME_YEARS = 10 SIGMOID_K = 2 SIGMOID_OFFSET = 0 -HEAT_PUMP_AWARENESS_GDT = 0.1 class ConstructionYearBand(enum.Enum): diff --git a/simulation/model.py b/simulation/model.py index d4c0e2a..340eb32 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_intervention_factor: float, ): self.start_datetime = start_datetime self.step_interval = step_interval @@ -74,6 +75,9 @@ def __init__( ) self.heat_pump_installations_at_current_step = 0 self.annual_new_builds = annual_new_builds + self.heat_pump_awareness_intervention_factor = ( + heat_pump_awareness_intervention_factor + ) super().__init__(UnorderedSpace()) @@ -263,6 +267,7 @@ 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_intervention_factor: float, ): model = DomesticHeatingABM( @@ -282,6 +287,7 @@ 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_intervention_factor=heat_pump_awareness_intervention_factor, ) households = create_household_agents( diff --git a/simulation/tests/common.py b/simulation/tests/common.py index f79d42b..ef0cd5b 100644 --- a/simulation/tests/common.py +++ b/simulation/tests/common.py @@ -57,6 +57,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_intervention_factor": 0.0, } return DomesticHeatingABM(**{**default_values, **model_attributes}) diff --git a/simulation/tests/household_population.csv b/simulation/tests/household_population.csv index 0189697..be625c1 100644 --- a/simulation/tests/household_population.csv +++ b/simulation/tests/household_population.csv @@ -7,4 +7,4 @@ id,location,property_value_gbp,total_floor_area_m2,is_off_gas_grid,construction_ 5,BIRMINGHAM,500000,100,True,BUILT_PRE_1900,BUNGALOW,MID_TERRACE,BOILER_OIL,F,B,RENTED_SOCIAL,False,3,4,4,False 6,BIRMINGHAM,400000,190,False,BUILT_1900_1929,FLAT,END_TERRACE,BOILER_ELECTRIC,E,C,RENTED_PRIVATE,False,1,4,4,False 7,LONDON,300000,50,True,BUILT_1900_1929,FLAT,SEMI_DETACHED,BOILER_GAS,F,E,OWNER_OCCUPIED,True,2,1,3,True -8,MANCHESTER,600000,80,False,BUILT_PRE_1900,BUNGALOW,MID_TERRACE,BOILER_OIL,B,B,RENTED_PRIVATE,True,2,3,2,False +8,MANCHESTER,600000,80,False,BUILT_PRE_1900,BUNGALOW,MID_TERRACE,BOILER_OIL,B,B,RENTED_PRIVATE,True,2,3,2,False \ No newline at end of file diff --git a/simulation/tests/test_agents.py b/simulation/tests/test_agents.py index 8e4035b..cc80d67 100644 --- a/simulation/tests/test_agents.py +++ b/simulation/tests/test_agents.py @@ -707,12 +707,14 @@ def test_households_increasingly_likely_to_become_heat_pump_aware( ) proba_becomes_heat_pump_aware = household.get_proba_becomes_heat_pump_aware( - model + model, heat_pump_awareness_intervention_factor=0.1 ) model.increment_timestep() proba_becomes_heat_pump_aware_updated = ( - household.get_proba_becomes_heat_pump_aware(model) + household.get_proba_becomes_heat_pump_aware( + model, heat_pump_awareness_intervention_factor=0.1 + ) ) assert proba_becomes_heat_pump_aware < proba_becomes_heat_pump_aware_updated @@ -727,7 +729,7 @@ def test_heat_pump_awareness_increase_is_zero_in_first_year( ) proba_becomes_heat_pump_aware = household.get_proba_becomes_heat_pump_aware( - model + model, heat_pump_awareness_intervention_factor=0.1 ) assert proba_becomes_heat_pump_aware == 0 diff --git a/simulation/tests/test_main.py b/simulation/tests/test_main.py index 156a8a3..bf95547 100644 --- a/simulation/tests/test_main.py +++ b/simulation/tests/test_main.py @@ -99,6 +99,24 @@ def test_rented_heating_system_hassle_factor_must_be_between_0_and_1( [*mandatory_local_args, "--rented-heating-system-hassle-factor", "10"] ) + def test_heat_pump_awareness_intervention_factor(self, mandatory_local_args): + args = parse_args( + [*mandatory_local_args, "--rented-heating-system-hassle-factor", "0.05"] + ) + assert args.rented_heating_system_hassle_factor == 0.05 + + def test_heat_pump_awareness_intervention_factor_must_be_between_0_and_1( + self, mandatory_local_args + ): + with pytest.raises(SystemExit): + parse_args( + [ + *mandatory_local_args, + "--heat-pump-awareness-intervention-factor", + "10.0", + ] + ) + def test_help_flag(self): with pytest.raises(SystemExit): parse_args(["-h"])