Skip to content

Commit

Permalink
Treat HP campaign args same as HP discount args (#151)
Browse files Browse the repository at this point in the history
* Treat HP campaign args same as HP discount args

* Update BUS grant cap
  • Loading branch information
charlotte-avery authored Dec 2, 2024
1 parent 5b8fdaa commit 787504d
Show file tree
Hide file tree
Showing 9 changed files with 162 additions and 49 deletions.
30 changes: 26 additions & 4 deletions k8s/job.jsonnet
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,32 @@ local job(name, args_excl_output) = {
'extended_boiler_upgrade_scheme',
'--intervention',
'heat_pump_campaign',
'--campaign-target-heat-pump-awareness',
'0.8',
'--heat-pump-awareness-campaign-date',
'2028-01-01',
'--campaign-target-heat-pump-awareness-date',
'2028-01-01:0.5',
'--campaign-target-heat-pump-awareness-date',
'2034-01-01:0.75',
'--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.25',
'--price-gbp-per-kwh-gas',
'0.0682',
'--price-gbp-per-kwh-electricity',
'0.182',
'--heat-pump-installer-count',
'10000000000'
]),
job('01b-%s-max-policy-extended-bus' % std.extVar('SHORT_SHA'), [
'--intervention',
'extended_boiler_upgrade_scheme',
'--intervention',
'heat_pump_campaign',
'--campaign-target-heat-pump-awareness-date',
'2028-01-01:0.75',
'--intervention',
'gas_oil_boiler_ban',
'--gas-oil-boiler-ban-date',
Expand Down
55 changes: 41 additions & 14 deletions simulation/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -179,30 +179,58 @@ def check_string_is_isoformat_datetime(string) -> str:
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,
"--campaign-target-heat-pump-awareness-date",
action="append",
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",
)

return parser.parse_args(args)


def check_parsed_target_heat_pump_awareness(
campaigns: dict, initial_awareness: float
) -> bool:
"""Determine whether target heat pump awareness increases over the model horizon
Args:
campaigns (dict): modelled heat pump awareness campaigns
initial_awareness (float): initial (t=0) heat pump awareness
Returns:
bool: True if target awareness values are set to increase over model horizon
"""

campaigns_date_ordered = sorted(campaigns)
_, awareness_factors = zip(*campaigns_date_ordered)
awareness_factors = list(awareness_factors)
awareness_factors.insert(0, initial_awareness)

increasing_awareness = all(
[
awareness_factors[i - 1] < awareness_factors[i]
for i in range(1, len(awareness_factors))
]
)
return increasing_awareness


def validate_args(args):
if args.gas_oil_boiler_ban_announce_date > args.gas_oil_boiler_ban_date:
raise ValueError(
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 args.campaign_target_heat_pump_awareness_date is not None:
# Check that target awareness inputs increase over the model horizon
increasing_awareness = check_parsed_target_heat_pump_awareness(
args.campaign_target_heat_pump_awareness_date, args.heat_pump_awareness
)
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}"
)


if __name__ == "__main__":
Expand Down Expand Up @@ -243,8 +271,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.campaign_target_heat_pump_awareness,
args.heat_pump_awareness_campaign_date,
args.campaign_target_heat_pump_awareness_date,
)

with smart_open.open(args.history_file, "w") as file:
Expand Down
1 change: 0 additions & 1 deletion simulation/agents.py
Original file line number Diff line number Diff line change
Expand Up @@ -633,7 +633,6 @@ def proba_of_becoming_heat_pump_aware_required_to_reach_campaign_target(
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
Expand Down
22 changes: 11 additions & 11 deletions simulation/costs.py
Original file line number Diff line number Diff line change
Expand Up @@ -157,20 +157,20 @@

# Grant cap is set to £250M for 2024-’25, SOURCE: https://www.gov.uk/government/publications/boiler-upgrade-scheme-budget-increase-and-approval-to-over-allocate-vouchers/approval-to-increase-the-budget-and-over-allocate-vouchers-for-the-boiler-upgrade-scheme-november-2024
# Grant cap is set to £1.54B for 2025-’28, SOURCE: https://www.gov.uk/government/news/families-business-and-industry-to-get-energy-efficiency-support
# Assume limited spend of £515M per annum during 2025-’28, unless the grant was underspent in year in which case the underspent amount gets carried forward to the future years
# Assume £515M PA grant will be extended to 2035.
# Assume limited spend of per annum during 2025-’28, unless the grant was underspent in year in which case the underspent amount gets carried forward to the future years
# Assume grant will be extended to 2035.
BOILER_UPGRADE_SCHEME_GRANT_CAP = {
2024: 250_000_000,
2025: 765_000_000,
2026: 1280_000_000,
2025: 545_000_000,
2026: 1025_000_000,
2027: 1795_000_000,
2028: 2310_000_000,
2029: 2825_000_000,
2030: 3340_000_000,
2031: 3855_000_000,
2032: 4370_000_000,
2033: 4885_000_000,
2034: 5400_000_000,
2028: 2565_000_000,
2029: 3335_000_000,
2030: 4105_000_000,
2031: 4875_000_000,
2032: 5645_000_000,
2033: 6415_000_000,
2034: 7185_000_000,
}


Expand Down
38 changes: 30 additions & 8 deletions simulation/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,9 @@ def __init__(
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,
heat_pump_awareness_campaign_schedule: Optional[
List[Tuple[datetime.datetime, float]]
],
population_heat_pump_awareness: List[bool],
):
self.start_datetime = start_datetime
Expand Down Expand Up @@ -79,8 +80,11 @@ 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.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)
Expand Down Expand Up @@ -211,6 +215,24 @@ def heat_pump_awareness_at_timestep(self) -> float:
+ self.num_households_switching_to_heat_pump_aware
) / self.household_count

@property
def campaign_target_heat_pump_awareness(self) -> float:

if self.heat_pump_awareness_campaign_schedule:

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
return awareness_factors[index - 1]

return self.heat_pump_awareness

def increment_timestep(self):
self.current_datetime += self.step_interval
self.boiler_upgrade_scheme_cumulative_spend_gbp += (
Expand Down Expand Up @@ -282,8 +304,9 @@ 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,
heat_pump_awareness_campaign_schedule: Optional[
List[Tuple[datetime.datetime, float]]
],
):

population_heat_pump_awareness = [
Expand All @@ -308,8 +331,7 @@ def create_and_run_simulation(
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,
heat_pump_awareness_campaign_schedule=heat_pump_awareness_campaign_schedule,
population_heat_pump_awareness=population_heat_pump_awareness,
)

Expand Down
3 changes: 1 addition & 2 deletions simulation/tests/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,7 @@ def model_factory(**model_attributes):
"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),
"heat_pump_awareness_campaign_schedule": None,
"population_heat_pump_awareness": [],
}

Expand Down
11 changes: 7 additions & 4 deletions simulation/tests/test_agents.py
Original file line number Diff line number Diff line change
Expand Up @@ -705,8 +705,10 @@ def test_heat_pump_awareness_updated_after_successful_campaign(
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,
heat_pump_awareness=0.0,
heat_pump_awareness_campaign_schedule=[
(datetime.datetime(2025, 3, 1), 1.0)
],
)
agent = household_factory(is_heat_pump_aware=False)
model.add_agents([agent])
Expand Down Expand Up @@ -751,9 +753,10 @@ 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_date=datetime.datetime(2025, 2, 1),
heat_pump_awareness_campaign_schedule=[
(datetime.datetime(2025, 2, 1), 0.0)
],
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)
Expand Down
16 changes: 13 additions & 3 deletions simulation/tests/test_main.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,11 @@

import simulation.__main__
from abm import read_jsonlines
from simulation.__main__ import parse_args, validate_args
from simulation.__main__ import (
check_parsed_target_heat_pump_awareness,
parse_args,
validate_args,
)
from simulation.constants import InterventionType


Expand Down Expand Up @@ -299,11 +303,17 @@ def test_campaign_target_less_than_heat_pump_awareness_raises_value_error(
args = parse_args(
[
*mandatory_local_args,
"--campaign-target-heat-pump-awareness",
"0.1",
"--campaign-target-heat-pump-awareness-date",
"2024-03-01:0.1",
"--heat-pump-awareness",
"0.5",
]
)

increasing_awareness = check_parsed_target_heat_pump_awareness(
args.campaign_target_heat_pump_awareness_date, args.heat_pump_awareness
)
assert not increasing_awareness

with pytest.raises(ValueError):
validate_args(args)
35 changes: 33 additions & 2 deletions simulation/tests/test_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,36 @@ def test_model_installs_heat_pumps_in_existing_builds_when_there_is_capacity(sel
assert 0 < capacity_new_build < capacity_existing_build
assert model.has_heat_pump_installation_capacity

def test_heat_pump_awareness_campaign_is_intial_awareness_if_no_campaign_schedule_passed(
self,
):

model = model_factory(heat_pump_awareness_campaign_schedule=None)

assert model.campaign_target_heat_pump_awareness == model.heat_pump_awareness

def test_heat_pump_awareness_changes_when_crosses_campaign_schedule_date(
self,
):

model = model_factory(
start_datetime=datetime.datetime(2024, 2, 1),
heat_pump_awareness=0.25,
heat_pump_awareness_campaign_schedule=[
(datetime.datetime(2024, 2, 2), 0.5),
(datetime.datetime(2024, 2, 3), 0.7),
],
step_interval=datetime.timedelta(minutes=1440),
)

assert model.campaign_target_heat_pump_awareness == 0.25

model.increment_timestep()
assert model.campaign_target_heat_pump_awareness == 0.5

model.increment_timestep()
assert model.campaign_target_heat_pump_awareness == 0.7


class test_household_agents:

Expand Down Expand Up @@ -344,9 +374,10 @@ def test_all_household_agents_become_heat_pump_aware_with_100_per_cent_campaign_
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,
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])
Expand Down

0 comments on commit 787504d

Please sign in to comment.