From 6a991077ff501aec66228d296daacf5fddc622a7 Mon Sep 17 00:00:00 2001 From: danielfeismann Date: Sun, 24 Nov 2024 10:33:47 +0100 Subject: [PATCH 1/9] Introduce ThermalDemandWrapper --- CHANGELOG.md | 1 + .../simona/model/participant/HpModel.scala | 87 +++++++++---------- .../simona/model/thermal/ThermalGrid.scala | 33 +++++-- .../model/thermal/ThermalGridTestData.scala | 24 ++++- .../ThermalGridWithHouseAndStorageSpec.scala | 10 ++- .../ThermalGridWithHouseOnlySpec.scala | 7 +- .../ThermalGridWithStorageOnlySpec.scala | 8 +- 7 files changed, 111 insertions(+), 59 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 44290c1dd2..c23f6be1da 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -39,6 +39,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Add unapply method for ThermalHouseResults [#934](https://github.com/ie3-institute/simona/issues/934) - Added `ApparentPower` to differentiate between different power types [#794](https://github.com/ie3-institute/simona/issues/794) - Update/enhance config documentation [#1013](https://github.com/ie3-institute/simona/issues/1013) +- Introduce ThermalDemandWrapper [#1049](https://github.com/ie3-institute/simona/issues/1049) ### Changed - Adapted to changed data source in PSDM [#435](https://github.com/ie3-institute/simona/issues/435) 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 98d18d9ce3..9a3dd773aa 100644 --- a/src/main/scala/edu/ie3/simona/model/participant/HpModel.scala +++ b/src/main/scala/edu/ie3/simona/model/participant/HpModel.scala @@ -12,7 +12,7 @@ import edu.ie3.simona.model.SystemComponent import edu.ie3.simona.model.participant.HpModel.{HpRelevantData, HpState} import edu.ie3.simona.model.participant.control.QControl import edu.ie3.simona.model.thermal.ThermalGrid.{ - ThermalEnergyDemand, + ThermalDemandWrapper, ThermalGridState, } import edu.ie3.simona.model.thermal.{ThermalGrid, ThermalThreshold} @@ -132,7 +132,7 @@ final case class HpModel( ): (Boolean, Boolean, HpState) = { // Use lastHpState and relevantData to update state of thermalGrid to the current tick - val (demandHouse, demandThermalStorage, currentThermalGridState) = + val (thermalDemandWrapper, currentThermalGridState) = thermalGrid.energyDemandAndUpdatedState( relevantData.currentTick, lastHpState.ambientTemperature.getOrElse( @@ -148,13 +148,12 @@ final case class HpModel( lastHpState, currentThermalGridState, relevantData, - demandHouse, - demandThermalStorage, + thermalDemandWrapper, ) // Updating the HpState val updatedState = - calcState(lastHpState, relevantData, turnOn) + calcState(lastHpState, relevantData, turnOn, thermalDemandWrapper) (canOperate, canBeOutOfOperation, updatedState) } @@ -170,10 +169,8 @@ final case class HpModel( * 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 + * @param thermalDemands + * ThermalEnergyDemand of the house and the thermal storage * @return * boolean defining if heat pump runs in next time step, if it can be in * operation and can be out of operation @@ -182,23 +179,21 @@ final case class HpModel( lastState: HpState, currentThermalGridState: ThermalGridState, relevantData: HpRelevantData, - demandHouse: ThermalEnergyDemand, - demandThermalStorage: ThermalEnergyDemand, + thermalDemands: ThermalDemandWrapper, ): (Boolean, Boolean, Boolean) = { - val ( - houseHasDemand, - heatStorageHasDemand, - noThermalStorageOrThermalStorageIsEmpty, - ) = determineDemandBooleans( - lastState, - currentThermalGridState, - demandHouse, - demandThermalStorage, + val demandHouse = thermalDemands.houseDemand + val demandThermalStorage = thermalDemands.heatStorageDemand + + val noThermalStorageOrThermalStorageIsEmpty = determineThermalStorageStatus( + currentThermalGridState ) - val turnHpOn: Boolean = - houseHasDemand || heatStorageHasDemand + val turnHpOn = + (demandHouse.hasRequiredDemand && noThermalStorageOrThermalStorageIsEmpty) || + (demandHouse.hasAdditionalDemand && lastState.isRunning) || + demandThermalStorage.hasRequiredDemand || + (demandThermalStorage.hasAdditionalDemand && lastState.isRunning) val canOperate = demandHouse.hasRequiredDemand || demandHouse.hasAdditionalDemand || @@ -206,33 +201,26 @@ final case class HpModel( val canBeOutOfOperation = !(demandHouse.hasRequiredDemand && noThermalStorageOrThermalStorageIsEmpty) - (turnHpOn, canOperate, canBeOutOfOperation) + ( + 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. + * boolean which 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) = { + private def determineThermalStorageStatus( + updatedGridState: ThermalGridState + ): Boolean = { implicit val tolerance: Energy = KilowattHours(1e-3) val noThermalStorageOrThermalStorageIsEmpty: Boolean = updatedGridState.storageState.isEmpty || updatedGridState.storageState @@ -240,11 +228,7 @@ final case class HpModel( _.storedEnergy =~ zeroKWh ) - val houseDemand = - (demandHouse.hasRequiredDemand && noThermalStorageOrThermalStorageIsEmpty) || (lastHpState.isRunning && demandHouse.hasAdditionalDemand) - val heatStorageDemand = - demandThermalStorage.hasRequiredDemand || (lastHpState.isRunning && demandThermalStorage.hasAdditionalDemand) - (houseDemand, heatStorageDemand, noThermalStorageOrThermalStorageIsEmpty) + noThermalStorageOrThermalStorageIsEmpty } /** Calculate state depending on whether heat pump is needed or not. Also @@ -257,6 +241,8 @@ final case class HpModel( * data of heat pump including state of the heat pump * @param isRunning * determines whether the heat pump is running or not + * @param demandWrapper + * holds the thermal demands of the thermal units (house, storage) * @return * next [[HpState]] */ @@ -264,6 +250,7 @@ final case class HpModel( lastState: HpState, relevantData: HpRelevantData, isRunning: Boolean, + demandWrapper: ThermalDemandWrapper, ): HpState = { val lastStateStorageQDot = lastState.thermalGridState.storageState .map(_.qDot) @@ -346,13 +333,25 @@ final case class HpModel( lastState: HpState, setPower: Power, ): (HpState, FlexChangeIndicator) = { - /* If the setpoint value is above 50 % of the electrical power, turn on the heat pump otherwise turn it off */ + /* If the set point value is above 50 % of the electrical power, turn on the heat pump otherwise turn it off */ val turnOn = setPower > (sRated.toActivePower(cosPhiRated) * 0.5) + val ( + thermalDemands, + _, + ) = + thermalGrid.energyDemandAndUpdatedState( + data.currentTick, + lastState.ambientTemperature.getOrElse(data.ambientTemperature), + data.ambientTemperature, + lastState.thermalGridState, + ) + val updatedHpState = calcState( lastState, data, turnOn, + thermalDemands, ) ( 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 50f56e1849..6116783509 100644 --- a/src/main/scala/edu/ie3/simona/model/thermal/ThermalGrid.scala +++ b/src/main/scala/edu/ie3/simona/model/thermal/ThermalGrid.scala @@ -15,6 +15,7 @@ import edu.ie3.datamodel.models.result.thermal.{ } import edu.ie3.simona.exceptions.agent.InconsistentStateException import edu.ie3.simona.model.thermal.ThermalGrid.{ + ThermalDemandWrapper, ThermalEnergyDemand, ThermalGridState, } @@ -63,7 +64,7 @@ final case class ThermalGrid( lastAmbientTemperature: Temperature, ambientTemperature: Temperature, state: ThermalGridState, - ): (ThermalEnergyDemand, ThermalEnergyDemand, ThermalGridState) = { + ): (ThermalDemandWrapper, ThermalGridState) = { /* First get the energy demand of the houses but only if inner temperature is below target temperature */ val (houseDemand, updatedHouseState) = @@ -134,13 +135,15 @@ final case class ThermalGrid( } ( - ThermalEnergyDemand( - houseDemand.required, - houseDemand.possible, - ), - ThermalEnergyDemand( - storageDemand.required, - storageDemand.possible, + ThermalDemandWrapper( + ThermalEnergyDemand( + houseDemand.required, + houseDemand.possible, + ), + ThermalEnergyDemand( + storageDemand.required, + storageDemand.possible, + ), ), ThermalGridState(updatedHouseState, updatedStorageState), ) @@ -191,6 +194,8 @@ final case class ThermalGrid( * Current state of the houses * @param qDot * Infeed to the grid + * @param thermalDemands + * holds the thermal demands of the thermal units (house, storage) * @return * Updated thermal grid state */ @@ -540,6 +545,18 @@ object ThermalGrid { thermalGrid.storage.map(_.startingState), ) + /** Wraps the demand of thermal units (thermal house, thermal storage). + * + * @param houseDemand + * the demand of the thermal house + * @param heatStorageDemand + * the demand of the thermal heat storage + */ + final case class ThermalDemandWrapper private ( + houseDemand: ThermalEnergyDemand, + heatStorageDemand: ThermalEnergyDemand, + ) + /** Defines the thermal energy demand of a thermal grid. It comprises the * absolutely required energy demand to reach the target state as well as an * energy, that can be handled. The possible energy always has to be greater diff --git a/src/test/scala/edu/ie3/simona/model/thermal/ThermalGridTestData.scala b/src/test/scala/edu/ie3/simona/model/thermal/ThermalGridTestData.scala index 6011c6caf2..df410786c5 100644 --- a/src/test/scala/edu/ie3/simona/model/thermal/ThermalGridTestData.scala +++ b/src/test/scala/edu/ie3/simona/model/thermal/ThermalGridTestData.scala @@ -9,7 +9,12 @@ package edu.ie3.simona.model.thermal import edu.ie3.datamodel.models.OperationTime import edu.ie3.datamodel.models.input.OperatorInput import edu.ie3.datamodel.models.input.thermal.ThermalBusInput -import squants.energy.{Kilowatts, Power} +import edu.ie3.simona.model.thermal.ThermalGrid.{ + ThermalDemandWrapper, + ThermalEnergyDemand, +} +import edu.ie3.util.scala.quantities.DefaultQuantities.zeroKWh +import squants.energy.{KilowattHours, Kilowatts, Power} import squants.thermal.{Celsius, Temperature} import java.util.UUID @@ -25,4 +30,21 @@ trait ThermalGridTestData { protected val testGridQDotInfeed: Power = Kilowatts(15d) protected val testGridQDotConsumption: Power = Kilowatts(-42d) protected val testGridQDotConsumptionHigh: Power = Kilowatts(-200d) + protected val noThermalDemand: ThermalDemandWrapper = + ThermalDemandWrapper( + ThermalEnergyDemand(zeroKWh, zeroKWh), + ThermalEnergyDemand(zeroKWh, zeroKWh), + ) + protected val onlyThermalDemandOfHouse: ThermalDemandWrapper = + ThermalDemandWrapper( + ThermalEnergyDemand(KilowattHours(1), KilowattHours(2)), + ThermalEnergyDemand(zeroKWh, zeroKWh), + ) + protected val onlyThermalDemandOfHeatStorage: ThermalDemandWrapper = + ThermalDemandWrapper( + ThermalEnergyDemand(zeroKWh, zeroKWh), + ThermalEnergyDemand(KilowattHours(1), KilowattHours(2)), + ) + protected val isRunning: Boolean = true + protected val isNotRunning: Boolean = false } diff --git a/src/test/scala/edu/ie3/simona/model/thermal/ThermalGridWithHouseAndStorageSpec.scala b/src/test/scala/edu/ie3/simona/model/thermal/ThermalGridWithHouseAndStorageSpec.scala index 7d8d2c27f1..cc35927238 100644 --- a/src/test/scala/edu/ie3/simona/model/thermal/ThermalGridWithHouseAndStorageSpec.scala +++ b/src/test/scala/edu/ie3/simona/model/thermal/ThermalGridWithHouseAndStorageSpec.scala @@ -20,6 +20,7 @@ import edu.ie3.simona.model.thermal.ThermalStorage.ThermalStorageThreshold.{ StorageFull, } import edu.ie3.simona.test.common.UnitSpec +import edu.ie3.util.scala.quantities.DefaultQuantities.{zeroKW, zeroKWh} import squants.energy._ import squants.thermal.Celsius import squants.{Energy, Kelvin, Power, Temperature} @@ -97,13 +98,16 @@ class ThermalGridWithHouseAndStorageSpec "deliver the house demand (no demand) with added flexibility by storage" in { val tick = 10800 // after three hours - val (houseDemand, storageDemand, updatedThermalGridState) = + val (thermalDemands, updatedThermalGridState) = thermalGrid.energyDemandAndUpdatedState( tick, testGridAmbientTemperature, testGridAmbientTemperature, ThermalGrid.startingState(thermalGrid), ) + val houseDemand = thermalDemands.houseDemand + val storageDemand = thermalDemands.heatStorageDemand + houseDemand.required should approximate(zeroKWh) houseDemand.possible should approximate(KilowattHours(31.05009722d)) storageDemand.required should approximate(KilowattHours(1150d)) @@ -120,7 +124,7 @@ class ThermalGridWithHouseAndStorageSpec val tick = 10800 // after three hours val startingState = ThermalGrid.startingState(thermalGrid) - val (houseDemand, storageDemand, updatedThermalGridState) = + val (thermalDemands, updatedThermalGridState) = thermalGrid.energyDemandAndUpdatedState( tick, testGridAmbientTemperature, @@ -131,6 +135,8 @@ class ThermalGridWithHouseAndStorageSpec ) ), ) + val houseDemand = thermalDemands.houseDemand + val storageDemand = thermalDemands.heatStorageDemand houseDemand.required should approximate(KilowattHours(45.6000555)) houseDemand.possible should approximate(KilowattHours(75.600055555)) diff --git a/src/test/scala/edu/ie3/simona/model/thermal/ThermalGridWithHouseOnlySpec.scala b/src/test/scala/edu/ie3/simona/model/thermal/ThermalGridWithHouseOnlySpec.scala index 6b14ee3780..14e76434b9 100644 --- a/src/test/scala/edu/ie3/simona/model/thermal/ThermalGridWithHouseOnlySpec.scala +++ b/src/test/scala/edu/ie3/simona/model/thermal/ThermalGridWithHouseOnlySpec.scala @@ -13,8 +13,8 @@ import edu.ie3.simona.model.thermal.ThermalHouse.ThermalHouseThreshold.{ HouseTemperatureLowerBoundaryReached, HouseTemperatureUpperBoundaryReached, } -import edu.ie3.util.scala.quantities.DefaultQuantities.{zeroKW, zeroKWh} import edu.ie3.simona.test.common.UnitSpec +import edu.ie3.util.scala.quantities.DefaultQuantities.{zeroKW, zeroKWh} import squants.energy._ import squants.thermal.Celsius import squants.{Energy, Kelvin, Power, Temperature} @@ -81,7 +81,7 @@ class ThermalGridWithHouseOnlySpec extends UnitSpec with ThermalHouseTestData { expectedHouseStartingState, ) - val (houseDemand, storageDemand, updatedThermalGridState) = + val (thermalDemands, updatedThermalGridState) = thermalGrid.energyDemandAndUpdatedState( tick, testGridAmbientTemperature, @@ -89,6 +89,9 @@ class ThermalGridWithHouseOnlySpec extends UnitSpec with ThermalHouseTestData { ThermalGrid.startingState(thermalGrid), ) + val houseDemand = thermalDemands.houseDemand + val storageDemand = thermalDemands.heatStorageDemand + houseDemand.required should approximate(expectedHouseDemand.required) houseDemand.possible should approximate(expectedHouseDemand.possible) storageDemand.required should approximate(zeroKWh) diff --git a/src/test/scala/edu/ie3/simona/model/thermal/ThermalGridWithStorageOnlySpec.scala b/src/test/scala/edu/ie3/simona/model/thermal/ThermalGridWithStorageOnlySpec.scala index ead1ff6b03..b8aaae8d9d 100644 --- a/src/test/scala/edu/ie3/simona/model/thermal/ThermalGridWithStorageOnlySpec.scala +++ b/src/test/scala/edu/ie3/simona/model/thermal/ThermalGridWithStorageOnlySpec.scala @@ -81,13 +81,15 @@ class ThermalGridWithStorageOnlySpec "deliver the capabilities of the storage" in { val tick = 10800 // after three hours - val (houseDemand, storageDemand, updatedThermalGridState) = + val (thermalDemands, updatedThermalGridState) = thermalGrid.energyDemandAndUpdatedState( tick, testGridAmbientTemperature, testGridAmbientTemperature, ThermalGrid.startingState(thermalGrid), ) + val houseDemand = thermalDemands.houseDemand + val storageDemand = thermalDemands.heatStorageDemand houseDemand.required should approximate(zeroKWh) houseDemand.possible should approximate(zeroKWh) @@ -102,7 +104,7 @@ class ThermalGridWithStorageOnlySpec "deliver the capabilities of a half full storage" in { val tick = 10800 // after three hours - val (houseDemand, storageDemand, updatedThermalGridState) = + val (thermalDemands, updatedThermalGridState) = thermalGrid.energyDemandAndUpdatedState( tick, testGridAmbientTemperature, @@ -112,6 +114,8 @@ class ThermalGridWithStorageOnlySpec Some(ThermalStorageState(0L, KilowattHours(575d), zeroKW)), ), ) + val houseDemand = thermalDemands.houseDemand + val storageDemand = thermalDemands.heatStorageDemand houseDemand.required should approximate(zeroKWh) houseDemand.possible should approximate(zeroKWh) From ca039183a35e8623d4c4c2dc17c80c63c4706ddf Mon Sep 17 00:00:00 2001 From: danielfeismann Date: Sun, 24 Nov 2024 11:07:18 +0100 Subject: [PATCH 2/9] sonarQube --- src/main/scala/edu/ie3/simona/model/participant/HpModel.scala | 3 --- .../model/thermal/ThermalGridWithHouseAndStorageSpec.scala | 1 - 2 files changed, 4 deletions(-) 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 9a3dd773aa..586006600f 100644 --- a/src/main/scala/edu/ie3/simona/model/participant/HpModel.scala +++ b/src/main/scala/edu/ie3/simona/model/participant/HpModel.scala @@ -241,8 +241,6 @@ final case class HpModel( * data of heat pump including state of the heat pump * @param isRunning * determines whether the heat pump is running or not - * @param demandWrapper - * holds the thermal demands of the thermal units (house, storage) * @return * next [[HpState]] */ @@ -250,7 +248,6 @@ final case class HpModel( lastState: HpState, relevantData: HpRelevantData, isRunning: Boolean, - demandWrapper: ThermalDemandWrapper, ): HpState = { val lastStateStorageQDot = lastState.thermalGridState.storageState .map(_.qDot) diff --git a/src/test/scala/edu/ie3/simona/model/thermal/ThermalGridWithHouseAndStorageSpec.scala b/src/test/scala/edu/ie3/simona/model/thermal/ThermalGridWithHouseAndStorageSpec.scala index cc35927238..88172c712f 100644 --- a/src/test/scala/edu/ie3/simona/model/thermal/ThermalGridWithHouseAndStorageSpec.scala +++ b/src/test/scala/edu/ie3/simona/model/thermal/ThermalGridWithHouseAndStorageSpec.scala @@ -13,7 +13,6 @@ import edu.ie3.simona.model.thermal.ThermalHouse.ThermalHouseThreshold.{ HouseTemperatureLowerBoundaryReached, HouseTemperatureUpperBoundaryReached, } -import edu.ie3.util.scala.quantities.DefaultQuantities.{zeroKW, zeroKWh} import edu.ie3.simona.model.thermal.ThermalStorage.ThermalStorageState import edu.ie3.simona.model.thermal.ThermalStorage.ThermalStorageThreshold.{ StorageEmpty, From 6106ad7ba963801377017dfb0911237c63618071 Mon Sep 17 00:00:00 2001 From: danielfeismann Date: Sun, 24 Nov 2024 11:09:56 +0100 Subject: [PATCH 3/9] fix HpModel --- .../edu/ie3/simona/model/participant/HpModel.scala | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) 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 586006600f..254d18f136 100644 --- a/src/main/scala/edu/ie3/simona/model/participant/HpModel.scala +++ b/src/main/scala/edu/ie3/simona/model/participant/HpModel.scala @@ -153,7 +153,7 @@ final case class HpModel( // Updating the HpState val updatedState = - calcState(lastHpState, relevantData, turnOn, thermalDemandWrapper) + calcState(lastHpState, relevantData, turnOn) (canOperate, canBeOutOfOperation, updatedState) } @@ -333,22 +333,10 @@ final case class HpModel( /* If the set point value is above 50 % of the electrical power, turn on the heat pump otherwise turn it off */ val turnOn = setPower > (sRated.toActivePower(cosPhiRated) * 0.5) - val ( - thermalDemands, - _, - ) = - thermalGrid.energyDemandAndUpdatedState( - data.currentTick, - lastState.ambientTemperature.getOrElse(data.ambientTemperature), - data.ambientTemperature, - lastState.thermalGridState, - ) - val updatedHpState = calcState( lastState, data, turnOn, - thermalDemands, ) ( From c12948041b899c879618dc330c6462df8bcbc5e5 Mon Sep 17 00:00:00 2001 From: danielfeismann Date: Mon, 25 Nov 2024 11:34:51 +0100 Subject: [PATCH 4/9] include reviewers comments --- .../edu/ie3/simona/model/participant/HpModel.scala | 11 ++++------- .../edu/ie3/simona/model/thermal/ThermalGrid.scala | 2 -- 2 files changed, 4 insertions(+), 9 deletions(-) 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 254d18f136..216353be2c 100644 --- a/src/main/scala/edu/ie3/simona/model/participant/HpModel.scala +++ b/src/main/scala/edu/ie3/simona/model/participant/HpModel.scala @@ -222,13 +222,10 @@ final case class HpModel( updatedGridState: ThermalGridState ): Boolean = { implicit val tolerance: Energy = KilowattHours(1e-3) - val noThermalStorageOrThermalStorageIsEmpty: Boolean = - updatedGridState.storageState.isEmpty || updatedGridState.storageState - .exists( - _.storedEnergy =~ zeroKWh - ) - - noThermalStorageOrThermalStorageIsEmpty + updatedGridState.storageState.isEmpty || updatedGridState.storageState + .exists( + _.storedEnergy =~ zeroKWh + ) } /** Calculate state depending on whether heat pump is needed or not. Also 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 6116783509..fa1eb7d017 100644 --- a/src/main/scala/edu/ie3/simona/model/thermal/ThermalGrid.scala +++ b/src/main/scala/edu/ie3/simona/model/thermal/ThermalGrid.scala @@ -194,8 +194,6 @@ final case class ThermalGrid( * Current state of the houses * @param qDot * Infeed to the grid - * @param thermalDemands - * holds the thermal demands of the thermal units (house, storage) * @return * Updated thermal grid state */ From e85273524541d18836679a93e93774eaedcf3090 Mon Sep 17 00:00:00 2001 From: danielfeismann Date: Mon, 25 Nov 2024 11:59:44 +0100 Subject: [PATCH 5/9] fix after merge conflicts --- src/main/scala/edu/ie3/simona/model/thermal/ThermalGrid.scala | 2 +- .../model/thermal/ThermalGridWithHouseAndStorageSpec.scala | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) 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 2f42aa6e15..c7e107d55a 100644 --- a/src/main/scala/edu/ie3/simona/model/thermal/ThermalGrid.scala +++ b/src/main/scala/edu/ie3/simona/model/thermal/ThermalGrid.scala @@ -58,7 +58,7 @@ final case class ThermalGrid( def energyDemandAndUpdatedState( relevantData: HpRelevantData, lastHpState: HpState, - ): (ThermalEnergyDemand, ThermalEnergyDemand, ThermalGridState) = { + ): (ThermalDemandWrapper, ThermalGridState) = { /* First get the energy demand of the houses but only if inner temperature is below target temperature */ val (houseDemand, updatedHouseState) = diff --git a/src/test/scala/edu/ie3/simona/model/thermal/ThermalGridWithHouseAndStorageSpec.scala b/src/test/scala/edu/ie3/simona/model/thermal/ThermalGridWithHouseAndStorageSpec.scala index a7e8118dbc..5d19bfdd73 100644 --- a/src/test/scala/edu/ie3/simona/model/thermal/ThermalGridWithHouseAndStorageSpec.scala +++ b/src/test/scala/edu/ie3/simona/model/thermal/ThermalGridWithHouseAndStorageSpec.scala @@ -109,7 +109,7 @@ class ThermalGridWithHouseAndStorageSpec ThermalGrid.startingState(thermalGrid), None, ) - val (houseDemand, storageDemand, updatedThermalGridState) = + val (thermalDemands, updatedThermalGridState) = thermalGrid.energyDemandAndUpdatedState( relevantData, lastHpState, @@ -149,7 +149,7 @@ class ThermalGridWithHouseAndStorageSpec None, ) - val (houseDemand, storageDemand, updatedThermalGridState) = + val (thermalDemands, updatedThermalGridState) = thermalGrid.energyDemandAndUpdatedState( relevantData, lastHpState, From 6a0b5f372331907780c3fe12aa18d4f3c2d64026 Mon Sep 17 00:00:00 2001 From: danielfeismann Date: Mon, 25 Nov 2024 15:18:48 +0100 Subject: [PATCH 6/9] move method to check for empty storage to ThermalGridState --- .../simona/model/participant/HpModel.scala | 24 ++----------------- .../simona/model/thermal/ThermalGrid.scala | 22 ++++++++++++++++- 2 files changed, 23 insertions(+), 23 deletions(-) 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 ab4db17eb2..ead8e89f02 100644 --- a/src/main/scala/edu/ie3/simona/model/participant/HpModel.scala +++ b/src/main/scala/edu/ie3/simona/model/participant/HpModel.scala @@ -22,8 +22,8 @@ import edu.ie3.util.quantities.PowerSystemUnits import edu.ie3.util.scala.OperationInterval import edu.ie3.util.scala.quantities.DefaultQuantities._ import edu.ie3.util.scala.quantities.{ApparentPower, Kilovoltamperes} -import squants.energy.{KilowattHours, Kilowatts} -import squants.{Energy, Power, Temperature} +import squants.energy.Kilowatts +import squants.{Power, Temperature} import java.time.ZonedDateTime import java.util.UUID @@ -204,26 +204,6 @@ final case class HpModel( ) } - /** 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 updatedGridState - * The updated state of the [[ThermalGrid]] - * @return - * boolean which is true, if there is no thermalStorage, or it's empty. - */ - - private def determineThermalStorageStatus( - updatedGridState: ThermalGridState - ): Boolean = { - implicit val tolerance: Energy = KilowattHours(1e-3) - updatedGridState.storageState.isEmpty || updatedGridState.storageState - .exists( - _.storedEnergy =~ zeroKWh - ) - } - /** Calculate state depending on whether heat pump is needed or not. Also * calculate inner temperature change of thermal house and update its inner * temperature. 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 c7e107d55a..85627e2057 100644 --- a/src/main/scala/edu/ie3/simona/model/thermal/ThermalGrid.scala +++ b/src/main/scala/edu/ie3/simona/model/thermal/ThermalGrid.scala @@ -25,7 +25,7 @@ import edu.ie3.simona.model.thermal.ThermalStorage.ThermalStorageState import edu.ie3.simona.util.TickUtil.TickLong import edu.ie3.util.quantities.QuantityUtils.RichQuantityDouble import edu.ie3.util.scala.quantities.DefaultQuantities._ -import squants.energy.Kilowatts +import squants.energy.{KilowattHours, Kilowatts} import squants.{Energy, Power, Temperature} import java.time.ZonedDateTime @@ -539,6 +539,26 @@ object ThermalGrid { thermalGrid.storage.map(_.startingState), ) + /** 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 updatedGridState + * The updated state of the [[ThermalGrid]] + * @return + * boolean which is true, if there is no thermalStorage, or it's empty. + */ + + def isThermalStorageEmpty( + updatedGridState: ThermalGridState + ): Boolean = { + implicit val tolerance: Energy = KilowattHours(1e-3) + updatedGridState.storageState.isEmpty || updatedGridState.storageState + .exists( + _.storedEnergy =~ zeroKWh + ) + } + /** Wraps the demand of thermal units (thermal house, thermal storage). * * @param houseDemand From 60442a73f61606295fa99998911f4b51cef261b0 Mon Sep 17 00:00:00 2001 From: danielfeismann Date: Mon, 25 Nov 2024 15:19:06 +0100 Subject: [PATCH 7/9] rename method --- .../scala/edu/ie3/simona/model/participant/HpModel.scala | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) 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 ead8e89f02..875f14654a 100644 --- a/src/main/scala/edu/ie3/simona/model/participant/HpModel.scala +++ b/src/main/scala/edu/ie3/simona/model/participant/HpModel.scala @@ -181,9 +181,10 @@ final case class HpModel( val demandHouse = thermalDemands.houseDemand val demandThermalStorage = thermalDemands.heatStorageDemand - val noThermalStorageOrThermalStorageIsEmpty = determineThermalStorageStatus( - currentThermalGridState - ) + val noThermalStorageOrThermalStorageIsEmpty = + ThermalGrid.isThermalStorageEmpty( + currentThermalGridState + ) val turnHpOn = (demandHouse.hasRequiredDemand && noThermalStorageOrThermalStorageIsEmpty) || From 6d97c3bcfad8a51c00ca1be0781b9ab181e437be Mon Sep 17 00:00:00 2001 From: danielfeismann Date: Mon, 25 Nov 2024 15:19:27 +0100 Subject: [PATCH 8/9] enhancing tests to check for empty storage in ThermalGridState --- .../model/thermal/ThermalGridSpec.scala | 50 ++++++++++++++++++- 1 file changed, 48 insertions(+), 2 deletions(-) diff --git a/src/test/scala/edu/ie3/simona/model/thermal/ThermalGridSpec.scala b/src/test/scala/edu/ie3/simona/model/thermal/ThermalGridSpec.scala index e4c1c14c70..b137abde99 100644 --- a/src/test/scala/edu/ie3/simona/model/thermal/ThermalGridSpec.scala +++ b/src/test/scala/edu/ie3/simona/model/thermal/ThermalGridSpec.scala @@ -6,13 +6,19 @@ package edu.ie3.simona.model.thermal +import edu.ie3.datamodel.models.input.thermal.ThermalStorageInput import edu.ie3.simona.model.thermal.ThermalGrid.ThermalEnergyDemand import edu.ie3.simona.test.common.UnitSpec -import squants.energy.{MegawattHours, WattHours, Watts} +import squants.energy.{KilowattHours, MegawattHours, WattHours, Watts} import squants.thermal.Celsius import squants.{Energy, Power, Temperature} -class ThermalGridSpec extends UnitSpec { +import scala.jdk.CollectionConverters._ + +class ThermalGridSpec + extends UnitSpec + with ThermalHouseTestData + with ThermalStorageTestData { implicit val tempTolerance: Temperature = Celsius(1e-3) implicit val powerTolerance: Power = Watts(1e-3) @@ -97,4 +103,44 @@ class ThermalGridSpec extends UnitSpec { } } } + "ThermalGridState" should { + val thermalGridOnlyHouse = ThermalGrid( + new edu.ie3.datamodel.models.input.container.ThermalGrid( + thermalBusInput, + Set(thermalHouseInput).asJava, + Set.empty[ThermalStorageInput].asJava, + ) + ) + + "return true when there is no storage" in { + val startingState = ThermalGrid.startingState(thermalGridOnlyHouse) + val result = ThermalGrid.isThermalStorageEmpty(startingState) + result shouldBe true + } + + val thermalGrid = ThermalGrid( + new edu.ie3.datamodel.models.input.container.ThermalGrid( + thermalBusInput, + Set(thermalHouseInput).asJava, + Set[ThermalStorageInput](thermalStorageInput).asJava, + ) + ) + + "return true when all stored energy is effectively zero" in { + val startingState = ThermalGrid.startingState(thermalGrid) + val result = ThermalGrid.isThermalStorageEmpty(startingState) + result shouldBe true + } + + "return false when storage is not empty" in { + val startingState = ThermalGrid.startingState(thermalGrid) + val gridState = startingState.copy(storageState = + startingState.storageState.map(storageState => + storageState.copy(storedEnergy = KilowattHours(1)) + ) + ) + val result = ThermalGrid.isThermalStorageEmpty(gridState) + result shouldBe false + } + } } From 202ecb10a2be76879569f6c72aabaeb4f1014fc7 Mon Sep 17 00:00:00 2001 From: danielfeismann Date: Mon, 25 Nov 2024 15:53:42 +0100 Subject: [PATCH 9/9] move isThermalStorageEmpty into ThermalGridState --- .../simona/model/participant/HpModel.scala | 5 +-- .../simona/model/thermal/ThermalGrid.scala | 39 +++++++++---------- .../model/thermal/ThermalGridSpec.scala | 16 ++++---- 3 files changed, 27 insertions(+), 33 deletions(-) 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 875f14654a..5911a9cade 100644 --- a/src/main/scala/edu/ie3/simona/model/participant/HpModel.scala +++ b/src/main/scala/edu/ie3/simona/model/participant/HpModel.scala @@ -180,11 +180,8 @@ final case class HpModel( val demandHouse = thermalDemands.houseDemand val demandThermalStorage = thermalDemands.heatStorageDemand - val noThermalStorageOrThermalStorageIsEmpty = - ThermalGrid.isThermalStorageEmpty( - currentThermalGridState - ) + currentThermalGridState.isThermalStorageEmpty val turnHpOn = (demandHouse.hasRequiredDemand && noThermalStorageOrThermalStorageIsEmpty) || 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 85627e2057..70f92e242c 100644 --- a/src/main/scala/edu/ie3/simona/model/thermal/ThermalGrid.scala +++ b/src/main/scala/edu/ie3/simona/model/thermal/ThermalGrid.scala @@ -531,7 +531,24 @@ object ThermalGrid { final case class ThermalGridState( houseState: Option[ThermalHouseState], storageState: Option[ThermalStorageState], - ) + ) { + + /** 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. + * + * @return + * boolean which is true, if there is no thermalStorage, or it's empty. + */ + + def isThermalStorageEmpty: Boolean = { + implicit val tolerance: Energy = KilowattHours(1e-3) + storageState.isEmpty || storageState + .exists( + _.storedEnergy =~ zeroKWh + ) + } + } def startingState(thermalGrid: ThermalGrid): ThermalGridState = ThermalGridState( @@ -539,26 +556,6 @@ object ThermalGrid { thermalGrid.storage.map(_.startingState), ) - /** 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 updatedGridState - * The updated state of the [[ThermalGrid]] - * @return - * boolean which is true, if there is no thermalStorage, or it's empty. - */ - - def isThermalStorageEmpty( - updatedGridState: ThermalGridState - ): Boolean = { - implicit val tolerance: Energy = KilowattHours(1e-3) - updatedGridState.storageState.isEmpty || updatedGridState.storageState - .exists( - _.storedEnergy =~ zeroKWh - ) - } - /** Wraps the demand of thermal units (thermal house, thermal storage). * * @param houseDemand diff --git a/src/test/scala/edu/ie3/simona/model/thermal/ThermalGridSpec.scala b/src/test/scala/edu/ie3/simona/model/thermal/ThermalGridSpec.scala index b137abde99..bb34a88682 100644 --- a/src/test/scala/edu/ie3/simona/model/thermal/ThermalGridSpec.scala +++ b/src/test/scala/edu/ie3/simona/model/thermal/ThermalGridSpec.scala @@ -113,8 +113,8 @@ class ThermalGridSpec ) "return true when there is no storage" in { - val startingState = ThermalGrid.startingState(thermalGridOnlyHouse) - val result = ThermalGrid.isThermalStorageEmpty(startingState) + val initialState = ThermalGrid.startingState(thermalGridOnlyHouse) + val result = initialState.isThermalStorageEmpty result shouldBe true } @@ -127,19 +127,19 @@ class ThermalGridSpec ) "return true when all stored energy is effectively zero" in { - val startingState = ThermalGrid.startingState(thermalGrid) - val result = ThermalGrid.isThermalStorageEmpty(startingState) + val initialState = ThermalGrid.startingState(thermalGrid) + val result = initialState.isThermalStorageEmpty result shouldBe true } "return false when storage is not empty" in { - val startingState = ThermalGrid.startingState(thermalGrid) - val gridState = startingState.copy(storageState = - startingState.storageState.map(storageState => + val initialState = ThermalGrid.startingState(thermalGrid) + val gridState = initialState.copy(storageState = + initialState.storageState.map(storageState => storageState.copy(storedEnergy = KilowattHours(1)) ) ) - val result = ThermalGrid.isThermalStorageEmpty(gridState) + val result = gridState.isThermalStorageEmpty result shouldBe false } }