diff --git a/CHANGELOG.md b/CHANGELOG.md index 169294fd29..95f489acbf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -97,6 +97,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Move compression of output files into `ResultEventListener`[#965](https://github.com/ie3-institute/simona/issues/965) - Rewrote StorageModelTest from groovy to scala [#646](https://github.com/ie3-institute/simona/issues/646) - Updated `ExtEvSimulationClasses` [#898](https://github.com/ie3-institute/simona/issues/898) +- Refactoring of `ThermalGrid.energyGrid` to distinguish between demand of house and storage [#928](https://github.com/ie3-institute/simona/issues/928) ### Fixed - Fix rendering of references in documentation [#505](https://github.com/ie3-institute/simona/issues/505) diff --git a/src/main/scala/edu/ie3/simona/agent/participant/hp/HpAgentFundamentals.scala b/src/main/scala/edu/ie3/simona/agent/participant/hp/HpAgentFundamentals.scala index e38454b020..58b9c402b1 100644 --- a/src/main/scala/edu/ie3/simona/agent/participant/hp/HpAgentFundamentals.scala +++ b/src/main/scala/edu/ie3/simona/agent/participant/hp/HpAgentFundamentals.scala @@ -314,9 +314,7 @@ trait HpAgentFundamentals nodalVoltage: squants.Dimensionless, model: HpModel, ): HpState = { - val (_, _, state) = - model.determineState(modelState, calcRelevantData) - state + model.determineState(modelState, calcRelevantData)._3 } /** Abstract definition, individual implementations found in individual agent diff --git a/src/main/scala/edu/ie3/simona/model/participant/HpModel.scala b/src/main/scala/edu/ie3/simona/model/participant/HpModel.scala index be852ad189..f884230853 100644 --- a/src/main/scala/edu/ie3/simona/model/participant/HpModel.scala +++ b/src/main/scala/edu/ie3/simona/model/participant/HpModel.scala @@ -82,7 +82,7 @@ final case class HpModel( * [[HpModel.determineState]]. This state then is fed into the power * calculation logic by [[HpState]]. * - * @param modelState + * @param currentState * Current state of the heat pump * @param relevantData * data of heat pump including state of the heat pump @@ -90,9 +90,9 @@ final case class HpModel( * active power */ override protected def calculateActivePower( - modelState: HpState, + currentState: HpState, relevantData: HpRelevantData, - ): Power = modelState.activePower + ): Power = currentState.activePower /** "Calculate" the heat output of the heat pump. The hp's state is already * updated, because the calculation of apparent power in @@ -101,7 +101,7 @@ final case class HpModel( * * @param tick * Current simulation time for the calculation - * @param modelState + * @param currentState * Current state of the heat pump * @param data * Relevant (external) data for calculation @@ -110,31 +110,59 @@ final case class HpModel( */ override def calculateHeat( tick: Long, - modelState: HpState, + currentState: HpState, data: HpRelevantData, - ): Power = modelState.qDot + ): Power = currentState.qDot - /** Given a [[HpRelevantData]] object and the current [[HpState]], this - * function calculates the heat pump's next state to get the actual active - * power of this state use [[calculateActivePower]] with the generated state + /** Given a [[HpRelevantData]] object and the last [[HpState]], this function + * calculates the heat pump's next state to get the actual active power of + * this state use [[calculateActivePower]] with the generated state * - * @param lastState + * @param lastHpState * Last state of the heat pump * @param relevantData * data of heat pump including * @return - * Booleans if Hp can operate and can be out of operation plus next + * Booleans if Hp can operate and can be out of operation plus the updated * [[HpState]] */ def determineState( - lastState: HpState, + lastHpState: HpState, relevantData: HpRelevantData, ): (Boolean, Boolean, HpState) = { - val (turnOn, canOperate, canBeOutOfOperation, houseDemand, storageDemand) = + + /* FIXME + val (turnOn, canOperate, canBeOutOfOperation, houseDemand, storageDemand) = operatesInNextState(lastState, relevantData) val updatedState = calcState(lastState, relevantData, turnOn, houseDemand, storageDemand) (canOperate, canBeOutOfOperation, updatedState) + */ + // Use lastHpState and relevantData to update state of thermalGrid to the current tick + val (demandHouse, demandThermalStorage, currentThermalGridState) = + thermalGrid.energyDemandAndUpdatedState( + relevantData.currentTick, + lastHpState.ambientTemperature.getOrElse( + relevantData.ambientTemperature + ), + relevantData.ambientTemperature, + lastHpState.thermalGridState, + ) + + // Determining the operation point and limitations at this tick + val (turnOn, canOperate, canBeOutOfOperation) = + operatesInNextState( + lastHpState, + currentThermalGridState, + relevantData, + demandHouse, + demandThermalStorage, + ) + + // Updating the HpState + val updatedState = + calcState(lastHpState, relevantData, turnOn) + (canOperate, canBeOutOfOperation, updatedState) } /** Depending on the input, this function decides whether the heat pump will @@ -143,19 +171,31 @@ final case class HpModel( * met or the heat pump currently is in operation and the grid is able to * handle additional energy * - * @param state - * Current state of the heat pump + * @param lastState + * last state of the heat pump + * @param currentThermalGridState + * to current tick updated state of the thermalGrid * @param relevantData * Relevant (external) data + * @param demandHouse + * ThermalEnergyDemand of the house + * @param demandThermalStorage + * ThermalEnergyDemand of the thermal storage * @return * boolean defining if heat pump runs in next time step, if it can be in * operation and out of operation plus the demand of house and storage */ private def operatesInNextState( - state: HpState, + lastState: HpState, + currentThermalGridState: ThermalGridState, relevantData: HpRelevantData, + demandHouse: ThermalEnergyDemand, + demandThermalStorage: ThermalEnergyDemand, ): (Boolean, Boolean, Boolean, Boolean, Boolean) = { - val (demandHouse, demandThermalStorage, updatedState) = + + //FIXME + /* + val (demandHouse, demandThermalStorage, updatedState) = thermalGrid.energyDemandAndUpdatedState( relevantData.currentTick, state.ambientTemperature.getOrElse(relevantData.ambientTemperature), @@ -185,13 +225,59 @@ final case class HpModel( (turnHpOn, canOperate, canBeOutOfOperation, houseDemand, heatStorageDemand) } + */ - def determineDemandBooleans( - hpState: HpState, - updatedGridState: ThermalGridState, - demandHouse: ThermalEnergyDemand, - demandThermalStorage: ThermalEnergyDemand, - ): (Boolean, Boolean, Boolean) = { + /* + + val ( + houseHasDemand, + heatStorageHasDemand, + noThermalStorageOrThermalStorageIsEmpty, + ) = determineDemandBooleans( + lastState, + currentThermalGridState, + demandHouse, + demandThermalStorage, + ) + + val turnHpOn: Boolean = + houseHasDemand || heatStorageHasDemand + + val canOperate = + demandHouse.hasRequiredDemand || demandHouse.hasAdditionalDemand || + demandThermalStorage.hasRequiredDemand || demandThermalStorage.hasAdditionalDemand + val canBeOutOfOperation = + !(demandHouse.hasRequiredDemand && noThermalStorageOrThermalStorageIsEmpty) + + (turnHpOn, canOperate, canBeOutOfOperation) + */ + + } + + /** This method will return booleans whether there is a heat demand of house + * or thermal storage as well as a boolean indicating if there is no thermal + * storage, or it is empty. + * + * @param lastHpState + * Current state of the heat pump + * @param updatedGridState + * The updated state of the [[ThermalGrid]] + * @param demandHouse + * heat demand of the thermal house + * @param demandThermalStorage + * heat demand of the thermal storage + * @return + * First boolean is true, if house has heat demand. Second boolean is true, + * if thermalStorage has heat demand. Third boolean is true, if there is no + * thermalStorage, or it's empty. + */ + + private def determineDemandBooleans( + lastHpState: HpState, + updatedGridState: ThermalGridState, + demandHouse: ThermalEnergyDemand, + demandThermalStorage: ThermalEnergyDemand, + ): (Boolean, Boolean, Boolean) = { implicit val tolerance: Energy = KilowattHours(1e-3) val noThermalStorageOrThermalStorageIsEmpty: Boolean = updatedGridState.storageState.isEmpty || updatedGridState.storageState @@ -200,10 +286,9 @@ final case class HpModel( ) val houseDemand = - (demandHouse.hasRequiredDemand && noThermalStorageOrThermalStorageIsEmpty) || (hpState.isRunning && demandHouse.hasAdditionalDemand) - val heatStorageDemand = { - (demandThermalStorage.hasRequiredDemand) || (hpState.isRunning && demandThermalStorage.hasAdditionalDemand) - } + (demandHouse.hasRequiredDemand && noThermalStorageOrThermalStorageIsEmpty) || (lastHpState.isRunning && demandHouse.hasAdditionalDemand) + val heatStorageDemand = + demandThermalStorage.hasRequiredDemand || (lastHpState.isRunning && demandThermalStorage.hasAdditionalDemand) (houseDemand, heatStorageDemand, noThermalStorageOrThermalStorageIsEmpty) } @@ -211,8 +296,8 @@ final case class HpModel( * calculate inner temperature change of thermal house and update its inner * temperature. * - * @param state - * Current state of the heat pump + * @param lastState + * state of the heat pump until this tick * @param relevantData * data of heat pump including state of the heat pump * @param isRunning @@ -225,13 +310,13 @@ final case class HpModel( * next [[HpState]] */ private def calcState( - state: HpState, + lastState: HpState, relevantData: HpRelevantData, isRunning: Boolean, houseDemand: Boolean, storageDemand: Boolean, ): HpState = { - val lastStateStorageqDot = state.thermalGridState.storageState + val lastStateStorageQDot = lastState.thermalGridState.storageState .map(_.qDot) .getOrElse(zeroKW) @@ -246,8 +331,8 @@ final case class HpModel( val (thermalGridState, maybeThreshold) = thermalGrid.updateState( relevantData.currentTick, - state.thermalGridState, - state.ambientTemperature.getOrElse(relevantData.ambientTemperature), + lastState.thermalGridState, + lastState.ambientTemperature.getOrElse(relevantData.ambientTemperature), relevantData.ambientTemperature, isRunning, newThermalPower, @@ -318,6 +403,15 @@ final case class HpModel( /* If the setpoint value is above 50 % of the electrical power, turn on the heat pump otherwise turn it off */ val turnOn = setPower > (sRated * cosPhiRated * 0.5) + // FIXME + /* val updatedHpState = calcState( + lastState, + data, + turnOn, + ) + + */ + val ( thermalEnergyDemandHouse, thermalEnergyDemandStorage, @@ -423,7 +517,7 @@ object HpModel { * @param qDot * result heat power * @param thermalGridState - * Currently applicable state of the thermal grid + * applicable state of the thermal grid * @param maybeThermalThreshold * An optional threshold of the thermal grid, indicating the next state * change diff --git a/src/main/scala/edu/ie3/simona/model/thermal/ThermalGrid.scala b/src/main/scala/edu/ie3/simona/model/thermal/ThermalGrid.scala index 9926c4aa5a..7ee49f7322 100644 --- a/src/main/scala/edu/ie3/simona/model/thermal/ThermalGrid.scala +++ b/src/main/scala/edu/ie3/simona/model/thermal/ThermalGrid.scala @@ -49,7 +49,7 @@ final case class ThermalGrid( * @param tick * Questioned instance in time * @param lastAmbientTemperature - * Ambient temperature valid up until (not including) the current tick + * Ambient temperature until this tick * @param ambientTemperature * Current ambient temperature in the instance in question * @param state @@ -68,9 +68,9 @@ final case class ThermalGrid( /* First get the energy demand of the houses but only if inner temperature is below target temperature */ val (houseDemand, updatedHouseState) = - house.zip(state.houseState).headOption match { + house.zip(state.houseState) match { case Some((thermalHouse, lastHouseState)) => - val (updatedHouseState, updatedStorageState) = + val (updatedHouseState, _) = thermalHouse.determineState( tick, lastHouseState, @@ -79,7 +79,8 @@ final case class ThermalGrid( lastHouseState.qDot, ) if ( - updatedHouseState.innerTemperature < thermalHouse.targetTemperature | (lastHouseState.qDot > zeroKW && updatedHouseState.innerTemperature < thermalHouse.upperBoundaryTemperature) + updatedHouseState.innerTemperature < thermalHouse.targetTemperature | + (lastHouseState.qDot > zeroKW && updatedHouseState.innerTemperature < thermalHouse.upperBoundaryTemperature) ) { ( thermalHouse.energyDemand(