Source code for pandaprosumer.controller.models.heat_storage

"""
Module containing the HeatStorageController class.
"""

import numpy as np
import pandas as pd

from pandaprosumer.controller.base import BasicProsumerController


[docs] class HeatStorageController(BasicProsumerController): """ Controller for heat storage systems. """ @classmethod def name(cls): return "heat_storage" def __init__(self, prosumer, heat_storage_object, order, level, init_soc=0., in_service=True, index=None, **kwargs): """ Initializes the HeatStorageController. :param prosumer: The prosumer object :param heat_storage_object: The heat storage object :param order: The order of the controller :param level: The level of the controller :param init_soc: Initial state of charge :param in_service: The in-service status of the controller :param index: The index of the controller :param kwargs: Additional keyword arguments """ super().__init__(prosumer, heat_storage_object, order=order, level=level, in_service=in_service, index=index, **kwargs) self._soc = float(init_soc)
[docs] def q_to_receive_kw(self, prosumer): """ Calculates the heat to receive in kW. :param prosumer: The prosumer object :return: Heat to receive in kW """ self.applied = False _q_capacity_kwh = self._get_element_param(prosumer, "q_capacity_kwh") fill_level_kwh = min(self._soc, 1) * _q_capacity_kwh q_to_receive_kw = (_q_capacity_kwh - fill_level_kwh) * 3600 / self.resol q_to_receive_kw += self.q_to_deliver_kw(prosumer) if not np.isnan(self._get_input('q_received_kw')): # If there is already some power in the input, don't require it again q_received_kw = self._get_input('q_received_kw') q_to_receive_kw -= q_received_kw q_to_receive_kw = max(0., q_to_receive_kw) return q_to_receive_kw
[docs] def q_to_deliver_kw(self, prosumer): """ Calculates the heat to deliver in kW. :param prosumer: The prosumer object :return: Heat to deliver in kW """ q_to_deliver_kw = 0. for responder in self._get_generic_mapped_responders(prosumer): q_to_deliver_kw += responder.q_to_receive_kw(prosumer) return q_to_deliver_kw
[docs] def control_step(self, prosumer): """ Executes the control step for the controller. :param prosumer: The prosumer object """ super().control_step(prosumer) q_to_deliver_kw = self.q_to_deliver_kw(prosumer) _q_capacity_kwh = self._get_element_param(prosumer, "q_capacity_kwh") e_received_kwh = self._get_input("q_received_kw") * self.resol / 3600 potential_kwh = self._soc * _q_capacity_kwh + e_received_kwh demand_kwh = q_to_deliver_kw * self.resol / 3600 if demand_kwh > potential_kwh: # Cannot meet the demand demand_kwh = potential_kwh if not isinstance(demand_kwh, np.ndarray) and demand_kwh == 0: demand_kwh = np.array([0.]) / self.resol / 3600 fill_level_kwh = potential_kwh - demand_kwh excess_energy_kwh = max(0, fill_level_kwh - _q_capacity_kwh) if excess_energy_kwh > 0: raise ValueError(f"Excess energy detected: {excess_energy_kwh} kWh exceeds the maximum capacity.") self._soc = fill_level_kwh / _q_capacity_kwh demand_kw = demand_kwh / (self.resol / 3600) assert 0 <= self._soc <= 1, (f"SOC = {self._soc} invalid for controller {self.name} in prosumer {prosumer.name}" f"at timestep {self.time}") result = np.array([pd.Series(self._soc), pd.Series(demand_kw)]) self.finalize(prosumer, result.T) self.applied = True