Source code for pandaprosumer.controller.models.booster_heat_pump

import pandas as pd
import logging
import numpy as np

from pandaprosumer.controller.base import BasicProsumerController

logging.basicConfig(level=logging.WARNING)


"""
Module containing the HeatPumpController class.
"""


[docs] class BoosterHeatPumpController(BasicProsumerController): """ Controller for heat pumps. """ @classmethod def name(cls): return "booster_heat_pump" def __init__(self, prosumer, heat_pump_object, order, level, in_service=True, index=None, **kwargs): """ Initializes the HeatPumpController. :param prosumer: The prosumer object :param heat_pump_object: The heat pump object :param order: The order of the controller :param level: The level of the controller :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_pump_object, order=order, level=level, in_service=in_service, index=index, **kwargs) @property def _t_source(self): return self._get_input("t_source_k") @property def _mode(self): return self._get_input("mode") @property def _q_received_kw(self): return self._get_input("q_received_kw") @property def _p_received_kw(self): return self._get_input("p_received_kw")
[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_to_receive_kw = 0. for responder in self._get_generic_mapped_responders(prosumer): q_to_receive_kw += responder.q_to_receive_kw(prosumer) return q_to_receive_kw
[docs] def p_to_receive_kw(self, prosumer): """ Calculates the power to receive in kW. :param prosumer: The prosumer object :return: Power to receive in kW """ self.applied = False p_to_receive_kw = 0. for responder in self._get_generic_mapped_responders(prosumer): p_to_receive_kw += responder.p_to_receive_kw(prosumer) return p_to_receive_kw
[docs] def q_requested_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) demand_kw = self.q_requested_kw(prosumer) p_el_kw = self._p_received_kw q_kw = self._q_received_kw t_source_k = self._t_source hp_type = self._get_element_param(prosumer, "hp_type") mode = self._mode t_source_k = t_source_k - 273.0 # in celsius if hp_type == "air-water": t_min_source_k = -25.0 t_max_source_k = 45.0 cop_coeff = [5.06, -0.05, 0.00006] elif hp_type == "water-water1": t_min_source_k = -10.0 t_max_source_k = 25.0 cop_coeff = [5.06, -0.05, 0.00006] elif hp_type == "water-water2": t_max_source_k = 45.0 cop_coeff = [23.69, -0.986, 0.012] else: raise ValueError(f"Unknown heat pump type: {hp_type}") if mode in [1, 2, 3]: if mode == 1: # Mode 1: total heat is source heat plus heat generated (boosting) if hp_type == 'water-water1' or hp_type == 'air-water': if t_source_k > t_max_source_k or t_source_k < t_min_source_k: cop_floor = 0 cop_radiator = 0 pel_floor_kw = 0 pel_radiator_kw = 0 q_floor_kw = 0 q_radiator_kw = 0 q_remain_kw = demand_kw logging.warning(f"Heat pump is not operating due to the heat source " f"temperature being out of range: {float(t_source_k)} celsius") else: t_sink_floor_heating_k = 30.0 - 0.5 * t_source_k t_sink_radiator_heating_k = 40.0 - 1.0 * t_source_k q_max_kw = 5.8 + 0.21 * t_source_k q_remain_kw, q_floor_kw, q_radiator_kw, cop_floor, cop_radiator = ( self.first_mode_calc(q_kw, demand_kw, p_el_kw, q_max_kw, t_sink_floor_heating_k, t_sink_radiator_heating_k, t_source_k, cop_coeff)) pel_floor_kw = p_el_kw pel_radiator_kw = p_el_kw if hp_type == 'water-water2': if t_source_k > t_max_source_k: cop_floor = 0 cop_radiator = 0 pel_floor_kw = 0 pel_radiator_kw = 0 q_floor_kw = 0 q_radiator_kw = 0 q_remain_kw = demand_kw logging.warning(f"Heat pump is not operating due to the heat source " f"temperature being out of range: {float(t_source_k)} celsius") else: t_sink_floor_heating_k = 30.0 - 0.5 * t_source_k t_sink_radiator_heating_k = 40.0 - 1.0 * t_source_k if t_sink_floor_heating_k > 80.0 or t_sink_radiator_heating_k > 80.0: raise ValueError(f"Temperature of the heat sink is too high: {t_sink_floor_heating_k} celsius") q_max_kw = 25.308 + 0.963 * t_source_k q_remain_kw, q_floor_kw, q_radiator_kw, cop_floor, cop_radiator = ( self.first_mode_calc(q_kw, demand_kw, p_el_kw, q_max_kw, t_sink_floor_heating_k, t_sink_radiator_heating_k, t_source_k, cop_coeff)) pel_floor_kw = p_el_kw pel_radiator_kw = p_el_kw if mode == 2: if hp_type == 'water-water1' or hp_type == 'air-water':# Mode 2: total heat is heat generated if t_source_k > t_max_source_k or t_source_k < t_min_source_k: cop_floor = 0 cop_radiator = 0 pel_floor_kw = 0 pel_radiator_kw = 0 q_floor_kw = 0 q_radiator_kw = 0 q_remain_kw = demand_kw logging.warning(f"Heat pump is not operating due to the heat source " f"temperature being out of range: {float(t_source_k)} celsius") else: t_sink_floor_heating_k = 30.0 - 0.5 * t_source_k t_sink_radiator_heating_k = 40.0 - 1.0 * t_source_k q_max_kw = 5.8 + 0.21 * t_source_k q_remain_kw, q_floor_kw, q_radiator_kw, cop_floor, cop_radiator = ( self.second_mode_calc(demand_kw, p_el_kw, q_max_kw, t_sink_floor_heating_k, t_sink_radiator_heating_k, t_source_k, cop_coeff)) pel_floor_kw = p_el_kw pel_radiator_kw = p_el_kw if hp_type == 'water-water2': if t_source_k > t_max_source_k: cop_floor = 0 cop_radiator = 0 pel_floor_kw = 0 pel_radiator_kw = 0 q_floor_kw = 0 q_radiator_kw = 0 q_remain_kw = demand_kw logging.warning(f"Heat pump is not operating due to the heat source " f"temperature being out of range: {float(t_source_k)} celsius") else: t_sink_floor_heating_k = 30.0 - 0.5 * t_source_k t_sink_radiator_heating_k = 40.0 - 1.0 * t_source_k if t_sink_floor_heating_k > 80.0 or t_sink_radiator_heating_k > 80.0: raise ValueError(f"Temperature of the heat sink is too high: {t_sink_floor_heating_k} celsius") q_max_kw = 25.308 + 0.963 * t_source_k q_remain_kw, q_floor_kw, q_radiator_kw, cop_floor, cop_radiator = ( self.second_mode_calc(demand_kw, p_el_kw, q_max_kw, t_sink_floor_heating_k, t_sink_radiator_heating_k, t_source_k, cop_coeff)) pel_floor_kw = p_el_kw pel_radiator_kw = p_el_kw if mode == 3: if hp_type == 'water-water1' or hp_type == 'air-water':# Mode 3: produced heat is a result of infinite electrical source, produced heat is either q_max_kw or demand_kw if t_source_k > t_max_source_k or t_source_k < t_min_source_k: cop_floor = 0 cop_radiator = 0 pel_floor_kw = 0 pel_radiator_kw = 0 q_floor_kw = 0 q_radiator_kw = 0 q_remain_kw = demand_kw logging.warning(f"Heat pump is not operating due to the heat source " f"temperature being out of range: {float(t_source_k)} celsius") else: t_sink_floor_heating_k = 30.0 - 0.5 * t_source_k t_sink_radiator_heating_k = 40.0 - 1.0 * t_source_k q_max_kw = 5.8 + 0.21 * t_source_k q_remain_kw, cop_floor, cop_radiator, pel_floor_kw, pel_radiator_kw, q_floor_kw, q_radiator_kw \ = self.third_mode_calc(demand_kw, q_max_kw, t_sink_floor_heating_k, t_sink_radiator_heating_k, t_source_k, cop_coeff) if hp_type == 'water-water2': if t_source_k > t_max_source_k: cop_floor = 0 cop_radiator = 0 pel_floor_kw = 0 pel_radiator_kw = 0 q_floor_kw = 0 q_radiator_kw = 0 q_remain_kw = demand_kw logging.warning(f"Heat pump is not operating due to the heat source " f"temperature being out of range: {float(t_source_k)} celsius") else: t_sink_floor_heating_k = 30.0 - 0.5 * t_source_k t_sink_radiator_heating_k = 40.0 - 1.0 * t_source_k if t_sink_floor_heating_k > 80.0 or t_sink_radiator_heating_k > 80.0: raise ValueError(f"Temperature of the heat sink is too high: {t_sink_floor_heating_k} celsius") q_max_kw = 25.308 + 0.963 * t_source_k q_remain_kw, cop_floor, cop_radiator, pel_floor_kw, pel_radiator_kw, q_floor_kw, q_radiator_kw \ = self.third_mode_calc(demand_kw, q_max_kw, t_sink_floor_heating_k, t_sink_radiator_heating_k, t_source_k, cop_coeff) else: raise ValueError(f"Unknown mode: {mode}") result = np.array([pd.Series(cop_floor), pd.Series(cop_radiator), pd.Series(pel_floor_kw), pd.Series(pel_radiator_kw), pd.Series(q_remain_kw), pd.Series(q_floor_kw), pd.Series(q_radiator_kw) ]) self.finalize(prosumer, result.T) self.applied = True
[docs] def first_mode_calc(self, q_kw, demand_kw, p_el_kw, q_max_kw, t_sink_floor_heating_k, t_sink_radiator_heating_k, t_source_k, cop_coeff): """ Calculation of quantities for the mode 1 of the heat pump. Booster heat pump in mode 1 takes the inputted heat and electricity from external source and boosts heat for the floor and radiator heating. Mode 1 also calculates the COP for the floor and radiator heating that is dependent on the temperature of the heat source and heat sink. :param q_kw: Inputted heat in kW :param demand_kw: Demand of heat in kW :param p_el_kw: Electrical power in kW :param q_max_kw: Maximum heat in kW :param t_sink_floor_heating_k: Temperature of the floor heating sink in K :param t_sink_radiator_heating_k: Temperature of the radiator heating sink in K :param t_source_k: Temperature of the heat source in K :param cop_coeff: Coefficients for the COP calculation :returns: - q_remain_kw - Remaining heat in kW - q_floor_kw - Heat for the floor heating in kW - q_radiator_kw - Heat for the radiator heating in kW - cop_floor - COP for the floor heating - cop_radiator - COP for the radiator heating """ if demand_kw > q_max_kw: q_remain_kw = demand_kw - q_max_kw else: q_remain_kw = 0 cop_floor = cop_coeff[0] + cop_coeff[1] * (t_sink_floor_heating_k - t_source_k) + cop_coeff[2] * ( t_sink_floor_heating_k - t_source_k) ** 2 cop_radiator = cop_coeff[0] + cop_coeff[1] * (t_sink_radiator_heating_k - t_source_k) + cop_coeff[2] * ( t_sink_floor_heating_k - t_source_k) ** 2 q_floor_kw = p_el_kw * cop_floor + q_kw if p_el_kw * cop_floor + q_kw < q_max_kw else q_max_kw q_radiator_kw = p_el_kw * cop_radiator + q_kw if p_el_kw * cop_radiator + q_kw < q_max_kw else q_max_kw return q_remain_kw, q_floor_kw, q_radiator_kw, cop_floor, cop_radiator
[docs] def second_mode_calc(self, demand_kw, p_el_kw, q_max_kw, t_sink_floor_heating_k, t_sink_radiator_heating_k, t_source_k, cop_coeff): """ Calculation of quantities for the mode 2 of the heat pump. Booster heat pump in mode 2 takes the inputted electricity from external source and produces heat for the floor and radiator heating. Mode 2 also calculates the COP for the floor and radiator heating that is dependent on the temperature of the heat source and heat sink. :param demand_kw: Demand of heat in kW :param p_el_kw: Electrical power in kW :param q_max_kw: Maximum heat in kW :param t_sink_floor_heating_k: Temperature of the floor heating sink in K :param t_sink_radiator_heating_k: Temperature of the radiator heating sink in K :param t_source_k: Temperature of the heat source in K :param cop_coeff: Coefficients for the COP calculation :returns: - q_remain_kw - Remaining heat in kW - q_floor_kw - Heat for the floor heating in kW - q_radiator_kw - Heat for the radiator heating in kW - cop_floor - COP for the floor heating - cop_radiator - COP for the radiator heating """ if demand_kw > q_max_kw: q_remain_kw = demand_kw - q_max_kw else: q_remain_kw = 0 cop_floor = cop_coeff[0] + cop_coeff[1] * (t_sink_floor_heating_k - t_source_k) + cop_coeff[2] * ( t_sink_floor_heating_k - t_source_k) ** 2 cop_radiator = cop_coeff[0] + cop_coeff[1] * (t_sink_radiator_heating_k - t_source_k) + cop_coeff[2] * ( t_sink_floor_heating_k - t_source_k) ** 2 q_floor_kw = p_el_kw * cop_floor if p_el_kw * cop_floor < q_max_kw else q_max_kw q_radiator_kw = p_el_kw * cop_radiator if p_el_kw * cop_radiator < q_max_kw else q_max_kw return q_remain_kw, q_floor_kw, q_radiator_kw, cop_floor, cop_radiator
[docs] def third_mode_calc(self, demand_kw, q_max_kw, t_sink_floor_heating_k, t_sink_radiator_heating_k, t_source_k, cop_coeff): """ Calculation of quantities for the mode 3 of the heat pump. Mode 3 calculates the inputted electricity from infinite source (grid etc.) based on the heat demand and produces heat for the floor and radiator heating. Mode 3 also calculates the COP for the floor and radiator heating that is dependent on the temperature of the heat source and heat sink. :param demand_kw: Demand of heat in kW :param q_max_kw: Maximum heat in kW :param t_sink_floor_heating_k: Temperature of the floor heating sink in K :param t_sink_radiator_heating_k: Temperature of the radiator heating sink in K :param t_source_k: Temperature of the heat source in K :param cop_coeff: Coefficients for the COP calculation :returns: - q_remain_kw - Remaining heat in kW - cop_floor - COP for the floor heating - cop_radiator - COP for the radiator heating - pel_floor_kw - Electrical power needed for the floor heating in kW - pel_radiator_kw - Electrical power needed for the radiator heating in kW - q_floor_kw - Heat for the floor heating in kW - q_radiator_kw - Heat for the radiator heating in kW """ cop_floor = cop_coeff[0] + cop_coeff[1] * (t_sink_floor_heating_k - t_source_k) + cop_coeff[2] * ( t_sink_floor_heating_k - t_source_k) ** 2 cop_radiator = cop_coeff[0] + cop_coeff[1] * (t_sink_radiator_heating_k - t_source_k) + cop_coeff[2] * ( t_sink_floor_heating_k - t_source_k) ** 2 if demand_kw > q_max_kw: q_remain_kw = demand_kw - q_max_kw pel_floor_kw = q_max_kw / cop_floor pel_radiator_kw = q_max_kw / cop_radiator q_floor_kw = q_max_kw q_radiator_kw = q_max_kw return q_remain_kw, cop_floor, cop_radiator, pel_floor_kw, pel_radiator_kw, q_floor_kw, q_radiator_kw else: q_remain_kw = 0 pel_floor_kw = demand_kw / cop_floor pel_radiator_kw = demand_kw / cop_radiator q_floor_kw = demand_kw q_radiator_kw = demand_kw return q_remain_kw, cop_floor, cop_radiator, pel_floor_kw, pel_radiator_kw, q_floor_kw, q_radiator_kw