-
Notifications
You must be signed in to change notification settings - Fork 1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Allow for sudden increase in the heat pump awareness #148
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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 | ||
|
||
|
@@ -277,6 +281,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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this the right variable? Should you have the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is referring to the property |
||
|
||
|
||
def is_first_timestep(model: "DomesticHeatingABM") -> bool: | ||
return model.current_datetime == model.start_datetime + model.step_interval | ||
|
||
|
@@ -304,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, | ||
|
@@ -352,6 +363,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), | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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 | ||
|
@@ -73,14 +74,20 @@ 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 | ||
self.heat_pump_awareness_campaign_date = heat_pump_awareness_campaign_date | ||
|
||
super().__init__(UnorderedSpace()) | ||
|
||
@property | ||
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,15 +206,105 @@ 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 population_heat_pump_awareness( | ||
household_population: pd.DataFrame, heat_pump_awareness: float | ||
) -> List[bool]: | ||
"""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 population_heat_pump_awareness_due_to_campaign( | ||
agents_heat_pump_awareness: List[bool], | ||
heat_pump_awareness_due_to_campaign: float, | ||
heat_pump_awareness: float, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just to check:
This function basically tries to select the people who are currently not yet aware, and randomly assign households s.t. after the campaign the total number of 'aware' household = specified target? If i'm summarising correctly, please could you:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes your understanding is correct. I will update the doc strings. |
||
) -> List[bool]: | ||
"""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 | ||
) | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we need to go through all these trouble? Why couldn't we simply set My understanding of
If you want to model heat pump awareness that will increase over time due to the campaign, then shouldn't all this logic live in collector.py, rather than right at the beginning? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Although I see that in Which suggests that this In which case:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. And if my guess of what you're trying to implement is correct, I feel like it would be more natural to implement this logic inside All this logic could have been in There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The reason I have defined awareness in Given that we can access the full population statistics in |
||
# 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 | ||
|
||
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(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: | ||
agents_heat_pump_awareness_after_campaign[i] = True | ||
|
||
return agents_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(): | ||
|
||
agents_heat_pump_awareness = population_heat_pump_awareness( | ||
household_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, | ||
) | ||
) | ||
|
||
for i, household in enumerate(household_population.itertuples()): | ||
yield Household( | ||
id=household.id, | ||
location=household.location, | ||
|
@@ -236,7 +333,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=agents_heat_pump_awareness[i], | ||
is_heat_pump_aware_after_campaign=agents_heat_pump_awareness_after_campaign[ | ||
i | ||
], | ||
) | ||
|
||
|
||
|
@@ -263,6 +363,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( | ||
|
@@ -282,13 +384,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) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the
self.is_heat_pump_aware
logic is: either (already has heat pump) or (is heat pump aware) because by definition, if they have a heat pump, they must be heat pump aware.I'm not sure if the same logic follows into
is heat pump aware after campaign
:will revisit this comment later
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
After having read the entire PR, I now understand this variable to be
latent variable
that gets activated later on if the campaign date is reached. But i'm still unsure if we need this variable to implement the campaign effect..