diff --git a/src/gsy_e/models/strategy/energy_parameters/heatpump/cop_models/__init__.py b/src/gsy_e/models/strategy/energy_parameters/heatpump/cop_models/__init__.py index 462f28351..6c3528bbb 100644 --- a/src/gsy_e/models/strategy/energy_parameters/heatpump/cop_models/__init__.py +++ b/src/gsy_e/models/strategy/energy_parameters/heatpump/cop_models/__init__.py @@ -2,7 +2,8 @@ COPModelType, cop_model_factory, BaseCOPModel, + UniversalCOPModel, ) -__all__ = ["COPModelType", "cop_model_factory", "BaseCOPModel"] +__all__ = ["COPModelType", "cop_model_factory", "BaseCOPModel", "UniversalCOPModel"] diff --git a/src/gsy_e/models/strategy/energy_parameters/heatpump/heat_pump.py b/src/gsy_e/models/strategy/energy_parameters/heatpump/heat_pump.py index b96b44299..2086d3084 100644 --- a/src/gsy_e/models/strategy/energy_parameters/heatpump/heat_pump.py +++ b/src/gsy_e/models/strategy/energy_parameters/heatpump/heat_pump.py @@ -19,6 +19,7 @@ COPModelType, cop_model_factory, BaseCOPModel, + UniversalCOPModel, ) from gsy_e.models.strategy.energy_parameters.heatpump.tank_parameters import ( WaterTankParameters, @@ -263,19 +264,25 @@ def update_cop_after_dis_charging( bought_energy_kWh: float, ): """Update the COP of the heat pump in its state class.""" - if bought_energy_kWh < FLOATING_POINT_TOLERANCE: - self._hp_state.set_cop(time_slot, self._hp_state.get_cop(last_time_slot)) - return - bought_energy_kW = convert_kWh_to_kW(bought_energy_kWh, GlobalConfig.slot_length) - heat_energy_kW = self._cop_model.calc_q_from_p_kW( - source_temp_C=source_temp_C, - condenser_temp_C=self._charger.get_average_inlet_temperature_C(last_time_slot), - electrical_demand_kW=bought_energy_kW, - ) - if heat_energy_kW is None: - cop = self._hp_state.get_cop(last_time_slot) + if isinstance(self._cop_model, UniversalCOPModel): + cop = self._cop_model.calc_cop( + source_temp_C=source_temp_C, + condenser_temp_C=self._charger.get_average_inlet_temperature_C(last_time_slot), + ) else: - cop = heat_energy_kW / bought_energy_kW + if bought_energy_kWh < FLOATING_POINT_TOLERANCE: + self._hp_state.set_cop(time_slot, self._hp_state.get_cop(last_time_slot)) + return + bought_energy_kW = convert_kWh_to_kW(bought_energy_kWh, GlobalConfig.slot_length) + heat_energy_kW = self._cop_model.calc_q_from_p_kW( + source_temp_C=source_temp_C, + condenser_temp_C=self._charger.get_average_inlet_temperature_C(last_time_slot), + electrical_demand_kW=bought_energy_kW, + ) + if heat_energy_kW is None: + cop = self._hp_state.get_cop(last_time_slot) + else: + cop = heat_energy_kW / bought_energy_kW # Set the calculated COP on both the last and the current time slot to use in calculations self._hp_state.set_cop(last_time_slot, cop) @@ -682,19 +689,25 @@ def _update_cop_after_dis_charging( bought_energy_kWh: float, ): """Update the COP of the heat pump in its state class.""" - if bought_energy_kWh < FLOATING_POINT_TOLERANCE: - self._hp_state.set_cop(time_slot, self._hp_state.get_cop(last_time_slot)) - return - bought_energy_kW = convert_kWh_to_kW(bought_energy_kWh, GlobalConfig.slot_length) - heat_energy_kW = self._cop_model.calc_q_from_p_kW( - source_temp_C=self._source_temp_C.get_value(last_time_slot), - condenser_temp_C=self._target_temp_C.get_value(last_time_slot), - electrical_demand_kW=bought_energy_kW, - ) - if heat_energy_kW is None: - cop = self.state.get_cop(last_time_slot) + if self._cop_model_type == COPModelType.UNIVERSAL: + cop = self._cop_model.calc_cop( + source_temp_C=self._source_temp_C.get_value(last_time_slot), + condenser_temp_C=self._target_temp_C.get_value(last_time_slot), + ) else: - cop = heat_energy_kW / bought_energy_kW + if bought_energy_kWh < FLOATING_POINT_TOLERANCE: + self.state.set_cop(time_slot, self.state.get_cop(last_time_slot)) + return + bought_energy_kW = convert_kWh_to_kW(bought_energy_kWh, GlobalConfig.slot_length) + heat_energy_kW = self._cop_model.calc_q_from_p_kW( + source_temp_C=self._source_temp_C.get_value(last_time_slot), + condenser_temp_C=self._target_temp_C.get_value(last_time_slot), + electrical_demand_kW=bought_energy_kW, + ) + if heat_energy_kW is None: + cop = self.state.get_cop(last_time_slot) + else: + cop = heat_energy_kW / bought_energy_kW # Set the calculated COP on both the last and the current time slot to use in calculations self.state.set_cop(last_time_slot, cop) @@ -752,3 +765,4 @@ def _populate_state(self, time_slot: DateTime): def _decrement_posted_energy(self, time_slot: DateTime, energy_kWh: float): updated_energy_demand_kWh = max(0.0, self.get_energy_demand_kWh(time_slot) - energy_kWh) self._state.set_energy_demand_kWh(time_slot, updated_energy_demand_kWh) + self._state.increase_total_traded_energy_kWh(energy_kWh) diff --git a/src/gsy_e/models/strategy/energy_parameters/heatpump/tank_parameters.py b/src/gsy_e/models/strategy/energy_parameters/heatpump/tank_parameters.py index d7c01b8e4..c4fc31645 100644 --- a/src/gsy_e/models/strategy/energy_parameters/heatpump/tank_parameters.py +++ b/src/gsy_e/models/strategy/energy_parameters/heatpump/tank_parameters.py @@ -25,12 +25,13 @@ class PCMType(Enum): @dataclass class BaseTankParameters: - # pylint: disable=too-many-instance-attributes) """Base class for tank parameters""" type: HeatpumpTankTypes = HeatpumpTankTypes.WATER name: str = "" initial_temp_C: float = ConstSettings.HeatPumpSettings.INIT_TEMP_C + min_temp_C: float = ConstSettings.HeatPumpSettings.MIN_TEMP_C + max_temp_C: float = ConstSettings.HeatPumpSettings.MAX_TEMP_C loss_per_day_percent: float = HeatPumpSettingsDefaultParameters.TANK_LOSS_PERCENT_DAY @property @@ -41,23 +42,15 @@ def per_market_slot_loss(self): @dataclass class WaterTankParameters(BaseTankParameters): - # pylint: disable=too-many-instance-attributes) """Nameplate parameters of a water tank.""" - min_temp_C: float = ConstSettings.HeatPumpSettings.MIN_TEMP_C - max_temp_C: float = ConstSettings.HeatPumpSettings.MAX_TEMP_C tank_volume_L: float = ConstSettings.HeatPumpSettings.TANK_VOL_L @dataclass class PCMTankParameters(BaseTankParameters): - # pylint: disable=too-many-instance-attributes) """Nameplate parameters of a pcm tank.""" - min_temp_htf_C: float = ConstSettings.HeatPumpSettings.MIN_TEMP_C - max_temp_htf_C: float = ConstSettings.HeatPumpSettings.MAX_TEMP_C - min_temp_pcm_C: float = ConstSettings.HeatPumpSettings.MIN_TEMP_C - max_temp_pcm_C: float = ConstSettings.HeatPumpSettings.MAX_TEMP_C pcm_tank_type: PCMType = PCMType.OM37 volume_flow_rate_l_min: float = 10 number_of_plates: int = 15 diff --git a/src/gsy_e/models/strategy/state/heatpump_state.py b/src/gsy_e/models/strategy/state/heatpump_state.py index 4671fe919..9cfc2cd40 100644 --- a/src/gsy_e/models/strategy/state/heatpump_state.py +++ b/src/gsy_e/models/strategy/state/heatpump_state.py @@ -106,11 +106,11 @@ def restore_state(self, state_dict: Dict): self._heat_demand_kJ = convert_str_to_pendulum_in_dict(state_dict["heat_demand_kJ"]) def get_results_dict(self, current_time_slot: DateTime) -> Dict: - retval = { + return { "cop": self.get_cop(current_time_slot), "heat_demand_kJ": self.get_heat_demand_kJ(current_time_slot), + "total_traded_energy_kWh": self._total_traded_energy_kWh, } - return retval class HeatPumpState(HeatPumpStateBase): @@ -207,16 +207,14 @@ def delete_past_state_values(self, current_time_slot: DateTime): self._delete_time_slots(self._heat_demand_kJ, last_time_slot) def get_results_dict(self, current_time_slot: DateTime) -> Dict: - retval = { + return { **super().get_results_dict(current_time_slot), "energy_consumption_kWh": self.get_energy_consumption_kWh(current_time_slot), "max_energy_demand_kWh": self.get_max_energy_demand_kWh(current_time_slot), "min_energy_demand_kWh": self.get_min_energy_demand_kWh(current_time_slot), "cop": self.get_cop(current_time_slot), - "total_traded_energy_kWh": self._total_traded_energy_kWh, "heat_demand_kJ": self.get_heat_demand_kJ(current_time_slot), } - return retval def __str__(self): return self.__class__.__name__ diff --git a/src/gsy_e/models/strategy/state/heatpump_tank_states/pcm_tank_state.py b/src/gsy_e/models/strategy/state/heatpump_tank_states/pcm_tank_state.py index 82f8071f1..f22d9a587 100644 --- a/src/gsy_e/models/strategy/state/heatpump_tank_states/pcm_tank_state.py +++ b/src/gsy_e/models/strategy/state/heatpump_tank_states/pcm_tank_state.py @@ -59,10 +59,8 @@ def serialize(self): """Return serializable dict of class parameters.""" return { "initial_temp_C": self._params.initial_temp_C, - "min_temp_htf_C": self._params.min_temp_htf_C, - "max_temp_htf_C": self._params.max_temp_htf_C, - "min_temp_pcm_C": self._params.min_temp_pcm_C, - "max_temp_pcm_C": self._params.max_temp_pcm_C, + "min_temp_C": self._params.min_temp_C, + "max_temp_C": self._params.max_temp_C, "type": self._params.type.value, "pcm_tank_type": self._params.pcm_tank_type.value, } @@ -102,8 +100,25 @@ def _get_heat_demand_kJ(self, time_slot: DateTime): def _set_heat_demand_kJ(self, energy_kJ: float, time_slot: DateTime): self._heat_demand_kJ[time_slot] = energy_kJ - def _set_condenser_temp_C(self, temp_C: float, time_slot: DateTime): - self._condenser_temp_C[time_slot] = temp_C + def _set_condenser_temp_C(self, condenser_temp_C: float, time_slot: DateTime): + if (self._params.min_temp_C - condenser_temp_C) > FLOATING_POINT_TOLERANCE: + log.warning( + "The PCM storage tank reached it's minimum (%s), discharging " + "condensor temperature of %s is limited to the minimum", + self._params.min_temp_C, + round(condenser_temp_C, 2), + ) + condenser_temp_C = self._params.min_temp_C + if (condenser_temp_C - self._params.max_temp_C) > FLOATING_POINT_TOLERANCE: + log.warning( + "The PCM storage tank reached it's maximum (%s), charging " + "condensor temperature of %s is limited to the maximum", + self._params.max_temp_C, + round(condenser_temp_C, 2), + ) + condenser_temp_C = self._params.max_temp_C + + self._condenser_temp_C[time_slot] = condenser_temp_C def _get_condenser_temp_C(self, time_slot: DateTime): return self._condenser_temp_C.get(time_slot, self.get_htf_temp_C(time_slot)) @@ -133,25 +148,6 @@ def _get_condenser_temp_from_heat_demand_kWh( assert 0 < condenser_temp_C < 100, f"unrealistic condenser temp {condenser_temp_C}" return condenser_temp_C - def _limit_condenser_temp(self, condenser_temp_C: float) -> float: - if (self._params.min_temp_htf_C - condenser_temp_C) > FLOATING_POINT_TOLERANCE: - log.warning( - "The PCM storage tank reached it's minimum (%s), discharging " - "condensor temperature of %s is limited to the minimum", - self._params.min_temp_htf_C, - round(condenser_temp_C, 2), - ) - return self._params.min_temp_htf_C - if (condenser_temp_C - self._params.max_temp_htf_C) > FLOATING_POINT_TOLERANCE: - log.warning( - "The PCM storage tank reached it's maximum (%s), charging " - "condensor temperature of %s is limited to the maximum", - self._params.max_temp_htf_C, - round(condenser_temp_C, 2), - ) - return self._params.max_temp_htf_C - return condenser_temp_C - def increase_tank_temp_from_heat_energy(self, heat_energy_kWh: float, time_slot: DateTime): """Increase the temperature of the water tank with the provided heat energy.""" assert heat_energy_kWh > FLOATING_POINT_TOLERANCE @@ -168,19 +164,26 @@ def decrease_tank_temp_from_heat_energy(self, heat_energy_kWh: float, time_slot: ) self._decrease_storage_temp_from_condenser_temp(temp_cond_C, time_slot) + def _limit_temps_after_dis_charging(self, temps: list[float]): + temps = [ + temp if temp <= self._params.max_temp_C else self._params.max_temp_C for temp in temps + ] + return [ + temp if temp >= self._params.min_temp_C else self._params.min_temp_C for temp in temps + ] + def _increase_storage_temp_from_condenser_temp( self, condenser_temp_C: float, time_slot: DateTime ): """Increase storage temperatures for provided condenser temperature.""" - condenser_temp_C = self._limit_condenser_temp(condenser_temp_C) self._set_condenser_temp_C(condenser_temp_C, time_slot) htf_temps, pcm_temps = self._pcm_charge_model.get_temp_after_charging( current_htf_temps_C=self._get_htf_temps_C(self._last_time_slot(time_slot)), current_pcm_temps_C=self._get_pcm_temps_C(self._last_time_slot(time_slot)), charging_temp=condenser_temp_C, ) - self._htf_temps_C[time_slot] = htf_temps - self._pcm_temps_C[time_slot] = pcm_temps + self._htf_temps_C[time_slot] = self._limit_temps_after_dis_charging(htf_temps) + self._pcm_temps_C[time_slot] = self._limit_temps_after_dis_charging(pcm_temps) self._set_soc_after_charging(time_slot) @@ -188,15 +191,14 @@ def _decrease_storage_temp_from_condenser_temp( self, condenser_temp_C: float, time_slot: DateTime ): """Decrease storage temperatures for provided condenser temperature.""" - condenser_temp_C = self._limit_condenser_temp(condenser_temp_C) self._set_condenser_temp_C(condenser_temp_C, time_slot) htf_temps, pcm_temps = self._pcm_discharge_model.get_temp_after_discharging( current_htf_temps_C=self._get_htf_temps_C(self._last_time_slot(time_slot)), current_pcm_temps_C=self._get_pcm_temps_C(self._last_time_slot(time_slot)), discharging_temp=condenser_temp_C, ) - self._htf_temps_C[time_slot] = htf_temps - self._pcm_temps_C[time_slot] = pcm_temps + self._htf_temps_C[time_slot] = self._limit_temps_after_dis_charging(htf_temps) + self._pcm_temps_C[time_slot] = self._limit_temps_after_dis_charging(pcm_temps) self._set_soc_after_discharging(time_slot) @@ -241,10 +243,8 @@ def restore_state(self, state_dict: Dict): self._pcm_temps_C = convert_str_to_pendulum_in_dict(state_dict["pcm_temps_C"]) self._condenser_temp_C = convert_str_to_pendulum_in_dict(state_dict["condenser_temp_C"]) self._soc = convert_str_to_pendulum_in_dict(state_dict["soc"]) - self._params.min_temp_htf_C = state_dict["min_temp_htf_C"] - self._params.max_temp_htf_C = state_dict["max_temp_htf_C"] - self._params.min_temp_pcm_C = state_dict["min_temp_pcm_C"] - self._params.max_temp_pcm_C = state_dict["max_temp_pcm_C"] + self._params.min_temp_C = state_dict["min_temp_C"] + self._params.max_temp_C = state_dict["max_temp_C"] self._params.initial_temp_C = state_dict["initial_temp_C"] def delete_past_state_values(self, current_time_slot: DateTime): @@ -263,12 +263,12 @@ def get_min_heat_energy_consumption_kJ(self, time_slot: DateTime, heat_demand_kJ def get_soc_energy_kJ(self, time_slot: DateTime) -> float: """Return the available energy stored in the tank.""" - if self.get_pcm_temp_C(time_slot) - self._params.min_temp_pcm_C < FLOATING_POINT_TOLERANCE: + if self.get_pcm_temp_C(time_slot) - self._params.min_temp_C < FLOATING_POINT_TOLERANCE: return 0 return convert_kWh_to_kJ( self._get_heat_demand_kWh_from_deltaT( - self.get_pcm_temp_C(time_slot) - self._params.min_temp_pcm_C + self.get_pcm_temp_C(time_slot) - self._params.min_temp_C ) ) @@ -286,12 +286,12 @@ def _get_heat_demand_kWh_from_deltaT(self, temperature_difference: float) -> flo def get_dod_energy_kJ(self, time_slot: DateTime) -> float: """Return depth of discharge as an energy value in kJ.""" - if self._params.max_temp_pcm_C - self.get_pcm_temp_C(time_slot) < FLOATING_POINT_TOLERANCE: + if self._params.max_temp_C - self.get_pcm_temp_C(time_slot) < FLOATING_POINT_TOLERANCE: return 0 return convert_kWh_to_kJ( self._get_heat_demand_kWh_from_deltaT( - self._params.max_temp_pcm_C - self.get_pcm_temp_C(time_slot) + self._params.max_temp_C - self.get_pcm_temp_C(time_slot) ) ) diff --git a/tests/strategies/energy_parameters/test_combined_heatpump_state.py b/tests/strategies/energy_parameters/test_combined_heatpump_state.py index f564adb6d..9c3b716e2 100644 --- a/tests/strategies/energy_parameters/test_combined_heatpump_state.py +++ b/tests/strategies/energy_parameters/test_combined_heatpump_state.py @@ -4,7 +4,9 @@ import pytest from gsy_framework.constants_limits import GlobalConfig +from gsy_framework.utils import convert_kWh_to_kW +from gsy_e.models.strategy.energy_parameters.heatpump.cop_models import UniversalCOPModel from gsy_e.models.strategy.energy_parameters.heatpump.heat_pump import CombinedHeatpumpTanksState CURRENT_MARKET_SLOT = GlobalConfig.start_date @@ -109,3 +111,82 @@ def test_delete_past_state_values_triggers_deletion_in_states(combined_state): combined_state._hp_state.delete_past_state_values.assert_called_once() combined_state._charger.delete_past_state_values.assert_called_once() + + def test_update_cop_after_dis_charging_universal_cop_model_sets_cop_on_both_slots(self): + # Given + cop_model = Mock(spec=UniversalCOPModel) + cop_model.calc_cop.return_value = 3.5 + combined_state = CombinedHeatpumpTanksState( + hp_state=Mock(), + tanks_state=Mock(), + cop_model=cop_model, + max_energy_consumption_kWh=5, + ) + combined_state._charger.get_average_inlet_temperature_C = Mock(return_value=40) + # When + combined_state.update_cop_after_dis_charging( + source_temp_C=10, + time_slot=CURRENT_MARKET_SLOT, + last_time_slot=LAST_MARKET_SLOT, + bought_energy_kWh=1.0, + ) + # Then + cop_model.calc_cop.assert_called_once_with(source_temp_C=10, condenser_temp_C=40) + combined_state._hp_state.set_cop.assert_any_call(LAST_MARKET_SLOT, 3.5) + combined_state._hp_state.set_cop.assert_any_call(CURRENT_MARKET_SLOT, 3.5) + + def test_update_cop_after_dis_charging_zero_energy_sets_only_current_slot_cop( + self, combined_state + ): + # Given + combined_state._hp_state.get_cop.return_value = 2.5 + # When + combined_state.update_cop_after_dis_charging( + source_temp_C=10, + time_slot=CURRENT_MARKET_SLOT, + last_time_slot=LAST_MARKET_SLOT, + bought_energy_kWh=0.0, + ) + # Then + combined_state._hp_state.get_cop.assert_called_once_with(LAST_MARKET_SLOT) + combined_state._hp_state.set_cop.assert_called_once_with(CURRENT_MARKET_SLOT, 2.5) + + def test_update_cop_after_dis_charging_valid_heat_energy_calculates_correct_cop( + self, combined_state + ): + # Given + heat_energy_kW = 8.0 + bought_energy_kWh = 1.0 + combined_state._charger.get_average_inlet_temperature_C = Mock(return_value=40) + combined_state._cop_model.calc_q_from_p_kW.return_value = heat_energy_kW + # When + combined_state.update_cop_after_dis_charging( + source_temp_C=10, + time_slot=CURRENT_MARKET_SLOT, + last_time_slot=LAST_MARKET_SLOT, + bought_energy_kWh=bought_energy_kWh, + ) + # Then + bought_energy_kW = convert_kWh_to_kW(bought_energy_kWh, GlobalConfig.slot_length) + expected_cop = heat_energy_kW / bought_energy_kW + combined_state._hp_state.set_cop.assert_any_call(LAST_MARKET_SLOT, expected_cop) + combined_state._hp_state.set_cop.assert_any_call(CURRENT_MARKET_SLOT, expected_cop) + + def test_update_cop_after_dis_charging_none_heat_energy_uses_last_slot_cop( + self, combined_state + ): + # Given + combined_state._charger.get_average_inlet_temperature_C = Mock(return_value=40) + combined_state._cop_model.calc_q_from_p_kW.return_value = None + combined_state._hp_state.get_cop.return_value = 2.8 + # When + combined_state.update_cop_after_dis_charging( + source_temp_C=10, + time_slot=CURRENT_MARKET_SLOT, + last_time_slot=LAST_MARKET_SLOT, + bought_energy_kWh=1.0, + ) + # Then + combined_state._hp_state.get_cop.assert_called_once_with(LAST_MARKET_SLOT) + combined_state._hp_state.set_cop.assert_any_call(LAST_MARKET_SLOT, 2.8) + combined_state._hp_state.set_cop.assert_any_call(CURRENT_MARKET_SLOT, 2.8) diff --git a/tests/strategies/energy_parameters/test_heat_pump_without_tanks.py b/tests/strategies/energy_parameters/test_heat_pump_without_tanks.py index 7298b594e..d11a2ec01 100644 --- a/tests/strategies/energy_parameters/test_heat_pump_without_tanks.py +++ b/tests/strategies/energy_parameters/test_heat_pump_without_tanks.py @@ -8,6 +8,7 @@ from gsy_e.models.strategy.energy_parameters.heatpump.heat_pump import ( HeatPumpEnergyParametersWithoutTanks, + COPModelType, ) CURRENT_MARKET_SLOT = today(tz=TIME_ZONE) @@ -72,12 +73,20 @@ def test_event_market_cycle_rotates_profiles(self, energy_params): assert energy_params._source_temp_C.get_value(CURRENT_MARKET_SLOT) == 10 assert energy_params._target_temp_C.get_value(CURRENT_MARKET_SLOT) == 25 - def test_event_market_cycle_populates_state_correctly(self, energy_params): + @pytest.mark.parametrize( + "cop_model_type, expected_cop", + [[COPModelType.UNIVERSAL, 5], [COPModelType.ELCO_AEROTOP_G07_14M, 1]], + ) + def test_event_market_cycle_populates_state_correctly( + self, cop_model_type, expected_cop, energy_params + ): # Given current_market_slot = NEXT_MARKET_SLOT last_market_slot = CURRENT_MARKET_SLOT energy_params.event_activate() - energy_params._cop_model.calc_q_from_p_kW = Mock(return_value=5) + energy_params._cop_model_type = cop_model_type + energy_params._cop_model.calc_cop = Mock(return_value=5) + energy_params._cop_model.calc_q_from_p_kW = Mock(return_value=1) energy_params._calc_Q_kJ_from_energy_kWh = Mock(return_value=5000) energy_params._bought_energy_kWh = 5 / 4 / 5 @@ -86,12 +95,13 @@ def test_event_market_cycle_populates_state_correctly(self, energy_params): energy_params.event_market_cycle(last_market_slot) energy_params.event_market_cycle(current_market_slot) # Then - assert energy_params._state._cop[last_market_slot] == 5 - assert energy_params._state._cop[current_market_slot] == 5 + assert energy_params._state._cop[last_market_slot] == expected_cop + assert energy_params._state._cop[current_market_slot] == expected_cop assert energy_params._bought_energy_kWh == 0 assert energy_params._consumption_kWh.get_value(current_market_slot) == 1 assert energy_params._state._heat_demand_kJ[current_market_slot] == 5000 assert energy_params._state._energy_demand_kWh[current_market_slot] == 1 + # assert False def test_event_market_cycle_populates_state_correctly_heat_profile( self, energy_params_heat_profile @@ -100,7 +110,7 @@ def test_event_market_cycle_populates_state_correctly_heat_profile( current_market_slot = NEXT_MARKET_SLOT last_market_slot = CURRENT_MARKET_SLOT energy_params_heat_profile.event_activate() - energy_params_heat_profile._cop_model.calc_q_from_p_kW = Mock(return_value=5) + energy_params_heat_profile._cop_model.calc_cop = Mock(return_value=5) energy_params_heat_profile._calc_Q_kJ_from_energy_kWh = Mock(return_value=5000) energy_params_heat_profile._bought_energy_kWh = 5 / 4 / 5 # When @@ -124,6 +134,7 @@ def test_event_traded_energy_updates_energy_state_variables(self, energy_params) # Then assert energy_params._bought_energy_kWh == 0.2 assert energy_params.state.get_energy_demand_kWh(CURRENT_MARKET_SLOT) == 0.8 + assert energy_params._state._total_traded_energy_kWh == 0.2 def test_serialize_returns_correct_input_arguments(self, energy_params): # When @@ -156,23 +167,32 @@ def test_get_energy_demand_kWh_returns_correct_value_heat_profile( # When / Then assert energy_params_heat_profile.get_energy_demand_kWh(CURRENT_MARKET_SLOT) == 0.001 + @pytest.mark.parametrize( + "cop_model_type, bought_energy_kWh, expected_cop", + [ + [COPModelType.UNIVERSAL, 1, 4], + [COPModelType.ELCO_AEROTOP_G07_14M, 5 / 4 / 5, 6], + [COPModelType.ELCO_AEROTOP_G07_14M, 0, 6], + ], + ) def test_event_market_cycle_populates_state_correctly_even_if_cop_model_fails( - self, energy_params + self, cop_model_type, bought_energy_kWh, expected_cop, energy_params ): # Given current_market_slot = NEXT_MARKET_SLOT last_market_slot = CURRENT_MARKET_SLOT energy_params.event_activate() - energy_params._bought_energy_kWh = 5 / 4 / 5 + energy_params._bought_energy_kWh = bought_energy_kWh + energy_params._cop_model_type = cop_model_type energy_params._cop_model.calc_q_from_p_kW = Mock(return_value=None) + energy_params._cop_model.calc_cop = Mock(return_value=4) energy_params.state.get_cop = Mock(return_value=6) # When # Event market cycle has to be called twice in order to have a last_market_slot energy_params.event_market_cycle(last_market_slot) energy_params.event_market_cycle(current_market_slot) # Then - assert energy_params._state._cop[last_market_slot] == 6 - assert energy_params._state._cop[current_market_slot] == 6 + assert energy_params._state._cop[current_market_slot] == expected_cop assert energy_params._bought_energy_kWh == 0 assert energy_params._consumption_kWh.get_value(current_market_slot) == 1 assert energy_params._state._heat_demand_kJ[current_market_slot] == 21600.0 diff --git a/tests/strategies/state/heatpump_tank_states/test_pcm_tank_state.py b/tests/strategies/state/heatpump_tank_states/test_pcm_tank_state.py index 8ddc3036a..00139b875 100644 --- a/tests/strategies/state/heatpump_tank_states/test_pcm_tank_state.py +++ b/tests/strategies/state/heatpump_tank_states/test_pcm_tank_state.py @@ -19,10 +19,8 @@ def fixture_pcm_tank(): pcm_tank = PCMTankState( tank_parameters=PCMTankParameters( initial_temp_C=37, - max_temp_htf_C=42, - min_temp_htf_C=32, - max_temp_pcm_C=42, - min_temp_pcm_C=32, + max_temp_C=42, + min_temp_C=32, ) ) pcm_tank._pcm_charge_model.get_soc = Mock(return_value=0.5) @@ -117,10 +115,8 @@ def test_init(self): pcm_tank = PCMTankState( tank_parameters=PCMTankParameters( initial_temp_C=37, - max_temp_htf_C=42, - min_temp_htf_C=32, - max_temp_pcm_C=42, - min_temp_pcm_C=32, + max_temp_C=42, + min_temp_C=32, ) ) assert pcm_tank._htf_temps_C == {} @@ -173,10 +169,8 @@ def test_restore_state_restores_values_correctly(self, pcm_tank): "pcm_temps_C": {CURRENT_MARKET_SLOT.format(DATE_TIME_FORMAT): [50] * 5}, "condenser_temp_C": {CURRENT_MARKET_SLOT.format(DATE_TIME_FORMAT): 37}, "soc": {CURRENT_MARKET_SLOT.format(DATE_TIME_FORMAT): 0.5}, - "min_temp_htf_C": 0, - "max_temp_htf_C": 100, - "min_temp_pcm_C": 0, - "max_temp_pcm_C": 100, + "min_temp_C": 0, + "max_temp_C": 100, "initial_temp_C": 50, } ) @@ -184,10 +178,8 @@ def test_restore_state_restores_values_correctly(self, pcm_tank): assert pcm_tank._htf_temps_C == {CURRENT_MARKET_SLOT: [50] * 5} assert pcm_tank._pcm_temps_C == {CURRENT_MARKET_SLOT: [50] * 5} assert pcm_tank._soc == {CURRENT_MARKET_SLOT: 0.5} - assert pcm_tank._params.min_temp_htf_C == 0.0 - assert pcm_tank._params.max_temp_htf_C == 100 - assert pcm_tank._params.min_temp_pcm_C == 0.0 - assert pcm_tank._params.max_temp_pcm_C == 100 + assert pcm_tank._params.min_temp_C == 0.0 + assert pcm_tank._params.max_temp_C == 100 assert pcm_tank._params.initial_temp_C == 50 assert pcm_tank._condenser_temp_C == {CURRENT_MARKET_SLOT: 37}