Source code for modules.Information_sources

"""
This module provides the classes and logic for how agents gather information.
It features a base `Information_source` class that implements the core
information-seeking process, where an agent receives potentially biased and
uncertain data about a heating system.

Specific subclasses, such as `Information_source_internet` and
`Information_source_plumber`, represent different channels through which an
agent can seek information. Each source has its own characteristics, including
the scope of its knowledge (content), the cost to consult it, and the specific
interaction mechanics.

:Authors:
 - Ivan Digel <ivan.digel@uni-kassel.de>
 - Sascha Holzhauer <sascha.holzhauer@uni-kassel.de>
 - Dmytro Mykhailiuk <dmytromykhailiuk6@gmail.com>
"""
import math
from copy import deepcopy
from helpers.config import settings
from modules.Rng import rng_information_source_run
from interventions.Subsidy import *


[docs] class Information_source: """ A base class for a generic source of information. This class provides the structure and methods for all information sources in the model. It defines standard attributes like search cost and distortion levels and contains the core `data_search` method, which simulates the process of an agent acquiring and perceiving information. Attributes ---------- cost : int The cognitive resource cost for an agent to perform one search action. distortion : float The base (maximum) factor for distorting a heating system's true parameters. min_distortion : float The minimum distortion (for a 100% market share system). uncertainty_lower : float The lower bound for the uncertainty range applied to parameters. uncertainty_upper : float The upper bound for the uncertainty range applied to parameters. content : list or None A list of heating system names that this source can provide information on. """ def __init__(self): """ Initialises a generic Information_source instance. This constructor sets the default attributes for an information source, such as cost and distortion levels, by loading them from the global settings. """ # Other params self.cost = settings.decision_making_costs.get_data # This is the worst max_distortion (for a 0% market share tech) self.distortion = settings.information_source.distortion # This is the best maximum distortion (for a 100% market share tech) self.min_distortion = 0.1 self.uncertainty_lower = settings.information_source.uncertainty_lower # To be used for uncertainty ranges self.uncertainty_upper = settings.information_source.uncertainty_upper self.content = None # Default content is None, subclasses can override self.known_subsidies_by_hs = {} self.subsidy_finding_prob = settings.information_source.subsidy_finding_prob # returns a list of all subclasses
[docs] @classmethod def instantiate_subclasses(cls): """ Creates and returns an instance of each direct subclass. This factory method is a convenient way to get a list of all available information source objects in the simulation. Returns ------- list[Information_source] A list containing one instance of each subclass. """ return [subclass() for subclass in cls.__subclasses__()]
[docs] class Information_source_internet(Information_source): """ Represents the Internet as a source of information. """ def __init__(self): """ Initialises the Internet source. The Internet is configured to have knowledge of all heating systems and subsidies listed in the project settings. """ super().__init__() self.content = ( settings.heating_systems.list ) # List of options to provide to an agent self.known_subsidies_by_hs = {} self.organize_subsidies()
[docs] def organize_subsidies(self): """ Populate the source's knowledge of available subsidies. This method compiles a list of subsidies applicable to the heating systems that the source provides information about. """ subsidies_list = [Subsidy_pellet(), Subsidy_heat_pump(), Subsidy_heat_pump_brine(), Subsidy_network_local(), Subsidy_GP_Joule(), Subsidy_climate_speed(), Subsidy_income(), Subsidy_efficiency()] for subsidy in subsidies_list: if isinstance(subsidy.heating_system, tuple): for hs_name in self.content: if hs_name in subsidy.heating_system: self.known_subsidies_by_hs.setdefault(hs_name, []).append( deepcopy( Subsidy( name=subsidy.name, abbr=subsidy.abbr, subsidy=subsidy.subsidy, heating_system=hs_name, condition=subsidy.condition, target=subsidy.target ) ) ) if subsidy.heating_system == "Any": for hs_name in self.content: self.known_subsidies_by_hs.setdefault(hs_name, []).append( deepcopy( Subsidy( name=subsidy.name, abbr=subsidy.abbr, subsidy=subsidy.subsidy, heating_system=hs_name, condition=subsidy.condition, target=subsidy.target ) ) ) elif not isinstance(subsidy.heating_system, tuple): self.known_subsidies_by_hs.setdefault(subsidy.heating_system, []).append( deepcopy(subsidy) )
[docs] class Information_source_magazine(Information_source): """ Represents a professional magazine as a source of information. """ def __init__(self): """ Initialises the magazine source. A magazine is configured with a limited set of conventional heating systems that it can provide information about. """ super().__init__() self.content = [ "Heating_system_gas", "Heating_system_heat_pump", "Heating_system_electricity", "Heating_system_pellet", ] # List of options to provide to an agent self.known_subsidies_by_hs = {} self.organize_subsidies()
[docs] def organize_subsidies(self): """ Populates the source's knowledge of available subsidies. This method compiles a list of subsidies applicable to the heating systems that the source provides information about. """ subsidies_list = [Subsidy_pellet(), Subsidy_heat_pump(), Subsidy_heat_pump_brine(), Subsidy_network_local(), Subsidy_GP_Joule(), Subsidy_climate_speed(), Subsidy_income(), Subsidy_efficiency()] for subsidy in subsidies_list: if isinstance(subsidy.heating_system, tuple): for hs_name in self.content: if hs_name in subsidy.heating_system: self.known_subsidies_by_hs.setdefault(hs_name, []).append( deepcopy( Subsidy( name=subsidy.name, abbr=subsidy.abbr, subsidy=subsidy.subsidy, heating_system=hs_name, condition=subsidy.condition, target=subsidy.target ) ) ) if subsidy.heating_system == "Any": for hs_name in self.content: self.known_subsidies_by_hs.setdefault(hs_name, []).append( deepcopy( Subsidy( name=subsidy.name, abbr=subsidy.abbr, subsidy=subsidy.subsidy, heating_system=hs_name, condition=subsidy.condition, target=subsidy.target ) ) ) elif not isinstance(subsidy.heating_system, tuple): self.known_subsidies_by_hs.setdefault(subsidy.heating_system, []).append( deepcopy(subsidy) )
[docs] class Information_source_plumber(Information_source): """ Represents a plumber as a source of information. Not the plumber itself. This subclass facilitates exclusively the data search part of houseowner-plumber interaction. """ def __init__(self): """ Initializes the plumber information source. """ super().__init__() self.known_subsidies_by_hs = {}
[docs] class Information_source_neighbours(Information_source): """ Represents neighbours as a source of information. Not the neighbours themselves. This subclass facilitates exclusively the information search among neighbours. """ def __init__(self): """ Initialises the neighbours information source. """ super().__init__() self.known_subsidies_by_hs = {}
[docs] class Information_source_energy_advisor(Information_source): """ Represents an energy advisor as a source of information. Not the EA itself. This subclass facilitates exclusively the data search part of houseowner-EA interaction. """ def __init__(self): """ Initialises the energy advisor information source. """ super().__init__() self.known_subsidies_by_hs = {}
[docs] def generate_imperfect_system(agent, system_name): """ Create a perceived, imperfect representation of a heating system. This function simulates an agent's imperfect perception of a heating system. It takes a system name, creates an instance with its "true" attributes calculated for the agent's house, and then applies a random "pessimistic-only" distortion and an uncertainty range to its parameters. The distortion's upper bound is based on the system's market share. Parameters ---------- agent : Houseowner The agent for whom the system perception is being generated. system_name : str The name of the heating system class to generate. Returns ------- Heating_system The newly created `Heating_system` object with distorted and uncertain parameters. """ # Get distortion bounds max_distortion = settings.information_source.distortion # e.g., 0.3 min_distortion = 0.1 # This must match the value in Information_source.__init__ # Get market distribution from the agent's model distribution = agent.model.heating_distribution total_houses = sum(distribution.values()) # 1. Get market share for the chosen system market_share = 0.0 if total_houses > 0: market_share = distribution.get(system_name, 0) / total_houses # 2. Calculate this system's dynamic *top* distortion # Linear interpolation: maps [0, 1] -> [max_distortion, min_distortion] dynamic_top_distortion = min_distortion + (1.0 - market_share) * (max_distortion - min_distortion) # 3. Set distortion range bot_distortion = -dynamic_top_distortion top_distortion = dynamic_top_distortion uncertainty_lower = settings.information_source.uncertainty_lower uncertainty_upper = settings.information_source.uncertainty_upper new_system = agent.generate_system(system_name) new_system.calculate_all_attributes( area=agent.house.area, energy_demand=agent.house.energy_demand, heat_load=agent.house.heat_load ) for key in [ "operation_effort", "fuel_cost", "emissions", "price", "installation_effort", "opex", ]: random_noise = rng_information_source_run().uniform( bot_distortion, top_distortion ) distortion_factor = max(0.5, 1 + random_noise) # Multiply, then round up the value new_system.params[key][0] = new_system.params[key][0] * distortion_factor # Setting the uncertainty range (UNCHANGED) for key, value in new_system.params.items(): value[1] = value[0] * rng_information_source_run().uniform( uncertainty_lower, uncertainty_upper ) return new_system