Source code for interventions.Subsidy

"""
Defines the data structures for subsidies applicable to heating systems.

This module uses Python's `dataclasses` to create a representation 
for various subsidies. It includes a base `Subsidy` class that
defines the common attributes, such as the subsidy amount and any application
conditions.

Several specific subclasses are defined for different types of subsidies. 
These objects are used throughout the model to calculate the final, 
subsidized price of a new heating system.

:Authors:
 - Sören Lohr
 - Ivan Digel <ivan.digel@uni-kassel.de>
"""
from dataclasses import dataclass, field
from typing import Callable, Optional, Any
from helpers.config import settings


[docs] @dataclass class Subsidy: """ A dataclass representing a generic subsidy for a heating system. This class serves as the primary data structure for all subsidies. It holds information about the subsidy's name, amount, and the system(s) it applies to. It also supports conditional application, allowing a subsidy to be contingent on specific properties of the agent or the heating system itself. Attributes ---------- name : str The full name of the subsidy. abbr : str A short abbreviation for the subsidy. subsidy : float The subsidy amount, typically as a decimal fraction of the total cost (e.g., 0.3 for 30%). heating_system : str or tuple[str, ...] The name(s) of the heating system classes this subsidy can apply to. condition : Callable[[Any], bool], optional A callable (e.g., a lambda function) that returns True if the subsidy conditions are met, by default None. target : str, optional Specifies whether the `condition` should be applied to the 'System' or the 'Agent', by default None. """ name: str abbr: str subsidy: float heating_system: str condition: Optional[Callable[[Any], bool]] = field(default=None) target: Optional[str] = field(default=None)
[docs] def check_condition(self, system, agent): """ Checks if the conditions for this subsidy are met. This method evaluates the `condition` function against the specified `target` (either the heating system or the agent). If no condition is defined, it automatically returns True. Parameters ---------- system : Heating_system The heating system being considered for the subsidy. agent : Houseowner The agent applying for the subsidy. Returns ------- bool True if the subsidy is applicable, False otherwise. """ if self.condition is None: return True elif self.target == "System": return self.condition(system) elif self.target == "Agent": return self.condition(agent) return False
[docs] @dataclass class Subsidy_district(Subsidy): """ A subsidy specifically for district heating connections. """ name: str = "District" abbr: str = "DSTR" subsidy: float = 0.3 heating_system: str = "Heating_system_network_district" condition: None target: str = None def __post_init__(self): """ Update the subsidy amount from the global settings file. """ self.subsidy = float(settings.subsidies.district)
[docs] @dataclass class Subsidy_heat_pump(Subsidy): """ A subsidy specifically for air-source heat pumps. """ name: str = "Heat_pump" abbr: str = "HTPMP" subsidy: float = 0.3 heating_system: str = "Heating_system_heat_pump" condition: None target: str = None def __post_init__(self): """ Update the subsidy amount from the global settings file. """ self.subsidy = float(settings.subsidies.heat_pump)
[docs] @dataclass class Subsidy_heat_pump_brine(Subsidy): """ A subsidy specifically for heat pump-based local cold network. """ name: str = "Heat_pump_brine" abbr: str = "HTPMPBR" subsidy: float = 0.3 heating_system: str = "Heating_system_heat_pump_brine" condition: None target: str = None def __post_init__(self): """ Update the subsidy amount from the global settings file. """ self.subsidy = float(settings.subsidies.heat_pump_brine)
[docs] @dataclass class Subsidy_pellet(Subsidy): """ A subsidy specifically for pellet heating. """ name: str = "Pellet" abbr: str = "PLLT" subsidy: float = 0.3 heating_system: str = "Heating_system_pellet" condition: None target: str = None def __post_init__(self): """ Update the subsidy amount from the global settings file. """ self.subsidy = float(settings.subsidies.pellet)
[docs] @dataclass class Subsidy_network_local(Subsidy): """ A subsidy specifically for local hot network. """ name: str = "Hot_network" abbr: str = "HTNTWRK" subsidy: float = 0.3 heating_system: str = "Heating_system_network_local" condition: None target: str = None def __post_init__(self): """ Update the subsidy amount from the global settings file. """ self.subsidy = float(settings.subsidies.network_local_hot)
[docs] @dataclass class Subsidy_GP_Joule(Subsidy): """ A subsidy specifically for local hot network provided by GP Joule. """ name: str = "GP_Joule" abbr: str = "GPJL" subsidy: float = 0.3 heating_system: str = "Heating_system_GP_Joule" condition: None target: str = None def __post_init__(self): """ Update the subsidy amount from the global settings file. """ self.subsidy = float(settings.subsidies.GP_Joule)
[docs] @dataclass class Subsidy_climate_speed(Subsidy): """ A bonus subsidy for replacing an oil or gas heating system. This subsidy acts as a bonus for houseowners who switch from a fossil-fuel system (oil or gas) to a renewable alternative. """ name: str = "Climate_speed" abbr: str = "CLMSPD" subsidy: float = 0.2 heating_system: tuple[str, ...] = ( "Heating_system_heat_pump", "Heating_system_heat_pump_brine", "Heating_system_pellet" ) condition: Callable[[Any], bool] = field( default_factory=lambda: lambda a: type(a.house.current_heating).__name__ in ["Heating_system_oil", "Heating_system_gas"] ) target: str = "Agent" def __post_init__(self): """ Update the subsidy amount from the global settings file. """ self.subsidy = float(settings.subsidies.climate_speed)
[docs] @dataclass class Subsidy_income(Subsidy): """ A subsidy for low-income households. This subsidy can be applied to any heating system but is conditional on the agent's annual income being below a specific threshold. """ name: str = "Income" abbr: str = "INC" subsidy: float = 0.3 heating_system: tuple[str, ...] = ( "Heating_system_heat_pump", "Heating_system_heat_pump_brine", "Heating_system_pellet" ) condition: Callable[[Any], bool] = field( default_factory=lambda: lambda a: 52*a.income <= settings.subsidies.low_income_threshold ) target: str = "Agent" def __post_init__(self): """ Update the subsidy amount from the global settings file. """ self.subsidy = float(settings.subsidies.income)
[docs] @dataclass class Subsidy_efficiency(Subsidy): """ A bonus subsidy for highly efficient heat pump systems. """ name: str = "Efficiency" abbr: str = "EFF" subsidy: float = 0.05 heating_system: tuple[str, ...] = ( "Heating_system_heat_pump", "Heating_system_heat_pump_brine" ) condition: None target: str = None def __post_init__(self): """ Update the subsidy amount from the global settings file. """ self.subsidy = float(settings.subsidies.efficiency)