From ebdc0dcf83a2fcede625ea5f0e7a248bb844c34c Mon Sep 17 00:00:00 2001 From: danielfeismann Date: Thu, 22 Aug 2024 11:49:12 +0200 Subject: [PATCH 01/65] fmt --- .../simona/model/participant/HpModel.scala | 49 +++++++++++++------ 1 file changed, 35 insertions(+), 14 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 83be5fb4dc..1473ee39f1 100644 --- a/src/main/scala/edu/ie3/simona/model/participant/HpModel.scala +++ b/src/main/scala/edu/ie3/simona/model/participant/HpModel.scala @@ -11,7 +11,10 @@ import edu.ie3.simona.agent.participant.data.Data.PrimaryData.ApparentPowerAndHe 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, ThermalGridState} +import edu.ie3.simona.model.thermal.ThermalGrid.{ + ThermalEnergyDemand, + ThermalGridState, +} import edu.ie3.simona.model.thermal.{ThermalGrid, ThermalThreshold} import edu.ie3.simona.ontology.messages.flex.FlexibilityMessage.ProvideFlexOptions import edu.ie3.simona.ontology.messages.flex.MinMaxFlexibilityMessage.ProvideMinMaxFlexOptions @@ -160,8 +163,16 @@ final case class HpModel( state.thermalGridState, ) - - val (houseDemand, heatStorageDemand, noThermalStorageOrThermalStorageIsEmpty) = determineDemandBooleans(state, updatedState, demandHouse, demandThermalStorage) + val ( + houseDemand, + heatStorageDemand, + noThermalStorageOrThermalStorageIsEmpty, + ) = determineDemandBooleans( + state, + updatedState, + demandHouse, + demandThermalStorage, + ) val turnHpOn: Boolean = houseDemand || heatStorageDemand @@ -172,20 +183,21 @@ final case class HpModel( val canBeOutOfOperation = !(demandHouse.hasRequiredDemand && noThermalStorageOrThermalStorageIsEmpty) - ( - turnHpOn, - canOperate, - canBeOutOfOperation, - houseDemand, - heatStorageDemand) + (turnHpOn, canOperate, canBeOutOfOperation, houseDemand, heatStorageDemand) } - def determineDemandBooleans(hpState: HpState, updatedGridState:ThermalGridState, demandHouse:ThermalEnergyDemand, demandThermalStorage: ThermalEnergyDemand):(Boolean, Boolean, Boolean)={ + def determineDemandBooleans( + hpState: 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.exists( - _.storedEnergy =~ zeroKWH - ) + updatedGridState.storageState.isEmpty || updatedGridState.storageState + .exists( + _.storedEnergy =~ zeroKWH + ) val houseDemand = (demandHouse.hasRequiredDemand && noThermalStorageOrThermalStorageIsEmpty) || (hpState.isRunning && demandHouse.hasAdditionalDemand) @@ -318,7 +330,16 @@ final case class HpModel( lastState.thermalGridState, ) - val (houseDemand, heatStorageDemand, noThermalStorageOrThermalStorageIsEmpty) = determineDemandBooleans(lastState, updatedThermalGridState, thermalEnergyDemandHouse, thermalEnergyDemandStorage) + val ( + houseDemand, + heatStorageDemand, + noThermalStorageOrThermalStorageIsEmpty, + ) = determineDemandBooleans( + lastState, + updatedThermalGridState, + thermalEnergyDemandHouse, + thermalEnergyDemandStorage, + ) val updatedHpState: HpState = calcState( From 64a67ecd5379a9d9d525902168d1ddebd499241c Mon Sep 17 00:00:00 2001 From: danielfeismann Date: Sat, 24 Aug 2024 10:32:58 +0200 Subject: [PATCH 02/65] Refactoring of ThermalGrid.handleInfeed to fix thermal storage recharge correctly when empty --- CHANGELOG.md | 1 + .../simona/model/participant/HpModel.scala | 7 + .../simona/model/thermal/ThermalGrid.scala | 334 ++++++++++++++---- .../edu/ie3/simona/agent/em/EmAgentIT.scala | 16 +- .../model/thermal/ThermalGridTestData.scala | 4 + .../ThermalGridWithHouseAndStorageSpec.scala | 8 +- .../ThermalGridWithHouseOnlySpec.scala | 12 + .../ThermalGridWithStorageOnlySpec.scala | 12 + .../test/common/input/EmInputTestData.scala | 2 +- 9 files changed, 317 insertions(+), 79 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 74bc169d92..3f51e98bd9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -106,6 +106,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Fix activation of Hp when not under control of an EM [#922](https://github.com/ie3-institute/simona/issues/922) - Fixed ThermalStorageResults having multiple entries [#924](https://github.com/ie3-institute/simona/issues/924) - Fix determineState of ThermalHouse [#926](https://github.com/ie3-institute/simona/issues/926) +- Refactoring of `ThermalGrid.handleInfeed` to fix thermal storage recharge correctly when empty [#930](https://github.com/ie3-institute/simona/issues/930) ## [3.0.0] - 2023-08-07 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 569bd48c10..7e2da343fe 100644 --- a/src/main/scala/edu/ie3/simona/model/participant/HpModel.scala +++ b/src/main/scala/edu/ie3/simona/model/participant/HpModel.scala @@ -217,6 +217,10 @@ 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 houseDemand + * determines if the thermal house has heat demand + * @param storageDemand + * determines if the thermal storage has heat demand * @return * next [[HpState]] */ @@ -245,7 +249,10 @@ final case class HpModel( state.thermalGridState, state.ambientTemperature.getOrElse(relevantData.ambientTemperature), relevantData.ambientTemperature, + isRunning, newThermalPower, + houseDemand, + storageDemand, ) HpState( 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 28d299d67e..bd63a8030b 100644 --- a/src/main/scala/edu/ie3/simona/model/thermal/ThermalGrid.scala +++ b/src/main/scala/edu/ie3/simona/model/thermal/ThermalGrid.scala @@ -146,6 +146,7 @@ final case class ThermalGrid( } /** Update the current state of the grid + * * @param tick * Instance in time * @param state @@ -154,8 +155,14 @@ final case class ThermalGrid( * Ambient temperature until this tick * @param ambientTemperature * Actual ambient temperature + * @param isRunning + * determines whether the heat pump is running or not * @param qDot * Thermal energy balance + * @param houseDemand + * determines if the thermal house has heat demand + * @param storageDemand + * determines if the thermal storage has heat demand * @return * The updated state of the grid */ @@ -164,9 +171,21 @@ final case class ThermalGrid( state: ThermalGridState, lastAmbientTemperature: Temperature, ambientTemperature: Temperature, + isRunning: Boolean, qDot: Power, + houseDemand: Boolean, + storageDemand: Boolean, ): (ThermalGridState, Option[ThermalThreshold]) = if (qDot > zeroKW) - handleInfeed(tick, lastAmbientTemperature, ambientTemperature, state, qDot) + handleInfeed( + tick, + lastAmbientTemperature, + ambientTemperature, + state, + isRunning, + qDot, + houseDemand, + storageDemand, + ) else handleConsumption( tick, @@ -176,8 +195,9 @@ final case class ThermalGrid( qDot, ) - /** Handles the case, when a grid has infeed. First, heat up all the houses to - * their maximum temperature, then fill up the storages + /** Handles the case, when a grid has infeed. Depending which entity has some + * heat demand the house or the storage will be heated up / filled up. + * * @param tick * Current tick * @param lastAmbientTemperature @@ -186,8 +206,14 @@ final case class ThermalGrid( * Actual ambient temperature * @param state * Current state of the houses + * @param isRunning + * determines whether the heat pump is running or not * @param qDot * Infeed to the grid + * @param houseDemand + * determines if the thermal house has heat demand + * @param storageDemand + * determines if the thermal storage has heat demand * @return * Updated thermal grid state */ @@ -196,41 +222,221 @@ final case class ThermalGrid( lastAmbientTemperature: Temperature, ambientTemperature: Temperature, state: ThermalGridState, + isRunning: Boolean, qDot: Power, - ): (ThermalGridState, Option[ThermalThreshold]) = - house.zip(state.houseState) match { - case Some((thermalHouse, lastHouseState)) => - /* Set thermal power exchange with storage to zero */ - // TODO: We would need to issue a storage result model here... - val updatedStorageState = storage.zip(state.storageState) match { - case Some((thermalStorage, storageState)) => - Some( - thermalStorage - .updateState( - tick, - zeroKW, - storageState, - ) - ._1 - ) - case _ => state.storageState + houseDemand: Boolean, + heatStorageDemand: Boolean, + ): (ThermalGridState, Option[ThermalThreshold]) = { + // TODO: We would need to issue a storage result model here... + + /* Consider the action in the last state */ + val (qDotHouseLastState, qDotStorageLastState) = state match { + case ThermalGridState(Some(houseState), Some(storageState)) => + (houseState.qDot, storageState.qDot) + case ThermalGridState(Some(houseState), None) => (houseState.qDot, zeroKW) + case ThermalGridState(None, Some(storageState)) => + (zeroKW, storageState.qDot) + case _ => + throw new InconsistentStateException( + "There should be at least a house or a storage state." + ) + } + + if ( + (qDotHouseLastState > zeroKW && !(qDotStorageLastState < zeroKW)) | (qDotStorageLastState > zeroKW & heatStorageDemand) + ) { + val (updatedHouseState, thermalHouseThreshold, remainingQDotHouse) = + handleInfeedHouse( + tick, + lastAmbientTemperature, + ambientTemperature, + state, + qDotHouseLastState, + ) + val (updatedStorageState, thermalStorageThreshold) = + if ( + qDotStorageLastState >= zeroKW && remainingQDotHouse > qDotStorageLastState + ) { + handleInfeedStorage( + tick, + state, + remainingQDotHouse, + ) + } else { + handleInfeedStorage( + tick, + state, + qDotStorageLastState, + ) } - val (updatedHouseState, maybeHouseThreshold) = - thermalHouse.determineState( + val nextThreshold = determineMostRecentThreshold( + thermalHouseThreshold, + thermalStorageThreshold, + ) + ( + state.copy( + houseState = updatedHouseState, + storageState = updatedStorageState, + ), + nextThreshold, + ) + } + // Handle edge case where house get heated from storage and HP will be activated in between + else if ((qDotHouseLastState > zeroKW && qDotStorageLastState < zeroKW)) { + if (isRunning) { + handleCases( + tick, + lastAmbientTemperature, + ambientTemperature, + state, + qDot, + zeroKW, + ) + } else { + + handleCases( + tick, + lastAmbientTemperature, + ambientTemperature, + state, + qDotHouseLastState, + qDotStorageLastState, + ) + } + } else { + + (houseDemand, heatStorageDemand) match { + + case (true, _) => + // house first then heatStorage after heating House + handleCases( tick, - lastHouseState, lastAmbientTemperature, ambientTemperature, + state, qDot, + zeroKW, ) + case (false, true) => + handleCases( + tick, + lastAmbientTemperature, + ambientTemperature, + state, + zeroKW, + qDot, + ) + + case (false, false) => + handleCases( + tick, + lastAmbientTemperature, + ambientTemperature, + state, + zeroKW, + zeroKW, + ) + case _ => + throw new InconsistentStateException( + "There should be at least a house or a storage state." + ) + } + } + } + + /** Handles the different cases, of thermal flows from and into the thermal + * grid. + * + * @param tick + * Current tick + * @param lastAmbientTemperature + * Ambient temperature until this tick + * @param ambientTemperature + * actual ambient temperature + * @param state + * Current state of the thermal grid + * @param qDotHouse + * Infeed to the house + * @param qDotStorage + * Infeed to the heat storage + * @return + * Updated thermal grid state and the next threshold if there is one + */ + private def handleCases( + tick: Long, + lastAmbientTemperature: Temperature, + ambientTemperature: Temperature, + state: ThermalGridState, + qDotHouse: Power, + qDotHeatStorage: Power, + ): (ThermalGridState, Option[ThermalThreshold]) = { + // FIXME: Is there any case where we get back some remainingQDotHouse? + val (updatedHouseState, thermalHouseThreshold, remainingQDotHouse) = + handleInfeedHouse( + tick, + lastAmbientTemperature, + ambientTemperature, + state, + qDotHouse, + ) + + val (updatedStorageState, thermalStorageThreshold) = + handleInfeedStorage(tick, state, qDotHeatStorage) + + val nextThreshold = determineMostRecentThreshold( + thermalHouseThreshold, + thermalStorageThreshold, + ) + + ( + state.copy( + houseState = updatedHouseState, + storageState = updatedStorageState, + ), + nextThreshold, + ) + } + + /** Handles the case, when the house has heat demand and will be heated up + * here. + * + * @param tick + * Current tick + * @param lastAmbientTemperature + * Ambient temperature until this tick + * @param ambientTemperature + * actual ambient temperature + * @param state + * Current state of the houses + * @param qDot + * Infeed to the grid + * @return + * Updated thermal house state, a ThermalThreshold and the remaining qDot + */ + private def handleInfeedHouse( + tick: Long, + lastAmbientTemperature: Temperature, + ambientTemperature: Temperature, + state: ThermalGridState, + qDot: Power, + ): (Option[ThermalHouseState], Option[ThermalThreshold], Power) = { + (house, state.houseState) match { + case (Some(thermalHouse), Some(lastHouseState)) => + val (newState, threshold) = thermalHouse.determineState( + tick, + lastHouseState, + lastAmbientTemperature, + ambientTemperature, + qDot, + ) + /* Check if house can handle the thermal feed in */ if ( thermalHouse.isInnerTemperatureTooHigh( - updatedHouseState.innerTemperature + newState.innerTemperature ) ) { - /* The house is already heated up fully, set back the infeed and put it into storage, if available */ val (fullHouseState, maybeFullHouseThreshold) = thermalHouse.determineState( tick, @@ -239,54 +445,44 @@ final case class ThermalGrid( ambientTemperature, zeroKW, ) - storage.zip(updatedStorageState) match { - case Some((thermalStorage, storageState)) => - val (updatedStorageState, maybeStorageThreshold) = - thermalStorage.updateState(tick, qDot, storageState) - - /* Both house and storage are updated. Determine what reaches the next threshold */ - val nextThreshold = determineMostRecentThreshold( - maybeFullHouseThreshold, - maybeStorageThreshold, - ) - - ( - state.copy( - houseState = Some(fullHouseState), - storageState = Some(updatedStorageState), - ), - nextThreshold, - ) - case None => - /* There is no storage, house determines the next activation */ - ( - state.copy(houseState = Some(fullHouseState)), - maybeFullHouseThreshold, - ) - } + (Some(fullHouseState), maybeFullHouseThreshold, qDot) } else { - /* The house can handle the infeed */ - ( - state.copy(houseState = Some(updatedHouseState)), - maybeHouseThreshold, - ) + (Some(newState), threshold, zeroKW) } + case _ => (None, None, zeroKW) + } + } - case None => - storage.zip(state.storageState) match { - case Some((thermalStorage, storageState)) => - val (updatedStorageState, maybeStorageThreshold) = - thermalStorage.updateState(tick, qDot, storageState) - ( - state.copy(storageState = Some(updatedStorageState)), - maybeStorageThreshold, - ) - case None => - throw new InconsistentStateException( - "A thermal grid has to contain either at least a house or a storage." - ) - } + /** Handles the cases, when the storage has heat demand and will be filled up + * here (positive qDot) or will be return its stored energy into the thermal + * grid (negative qDot). + * @param tick + * Current tick + * @param ambientTemperature + * Ambient temperature + * @param state + * Current state of the houses + * @param qDot + * Infeed to the grid + * @return + * Updated thermal grid state + */ + private def handleInfeedStorage( + tick: Long, + state: ThermalGridState, + qDot: Power, + ): (Option[ThermalStorageState], Option[ThermalThreshold]) = { + (storage, state.storageState) match { + case (Some(thermalStorage), Some(lastStorageState)) => + val (newState, threshold) = thermalStorage.updateState( + tick, + qDot, + lastStorageState, + ) + (Some(newState), threshold) + case _ => (None, None) } + } private def determineMostRecentThreshold( maybeHouseThreshold: Option[ThermalThreshold], diff --git a/src/test/scala/edu/ie3/simona/agent/em/EmAgentIT.scala b/src/test/scala/edu/ie3/simona/agent/em/EmAgentIT.scala index 09f6acb019..be1cee377f 100644 --- a/src/test/scala/edu/ie3/simona/agent/em/EmAgentIT.scala +++ b/src/test/scala/edu/ie3/simona/agent/em/EmAgentIT.scala @@ -680,7 +680,7 @@ class EmAgentIT Celsius(0d), MetersPerSecond(0d), ), - Some(28800), + Some(56000), ) } @@ -692,26 +692,26 @@ class EmAgentIT emResult.getQ should equalWithTolerance(0.000088285537.asMegaVar) } - scheduler.expectMessage(Completion(emAgentActivation, Some(28665))) + scheduler.expectMessage(Completion(emAgentActivation, Some(44398))) - /* TICK 28666 + /* TICK 44398 LOAD: 0.000269 MW (unchanged) PV: -0.000032 MW (unchanged) Heat pump: Is turned on again and cannot be turned off -> flex signal is no control -> 0.00485 MW */ - emAgentActivation ! Activation(28665) + emAgentActivation ! Activation(44398) resultListener.expectMessageType[ParticipantResultEvent] match { case ParticipantResultEvent(emResult: EmResult) => emResult.getInputModel shouldBe emInput.getUuid - emResult.getTime shouldBe 28665.toDateTime - emResult.getP should equalWithTolerance(0.0050867679996.asMegaWatt) - emResult.getQ should equalWithTolerance(0.001073120040.asMegaVar) + emResult.getTime shouldBe 44398.toDateTime + emResult.getP should equalWithTolerance(0.005086768.asMegaWatt) + emResult.getQ should equalWithTolerance(0.00107312004.asMegaVar) } - scheduler.expectMessage(Completion(emAgentActivation, Some(28800))) + scheduler.expectMessage(Completion(emAgentActivation, Some(56000))) } } 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..0a016d4566 100644 --- a/src/test/scala/edu/ie3/simona/model/thermal/ThermalGridTestData.scala +++ b/src/test/scala/edu/ie3/simona/model/thermal/ThermalGridTestData.scala @@ -25,4 +25,8 @@ trait ThermalGridTestData { protected val testGridQDotInfeed: Power = Kilowatts(15d) protected val testGridQDotConsumption: Power = Kilowatts(-42d) protected val testGridQDotConsumptionHigh: Power = Kilowatts(-200d) + protected val noThermalDemand: Boolean = false + protected val thermalDemand: Boolean = true + 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 1ea45a656d..b2b732ae0e 100644 --- a/src/test/scala/edu/ie3/simona/model/thermal/ThermalGridWithHouseAndStorageSpec.scala +++ b/src/test/scala/edu/ie3/simona/model/thermal/ThermalGridWithHouseAndStorageSpec.scala @@ -496,7 +496,10 @@ class ThermalGridWithHouseAndStorageSpec testGridAmbientTemperature, testGridAmbientTemperature, initialGridState, + isNotRunning, externalQDot, + thermalDemand, + noThermalDemand, ) updatedGridState match { @@ -510,7 +513,7 @@ class ThermalGridWithHouseAndStorageSpec innerTemperature should approximate(Celsius(18.9999d)) qDotHouse should approximate(externalQDot) - storageTick shouldBe -1L + storageTick shouldBe 0L storedEnergy should approximate( initialGridState.storageState .map(_.storedEnergy) @@ -542,7 +545,10 @@ class ThermalGridWithHouseAndStorageSpec testGridAmbientTemperature, testGridAmbientTemperature, gridState, + isNotRunning, externalQDot, + noThermalDemand, + thermalDemand, ) updatedGridState match { 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 eb447d0fc4..4ecbfc819b 100644 --- a/src/test/scala/edu/ie3/simona/model/thermal/ThermalGridWithHouseOnlySpec.scala +++ b/src/test/scala/edu/ie3/simona/model/thermal/ThermalGridWithHouseOnlySpec.scala @@ -179,7 +179,10 @@ class ThermalGridWithHouseOnlySpec extends UnitSpec with ThermalHouseTestData { testGridAmbientTemperature, testGridAmbientTemperature, gridState, + isNotRunning, testGridQDotInfeed, + thermalDemand, + noThermalDemand, ) updatedGridState match { @@ -205,7 +208,10 @@ class ThermalGridWithHouseOnlySpec extends UnitSpec with ThermalHouseTestData { ThermalGrid.startingState(thermalGrid), testGridAmbientTemperature, testGridAmbientTemperature, + isRunning, testGridQDotInfeed, + thermalDemand, + noThermalDemand, ) match { case ( ThermalGridState( @@ -228,7 +234,10 @@ class ThermalGridWithHouseOnlySpec extends UnitSpec with ThermalHouseTestData { ThermalGrid.startingState(thermalGrid), testGridAmbientTemperature, testGridAmbientTemperature, + isNotRunning, testGridQDotConsumption, + thermalDemand, + noThermalDemand, ) match { case ( ThermalGridState( @@ -251,7 +260,10 @@ class ThermalGridWithHouseOnlySpec extends UnitSpec with ThermalHouseTestData { ThermalGrid.startingState(thermalGrid), testGridAmbientTemperature, testGridAmbientTemperature, + isNotRunning, Megawatts(0d), + thermalDemand, + noThermalDemand, ) match { case ( ThermalGridState( 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 93a45e3180..c1ded4c6dc 100644 --- a/src/test/scala/edu/ie3/simona/model/thermal/ThermalGridWithStorageOnlySpec.scala +++ b/src/test/scala/edu/ie3/simona/model/thermal/ThermalGridWithStorageOnlySpec.scala @@ -182,7 +182,10 @@ class ThermalGridWithStorageOnlySpec testGridAmbientTemperature, testGridAmbientTemperature, gridState, + isNotRunning, testGridQDotInfeed, + noThermalDemand, + thermalDemand, ) updatedGridState match { @@ -206,7 +209,10 @@ class ThermalGridWithStorageOnlySpec ThermalGrid.startingState(thermalGrid), testGridAmbientTemperature, testGridAmbientTemperature, + isRunning, testGridQDotInfeed, + noThermalDemand, + thermalDemand, ) nextThreshold shouldBe Some(StorageFull(276000L)) @@ -239,7 +245,10 @@ class ThermalGridWithStorageOnlySpec ), testGridAmbientTemperature, testGridAmbientTemperature, + isRunning, testGridQDotConsumptionHigh, + thermalDemand, + noThermalDemand, ) match { case ( ThermalGridState( @@ -262,7 +271,10 @@ class ThermalGridWithStorageOnlySpec ThermalGrid.startingState(thermalGrid), testGridAmbientTemperature, testGridAmbientTemperature, + isRunning, Kilowatts(0d), + noThermalDemand, + noThermalDemand, ) updatedState match { case ( diff --git a/src/test/scala/edu/ie3/simona/test/common/input/EmInputTestData.scala b/src/test/scala/edu/ie3/simona/test/common/input/EmInputTestData.scala index c5f3cf8efe..9c5abd30de 100644 --- a/src/test/scala/edu/ie3/simona/test/common/input/EmInputTestData.scala +++ b/src/test/scala/edu/ie3/simona/test/common/input/EmInputTestData.scala @@ -140,7 +140,7 @@ trait EmInputTestData UUID.fromString("91940626-bdd0-41cf-96dd-47c94c86b20e"), "thermal house", thermalBusInput, - Quantities.getQuantity(0.325, StandardUnits.THERMAL_TRANSMISSION), + Quantities.getQuantity(0.15, StandardUnits.THERMAL_TRANSMISSION), Quantities.getQuantity(75, StandardUnits.HEAT_CAPACITY), Quantities.getQuantity(20.3, StandardUnits.TEMPERATURE), Quantities.getQuantity(22.0, StandardUnits.TEMPERATURE), From 1d17e184b2232663d1c9d6c5de4a81af8297ea49 Mon Sep 17 00:00:00 2001 From: danielfeismann Date: Sat, 24 Aug 2024 10:58:05 +0200 Subject: [PATCH 03/65] small changes and fmt --- .../agent/participant/ParticipantAgentFundamentals.scala | 3 ++- .../simona/agent/participant/hp/HpAgentFundamentals.scala | 2 +- src/test/scala/edu/ie3/simona/agent/em/EmAgentIT.scala | 4 ++-- .../ie3/simona/model/thermal/ThermalGridTestData.scala | 2 ++ .../thermal/ThermalGridWithHouseAndStorageSpec.scala | 4 ++-- .../model/thermal/ThermalGridWithHouseOnlySpec.scala | 8 ++++---- .../model/thermal/ThermalGridWithStorageOnlySpec.scala | 8 ++++---- 7 files changed, 17 insertions(+), 14 deletions(-) diff --git a/src/main/scala/edu/ie3/simona/agent/participant/ParticipantAgentFundamentals.scala b/src/main/scala/edu/ie3/simona/agent/participant/ParticipantAgentFundamentals.scala index ad6941ac8a..fe78e9136f 100644 --- a/src/main/scala/edu/ie3/simona/agent/participant/ParticipantAgentFundamentals.scala +++ b/src/main/scala/edu/ie3/simona/agent/participant/ParticipantAgentFundamentals.scala @@ -54,6 +54,7 @@ import edu.ie3.simona.event.ResultEvent import edu.ie3.simona.event.ResultEvent.{ FlexOptionsResultEvent, ParticipantResultEvent, + ThermalResultEvent, } import edu.ie3.simona.event.notifier.NotifierConfig import edu.ie3.simona.exceptions.CriticalFailureException @@ -1907,7 +1908,7 @@ protected trait ParticipantAgentFundamentals[ result: R ): Option[ResultEvent] = result match { case thermalUnitResult: ThermalUnitResult => - Some(ResultEvent.ThermalResultEvent(thermalUnitResult)) + Some(ThermalResultEvent(thermalUnitResult)) case unsupported => log.debug( 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 1d6506c714..7b54fcc40f 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 @@ -312,7 +312,7 @@ trait HpAgentFundamentals nodalVoltage: squants.Dimensionless, model: HpModel, ): HpState = { - val (canOperate, canBeOutOfOperation, state) = + val (_, _, state) = model.determineState(modelState, calcRelevantData) state } diff --git a/src/test/scala/edu/ie3/simona/agent/em/EmAgentIT.scala b/src/test/scala/edu/ie3/simona/agent/em/EmAgentIT.scala index 0a6a6c7b44..be1cee377f 100644 --- a/src/test/scala/edu/ie3/simona/agent/em/EmAgentIT.scala +++ b/src/test/scala/edu/ie3/simona/agent/em/EmAgentIT.scala @@ -561,7 +561,7 @@ class EmAgentIT LOAD: 0.000269 MW PV: -0.005685 MW Heat pump: off, can be turned on or stay off - -> set point ~3.5 kW (bigger than 50 % rated thermal power): turned on + -> set point ~3.5 kW (bigger than 50 % rated apparent power): turned on -> remaining -0.000566 MW */ @@ -597,7 +597,7 @@ class EmAgentIT LOAD: 0.000269 MW (unchanged) PV: -0.003797 MW Heat pump: running (turned on from last request), can also be turned off - -> set point ~3.5 kW (bigger than 50 % rated thermal power): stays turned on with unchanged state + -> set point ~3.5 kW (bigger than 50 % rated apparent power): stays turned on with unchanged state -> remaining 0 MW */ 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 d9858707e9..0a016d4566 100644 --- a/src/test/scala/edu/ie3/simona/model/thermal/ThermalGridTestData.scala +++ b/src/test/scala/edu/ie3/simona/model/thermal/ThermalGridTestData.scala @@ -27,4 +27,6 @@ trait ThermalGridTestData { protected val testGridQDotConsumptionHigh: Power = Kilowatts(-200d) protected val noThermalDemand: Boolean = false protected val thermalDemand: Boolean = true + 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 687f5c4560..b2b732ae0e 100644 --- a/src/test/scala/edu/ie3/simona/model/thermal/ThermalGridWithHouseAndStorageSpec.scala +++ b/src/test/scala/edu/ie3/simona/model/thermal/ThermalGridWithHouseAndStorageSpec.scala @@ -496,7 +496,7 @@ class ThermalGridWithHouseAndStorageSpec testGridAmbientTemperature, testGridAmbientTemperature, initialGridState, - false, + isNotRunning, externalQDot, thermalDemand, noThermalDemand, @@ -545,7 +545,7 @@ class ThermalGridWithHouseAndStorageSpec testGridAmbientTemperature, testGridAmbientTemperature, gridState, - false, + isNotRunning, externalQDot, noThermalDemand, thermalDemand, 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 21d5db73f1..4ecbfc819b 100644 --- a/src/test/scala/edu/ie3/simona/model/thermal/ThermalGridWithHouseOnlySpec.scala +++ b/src/test/scala/edu/ie3/simona/model/thermal/ThermalGridWithHouseOnlySpec.scala @@ -179,7 +179,7 @@ class ThermalGridWithHouseOnlySpec extends UnitSpec with ThermalHouseTestData { testGridAmbientTemperature, testGridAmbientTemperature, gridState, - false, + isNotRunning, testGridQDotInfeed, thermalDemand, noThermalDemand, @@ -208,7 +208,7 @@ class ThermalGridWithHouseOnlySpec extends UnitSpec with ThermalHouseTestData { ThermalGrid.startingState(thermalGrid), testGridAmbientTemperature, testGridAmbientTemperature, - true, + isRunning, testGridQDotInfeed, thermalDemand, noThermalDemand, @@ -234,7 +234,7 @@ class ThermalGridWithHouseOnlySpec extends UnitSpec with ThermalHouseTestData { ThermalGrid.startingState(thermalGrid), testGridAmbientTemperature, testGridAmbientTemperature, - false, + isNotRunning, testGridQDotConsumption, thermalDemand, noThermalDemand, @@ -260,7 +260,7 @@ class ThermalGridWithHouseOnlySpec extends UnitSpec with ThermalHouseTestData { ThermalGrid.startingState(thermalGrid), testGridAmbientTemperature, testGridAmbientTemperature, - false, + isNotRunning, Megawatts(0d), thermalDemand, noThermalDemand, 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 8a34ad8d1c..c1ded4c6dc 100644 --- a/src/test/scala/edu/ie3/simona/model/thermal/ThermalGridWithStorageOnlySpec.scala +++ b/src/test/scala/edu/ie3/simona/model/thermal/ThermalGridWithStorageOnlySpec.scala @@ -182,7 +182,7 @@ class ThermalGridWithStorageOnlySpec testGridAmbientTemperature, testGridAmbientTemperature, gridState, - false, + isNotRunning, testGridQDotInfeed, noThermalDemand, thermalDemand, @@ -209,7 +209,7 @@ class ThermalGridWithStorageOnlySpec ThermalGrid.startingState(thermalGrid), testGridAmbientTemperature, testGridAmbientTemperature, - true, + isRunning, testGridQDotInfeed, noThermalDemand, thermalDemand, @@ -245,7 +245,7 @@ class ThermalGridWithStorageOnlySpec ), testGridAmbientTemperature, testGridAmbientTemperature, - true, + isRunning, testGridQDotConsumptionHigh, thermalDemand, noThermalDemand, @@ -271,7 +271,7 @@ class ThermalGridWithStorageOnlySpec ThermalGrid.startingState(thermalGrid), testGridAmbientTemperature, testGridAmbientTemperature, - true, + isRunning, Kilowatts(0d), noThermalDemand, noThermalDemand, From d8157630914e0a8aaf0e863039bfe04a8a643d78 Mon Sep 17 00:00:00 2001 From: danielfeismann Date: Sat, 24 Aug 2024 11:02:04 +0200 Subject: [PATCH 04/65] fmt --- .../scala/edu/ie3/simona/model/participant/HpModel.scala | 4 ++-- .../scala/edu/ie3/simona/model/thermal/ThermalGrid.scala | 9 ++++----- .../edu/ie3/simona/model/thermal/ThermalHouse.scala | 8 ++++---- 3 files changed, 10 insertions(+), 11 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 1473ee39f1..7e2da343fe 100644 --- a/src/main/scala/edu/ie3/simona/model/participant/HpModel.scala +++ b/src/main/scala/edu/ie3/simona/model/participant/HpModel.scala @@ -21,8 +21,8 @@ import edu.ie3.simona.ontology.messages.flex.MinMaxFlexibilityMessage.ProvideMin import edu.ie3.util.quantities.PowerSystemUnits import edu.ie3.util.scala.OperationInterval import edu.ie3.util.scala.quantities.DefaultQuantities._ -import squants.energy.{Energy, KilowattHours, Kilowatts} -import squants.{Power, Temperature} +import squants.energy.{KilowattHours, Kilowatts} +import squants.{Energy, Power, Temperature} import java.time.ZonedDateTime import java.util.UUID 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 58b2e8f189..9b870aca17 100644 --- a/src/main/scala/edu/ie3/simona/model/thermal/ThermalGrid.scala +++ b/src/main/scala/edu/ie3/simona/model/thermal/ThermalGrid.scala @@ -13,7 +13,6 @@ import edu.ie3.datamodel.models.result.thermal.{ CylindricalStorageResult, ThermalHouseResult, } -import edu.ie3.simona.exceptions.InvalidParameterException import edu.ie3.simona.exceptions.agent.InconsistentStateException import edu.ie3.simona.model.thermal.ThermalGrid.{ ThermalEnergyDemand, @@ -155,7 +154,7 @@ final case class ThermalGrid( * @param lastAmbientTemperature * Ambient temperature until this tick * @param ambientTemperature - * actual ambient temperature + * Actual ambient temperature * @param isRunning * determines whether the heat pump is running or not * @param qDot @@ -204,7 +203,7 @@ final case class ThermalGrid( * @param lastAmbientTemperature * Ambient temperature until this tick * @param ambientTemperature - * actual ambient temperature + * Actual ambient temperature * @param state * Current state of the houses * @param isRunning @@ -507,7 +506,7 @@ final case class ThermalGrid( * @param lastAmbientTemperature * Ambient temperature until this tick * @param ambientTemperature - * actual ambient temperature + * Actual ambient temperature * @param state * Current state of the houses * @param qDot @@ -583,7 +582,7 @@ final case class ThermalGrid( * @param lastAmbientTemperature * Ambient temperature until this tick * @param ambientTemperature - * actual ambient temperature + * Actual ambient temperature * @param qDot * Thermal influx * @return diff --git a/src/main/scala/edu/ie3/simona/model/thermal/ThermalHouse.scala b/src/main/scala/edu/ie3/simona/model/thermal/ThermalHouse.scala index 6bf7a65629..0b8544fd67 100644 --- a/src/main/scala/edu/ie3/simona/model/thermal/ThermalHouse.scala +++ b/src/main/scala/edu/ie3/simona/model/thermal/ThermalHouse.scala @@ -298,15 +298,15 @@ final case class ThermalHouse( /** Update the current state of the house * * @param tick - * current instance in time + * Current instance in time * @param state - * currently applicable state + * Currently applicable state * @param lastAmbientTemperature * Ambient temperature until this tick * @param ambientTemperature - * actual ambient temperature + * Actual ambient temperature * @param qDot - * new thermal influx + * New thermal influx * @return * Updated state and the tick in which the next threshold is reached */ From 6c5477a2ba738f2dbea24948427abb777bb5a6ec Mon Sep 17 00:00:00 2001 From: danielfeismann Date: Thu, 29 Aug 2024 07:12:27 +0200 Subject: [PATCH 05/65] fixing merge conflicts --- .../simona/model/thermal/ThermalGrid.scala | 29 ++++++++++--------- .../ThermalGridWithHouseOnlySpec.scala | 5 ---- .../ThermalGridWithStorageOnlySpec.scala | 2 -- 3 files changed, 15 insertions(+), 21 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 727f37c73f..cbb708fb1e 100644 --- a/src/main/scala/edu/ie3/simona/model/thermal/ThermalGrid.scala +++ b/src/main/scala/edu/ie3/simona/model/thermal/ThermalGrid.scala @@ -13,6 +13,7 @@ import edu.ie3.datamodel.models.result.thermal.{ CylindricalStorageResult, ThermalHouseResult, } +import edu.ie3.simona.exceptions.InvalidParameterException import edu.ie3.simona.exceptions.agent.InconsistentStateException import edu.ie3.simona.model.thermal.ThermalGrid.{ ThermalEnergyDemand, @@ -48,9 +49,9 @@ final case class ThermalGrid( * @param tick * Questioned instance in time * @param lastAmbientTemperature - * Ambient temperature until this tick + * Ambient temperature valid up until (not including) the current tick * @param ambientTemperature - * Ambient temperature in the instance in question + * Current ambient temperature in the instance in question * @param state * Currently applicable state of the thermal grid * @return @@ -155,14 +156,14 @@ final case class ThermalGrid( * Ambient temperature valid up until (not including) the current tick * @param ambientTemperature * Current ambient temperature - * @param isRunning - * determines whether the heat pump is running or not - * @param qDot + * @param isRunning + * Determines whether the heat pump is running or not + * @param qDot * Thermal energy balance * @param houseDemand - * determines if the thermal house has heat demand + * Determines if the thermal house has heat demand * @param storageDemand - * determines if the thermal storage has heat demand + * Determines if the thermal storage has heat demand * @return * The updated state of the grid */ @@ -207,13 +208,13 @@ final case class ThermalGrid( * @param state * Current state of the houses * @param isRunning - * determines whether the heat pump is running or not + * Determines whether the heat pump is running or not * @param qDot * Infeed to the grid * @param houseDemand - * determines if the thermal house has heat demand + * Determines if the thermal house has heat demand * @param storageDemand - * determines if the thermal storage has heat demand + * Determines if the thermal storage has heat demand * @return * Updated thermal grid state */ @@ -352,9 +353,9 @@ final case class ThermalGrid( * @param tick * Current tick * @param lastAmbientTemperature - * Ambient temperature until this tick + * Ambient temperature valid up until (not including) the current tick * @param ambientTemperature - * actual ambient temperature + * Current ambient temperature * @param state * Current state of the thermal grid * @param qDotHouse @@ -405,9 +406,9 @@ final case class ThermalGrid( * @param tick * Current tick * @param lastAmbientTemperature - * Ambient temperature until this tick + * Ambient temperature valid up until (not including) the current tick * @param ambientTemperature - * actual ambient temperature + * Current ambient temperature * @param state * Current state of the houses * @param qDot 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 fed891d395..4ecbfc819b 100644 --- a/src/test/scala/edu/ie3/simona/model/thermal/ThermalGridWithHouseOnlySpec.scala +++ b/src/test/scala/edu/ie3/simona/model/thermal/ThermalGridWithHouseOnlySpec.scala @@ -115,7 +115,6 @@ class ThermalGridWithHouseOnlySpec extends UnitSpec with ThermalHouseTestData { tick, testGridAmbientTemperature, testGridAmbientTemperature, - testGridAmbientTemperature, gridState, externalQDot, ) @@ -144,7 +143,6 @@ class ThermalGridWithHouseOnlySpec extends UnitSpec with ThermalHouseTestData { tick, testGridAmbientTemperature, testGridAmbientTemperature, - testGridAmbientTemperature, gridState, testGridQDotConsumption, ) @@ -180,7 +178,6 @@ class ThermalGridWithHouseOnlySpec extends UnitSpec with ThermalHouseTestData { tick, testGridAmbientTemperature, testGridAmbientTemperature, - testGridAmbientTemperature, gridState, isNotRunning, testGridQDotInfeed, @@ -211,7 +208,6 @@ class ThermalGridWithHouseOnlySpec extends UnitSpec with ThermalHouseTestData { ThermalGrid.startingState(thermalGrid), testGridAmbientTemperature, testGridAmbientTemperature, - testGridAmbientTemperature, isRunning, testGridQDotInfeed, thermalDemand, @@ -264,7 +260,6 @@ class ThermalGridWithHouseOnlySpec extends UnitSpec with ThermalHouseTestData { ThermalGrid.startingState(thermalGrid), testGridAmbientTemperature, testGridAmbientTemperature, - testGridAmbientTemperature, isNotRunning, Megawatts(0d), thermalDemand, 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 3210700166..c1ded4c6dc 100644 --- a/src/test/scala/edu/ie3/simona/model/thermal/ThermalGridWithStorageOnlySpec.scala +++ b/src/test/scala/edu/ie3/simona/model/thermal/ThermalGridWithStorageOnlySpec.scala @@ -245,7 +245,6 @@ class ThermalGridWithStorageOnlySpec ), testGridAmbientTemperature, testGridAmbientTemperature, - testGridAmbientTemperature, isRunning, testGridQDotConsumptionHigh, thermalDemand, @@ -272,7 +271,6 @@ class ThermalGridWithStorageOnlySpec ThermalGrid.startingState(thermalGrid), testGridAmbientTemperature, testGridAmbientTemperature, - testGridAmbientTemperature, isRunning, Kilowatts(0d), noThermalDemand, From bbe4c0ae0a513df319083854409445aa4d2e5dee Mon Sep 17 00:00:00 2001 From: danielfeismann Date: Thu, 29 Aug 2024 07:16:22 +0200 Subject: [PATCH 06/65] remove changelog entry that is no longer valid --- CHANGELOG.md | 1 - 1 file changed, 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1af422e84f..428423c19d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -107,7 +107,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Fix determineState of ThermalHouse [#926](https://github.com/ie3-institute/simona/issues/926) - Fixed Hp results leading to overheating house and other effects [#827](https://github.com/ie3-institute/simona/issues/827) - Fixed thermal storage getting recharged when empty [#827](https://github.com/ie3-institute/simona/issues/827) -- Provide actual ambient temperature of tick to HpModel when calculate state [#882](https://github.com/ie3-institute/simona/issues/882) ## [3.0.0] - 2023-08-07 From 3df14e4ab96d048ba06823285654ea3e2206ccf0 Mon Sep 17 00:00:00 2001 From: danielfeismann Date: Thu, 19 Sep 2024 16:28:40 +0200 Subject: [PATCH 07/65] rollback change to fallback to latest weatherData --- .../participant/hp/HpAgentFundamentals.scala | 43 ++++++------------- 1 file changed, 12 insertions(+), 31 deletions(-) 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 4352b07d57..121004738f 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 @@ -407,44 +407,25 @@ trait HpAgentFundamentals tick: Long, ): HpRelevantData = { /* extract weather data from secondary data, which should have been requested and received before */ - val weatherData = { - val currentWeatherData = baseStateData.receivedSecondaryDataStore + val weatherData = + baseStateData.receivedSecondaryDataStore .last(tick) .flatMap { case (receivedTick, receivedValues) => - if (receivedTick == tick) { - receivedValues.collectFirst { case (_, data: WeatherData) => - data - } - } else None - } - - // If current weather data is not found, fallback to the latest entry where the map is not empty - currentWeatherData - .orElse { - val latestEntry = - baseStateData.receivedSecondaryDataStore.store.toSeq.findLast { - case (_, receivedValues) => receivedValues.nonEmpty - } - - latestEntry.flatMap { case (receivedTick, receivedValues) => - if (tick - receivedTick > 3600) { - log.warning( - s"The model ${baseStateData.model.getUuid} is using weather data from tick $receivedTick, " + - s"but there is a discrepancy of ${tick - receivedTick} seconds compared to the current tick $tick." - ) - } - - receivedValues.collectFirst { case (_, data: WeatherData) => - data - } + if (receivedTick != tick) + log.debug( + s"The model ${baseStateData.model.getUuid} needs to do calculations with values received " + + s"in tick $receivedTick, as no weather data has been received in tick $tick." + ) + receivedValues.collectFirst { + // filter secondary data for weather data + case (_, data: WeatherData) => data } } - .getOrElse { + .getOrElse( throw new InconsistentStateException( s"The model ${baseStateData.model} was not provided with needed weather data." ) - } - } + ) HpRelevantData( tick, From 55001194f0f53de3f6c60602d69dc4525994f36a Mon Sep 17 00:00:00 2001 From: danielfeismann Date: Tue, 29 Oct 2024 17:35:57 +0100 Subject: [PATCH 08/65] rename lastTimeTick into tick and use updatedState for thermalResults --- .../simona/agent/participant/hp/HpAgentFundamentals.scala | 6 +++--- .../scala/edu/ie3/simona/model/participant/HpModel.scala | 6 +++--- .../agent/participant/HpAgentModelCalculationSpec.scala | 8 ++++---- 3 files changed, 10 insertions(+), 10 deletions(-) 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 c37b4d1a9a..e38454b020 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 @@ -188,7 +188,7 @@ trait HpAgentFundamentals ) val accompanyingResults = baseStateData.model.thermalGrid.results( - lastState.lastTimeTick, + updatedState.tick, updatedState.thermalGridState, )(baseStateData.startDate) val result = AccompaniedSimulationResult(power, accompanyingResults) @@ -253,8 +253,8 @@ trait HpAgentFundamentals relevantData, ) val accompanyingResults = baseStateData.model.thermalGrid.results( - lastModelState.lastTimeTick, - lastModelState.thermalGridState, + updatedState.tick, + updatedState.thermalGridState, )(baseStateData.startDate) val result = AccompaniedSimulationResult(power, accompanyingResults) 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 7e2da343fe..8b817f35b4 100644 --- a/src/main/scala/edu/ie3/simona/model/participant/HpModel.scala +++ b/src/main/scala/edu/ie3/simona/model/participant/HpModel.scala @@ -414,8 +414,8 @@ object HpModel { * * @param isRunning * indicates if CHP is turned on - * @param lastTimeTick - * contains last time tick + * @param tick + * the time tick of the HpState * @param ambientTemperature * Optional ambient temperature, if available * @param activePower @@ -430,7 +430,7 @@ object HpModel { */ final case class HpState( isRunning: Boolean, - lastTimeTick: Long, + tick: Long, ambientTemperature: Option[Temperature], activePower: Power, qDot: Power, diff --git a/src/test/scala/edu/ie3/simona/agent/participant/HpAgentModelCalculationSpec.scala b/src/test/scala/edu/ie3/simona/agent/participant/HpAgentModelCalculationSpec.scala index 3ff4229ab7..03007b225a 100644 --- a/src/test/scala/edu/ie3/simona/agent/participant/HpAgentModelCalculationSpec.scala +++ b/src/test/scala/edu/ie3/simona/agent/participant/HpAgentModelCalculationSpec.scala @@ -506,7 +506,7 @@ class HpAgentModelCalculationSpec _, HpState( isRunning, - lastTimeTick, + tick, _, activePower, qDot, @@ -516,7 +516,7 @@ class HpAgentModelCalculationSpec ) ) => isRunning shouldBe false - lastTimeTick shouldBe 0L + tick shouldBe 0L activePower should approximate(Kilowatts(0.0)) qDot should approximate(Kilowatts(0.0)) @@ -634,7 +634,7 @@ class HpAgentModelCalculationSpec _, HpState( isRunning, - lastTimeTick, + tick, _, activePower, qDot, @@ -644,7 +644,7 @@ class HpAgentModelCalculationSpec ) ) => isRunning shouldBe false - lastTimeTick shouldBe 0L + tick shouldBe 0L activePower should approximate(Kilowatts(0d)) qDot should approximate(Kilowatts(0d)) From 393d19f33bbc1f13a5e2d84b43dc0e828e64de79 Mon Sep 17 00:00:00 2001 From: danielfeismann Date: Tue, 29 Oct 2024 17:36:34 +0100 Subject: [PATCH 09/65] make store of ValueStore non-private --- src/main/scala/edu/ie3/simona/agent/ValueStore.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/scala/edu/ie3/simona/agent/ValueStore.scala b/src/main/scala/edu/ie3/simona/agent/ValueStore.scala index fbae2ff77c..a21f7d0f58 100644 --- a/src/main/scala/edu/ie3/simona/agent/ValueStore.scala +++ b/src/main/scala/edu/ie3/simona/agent/ValueStore.scala @@ -22,7 +22,7 @@ import scala.collection.SortedMap */ final case class ValueStore[+D]( maxTickSpan: Long, - store: SortedMap[Long, D] = SortedMap.empty[Long, D], + private val store: SortedMap[Long, D] = SortedMap.empty[Long, D], ) { /** Determine the lastly known data tick, if available. Includes the given From 099838a11b9b6ac3e497b050caaa4a25883dcaf2 Mon Sep 17 00:00:00 2001 From: danielfeismann Date: Tue, 29 Oct 2024 17:37:18 +0100 Subject: [PATCH 10/65] adapt ThermalGridIT to results processed in the currentTick --- .../ie3/simona/agent/grid/ThermalGridIT.scala | 142 ++++++++---------- 1 file changed, 66 insertions(+), 76 deletions(-) diff --git a/src/test/scala/edu/ie3/simona/agent/grid/ThermalGridIT.scala b/src/test/scala/edu/ie3/simona/agent/grid/ThermalGridIT.scala index 70be23c118..c4e8d736f4 100644 --- a/src/test/scala/edu/ie3/simona/agent/grid/ThermalGridIT.scala +++ b/src/test/scala/edu/ie3/simona/agent/grid/ThermalGridIT.scala @@ -196,9 +196,11 @@ class ThermalGridIT indoorTemperature, ) => inputModel shouldBe typicalThermalHouse.getUuid - time shouldBe (-1).toDateTime + time shouldBe 0.toDateTime qDot should equalWithTolerance(0.0.asMegaWatt) - indoorTemperature should equalWithTolerance(20.asDegreeCelsius) + indoorTemperature should equalWithTolerance( + 19.9999074074074.asDegreeCelsius + ) case CylindricalThermalStorageResult( time, inputModel, @@ -206,8 +208,8 @@ class ThermalGridIT energy, ) => inputModel shouldBe typicalThermalStorage.getUuid - time shouldBe (-1).toDateTime - qDot should equalWithTolerance(0.asMegaWatt) + time shouldBe 0.toDateTime + qDot should equalWithTolerance(0.011.asMegaWatt) energy should equalWithTolerance(0.asMegaWattHour) case _ => fail( @@ -229,7 +231,6 @@ class ThermalGridIT heatPumpAgent ! Activation(3417) - // Results of tick 3417 for hp resultListener.expectMessageType[ParticipantResultEvent] match { case ParticipantResultEvent(hpResult) => hpResult.getInputModel shouldBe typicalHpInputModel.getUuid @@ -240,7 +241,6 @@ class ThermalGridIT ) } - // Results of 0 for house and storage Range(0, 2) .map { _ => resultListener.expectMessageType[ResultEvent] @@ -254,11 +254,12 @@ class ThermalGridIT indoorTemperature, ) => inputModel shouldBe typicalThermalHouse.getUuid - time shouldBe 0.toDateTime - qDot should equalWithTolerance(0.asMegaWatt) + time shouldBe 3417.toDateTime + qDot should equalWithTolerance(0.011.asMegaWatt) indoorTemperature should equalWithTolerance( - 19.9999074074074.asDegreeCelsius + 19.6835196903292.asDegreeCelsius ) + case CylindricalThermalStorageResult( time, inputModel, @@ -266,9 +267,9 @@ class ThermalGridIT energy, ) => inputModel shouldBe typicalThermalStorage.getUuid - time shouldBe 0.toDateTime - qDot should equalWithTolerance(0.011.asMegaWatt) - energy should equalWithTolerance(0.asMegaWattHour) + time shouldBe 3417.toDateTime + qDot should equalWithTolerance(0.asMegaWatt) + energy should equalWithTolerance(0.01044.asMegaWattHour) case _ => fail( "Expected a ThermalHouseResult and a ThermalStorageResult but got something else" @@ -304,7 +305,6 @@ class ThermalGridIT ) } - // Results of 7200 for hp resultListener.expectMessageType[ParticipantResultEvent] match { case ParticipantResultEvent(hpResult) => hpResult.getInputModel shouldBe typicalHpInputModel.getUuid @@ -315,7 +315,6 @@ class ThermalGridIT ) } - // Results of 3417 for house and storage Range(0, 2) .map { _ => resultListener.expectMessageType[ResultEvent] @@ -329,12 +328,11 @@ class ThermalGridIT indoorTemperature, ) => inputModel shouldBe typicalThermalHouse.getUuid - time shouldBe 3417.toDateTime + time shouldBe 7200.toDateTime qDot should equalWithTolerance(0.011.asMegaWatt) indoorTemperature should equalWithTolerance( - 19.6835196903292.asDegreeCelsius + 20.8788983755569.asDegreeCelsius ) - case CylindricalThermalStorageResult( time, inputModel, @@ -342,7 +340,7 @@ class ThermalGridIT energy, ) => inputModel shouldBe typicalThermalStorage.getUuid - time shouldBe 3417.toDateTime + time shouldBe 7200.toDateTime qDot should equalWithTolerance(0.asMegaWatt) energy should equalWithTolerance(0.01044.asMegaWattHour) case _ => @@ -366,7 +364,6 @@ class ThermalGridIT heatPumpAgent ! Activation(10798) - // Results of 10798 for hp resultListener.expectMessageType[ParticipantResultEvent] match { case ParticipantResultEvent(hpResult) => hpResult.getInputModel shouldBe typicalHpInputModel.getUuid @@ -375,7 +372,6 @@ class ThermalGridIT hpResult.getQ should equalWithTolerance(0.asMegaVar) } - // Results of 7200 for house and storage Range(0, 2) .map { _ => resultListener.expectMessageType[ResultEvent] @@ -389,10 +385,10 @@ class ThermalGridIT indoorTemperature, ) => inputModel shouldBe typicalThermalHouse.getUuid - time shouldBe 7200.toDateTime - qDot should equalWithTolerance(0.011.asMegaWatt) + time shouldBe 10798.toDateTime + qDot should equalWithTolerance(0.asMegaWatt) indoorTemperature should equalWithTolerance( - 20.8788983755569.asDegreeCelsius + 21.9998899446115.asDegreeCelsius ) case CylindricalThermalStorageResult( time, @@ -401,7 +397,7 @@ class ThermalGridIT energy, ) => inputModel shouldBe typicalThermalStorage.getUuid - time shouldBe 7200.toDateTime + time shouldBe 10798.toDateTime qDot should equalWithTolerance(0.asMegaWatt) energy should equalWithTolerance(0.01044.asMegaWattHour) case _ => @@ -448,7 +444,6 @@ class ThermalGridIT hpResult.getQ should equalWithTolerance(0.0.asMegaVar) } - // Results of 10798 for house and storage Range(0, 2) .map { _ => resultListener.expectMessageType[ResultEvent] @@ -462,11 +457,12 @@ class ThermalGridIT indoorTemperature, ) => inputModel shouldBe typicalThermalHouse.getUuid - time shouldBe 10798.toDateTime - qDot should equalWithTolerance(0.asMegaWatt) + time shouldBe 28800.toDateTime + qDot should equalWithTolerance(0.0.asMegaWatt) indoorTemperature should equalWithTolerance( - 21.9998899446115.asDegreeCelsius + 20.19969728245267.asDegreeCelsius ) + case CylindricalThermalStorageResult( time, inputModel, @@ -474,8 +470,8 @@ class ThermalGridIT energy, ) => inputModel shouldBe typicalThermalStorage.getUuid - time shouldBe 10798.toDateTime - qDot should equalWithTolerance(0.asMegaWatt) + time shouldBe 28800.toDateTime + qDot should equalWithTolerance(0.0.asMegaWatt) energy should equalWithTolerance(0.01044.asMegaWattHour) case _ => fail( @@ -520,12 +516,11 @@ class ThermalGridIT indoorTemperature, ) => inputModel shouldBe typicalThermalHouse.getUuid - time shouldBe 28800.toDateTime - qDot should equalWithTolerance(0.0.asMegaWatt) + time shouldBe 41940.toDateTime + qDot should equalWithTolerance(0.01044.asMegaWatt) indoorTemperature should equalWithTolerance( - 20.19969728245267.asDegreeCelsius + 17.9999786813733.asDegreeCelsius ) - case CylindricalThermalStorageResult( time, inputModel, @@ -533,8 +528,8 @@ class ThermalGridIT energy, ) => inputModel shouldBe typicalThermalStorage.getUuid - time shouldBe 28800.toDateTime - qDot should equalWithTolerance(0.0.asMegaWatt) + time shouldBe 41940.toDateTime + qDot should equalWithTolerance((-0.01044).asMegaWatt) energy should equalWithTolerance(0.01044.asMegaWattHour) case _ => fail( @@ -579,7 +574,6 @@ class ThermalGridIT hpResult.getQ should equalWithTolerance(0.0.asMegaVar) } - // Results of 34132 for house and storage Range(0, 2) .map { _ => resultListener.expectMessageType[ResultEvent] @@ -593,11 +587,12 @@ class ThermalGridIT indoorTemperature, ) => inputModel shouldBe typicalThermalHouse.getUuid - time shouldBe 41940.toDateTime + time shouldBe 45000.toDateTime qDot should equalWithTolerance(0.01044.asMegaWatt) indoorTemperature should equalWithTolerance( - 17.9999786813733.asDegreeCelsius + 18.69584558965105.asDegreeCelsius ) + case CylindricalThermalStorageResult( time, inputModel, @@ -605,9 +600,11 @@ class ThermalGridIT energy, ) => inputModel shouldBe typicalThermalStorage.getUuid - time shouldBe 41940.toDateTime + time shouldBe 45000.toDateTime qDot should equalWithTolerance((-0.01044).asMegaWatt) - energy should equalWithTolerance(0.01044.asMegaWattHour) + energy should equalWithTolerance( + 0.00156599999999999.asMegaWattHour + ) case _ => fail( "Expected a ThermalHouseResult and a ThermalStorageResult but got something else" @@ -638,7 +635,6 @@ class ThermalGridIT ) } - // Results of 45000 for house and storage Range(0, 2) .map { _ => resultListener.expectMessageType[ResultEvent] @@ -652,10 +648,10 @@ class ThermalGridIT indoorTemperature, ) => inputModel shouldBe typicalThermalHouse.getUuid - time shouldBe 45000.toDateTime - qDot should equalWithTolerance(0.01044.asMegaWatt) + time shouldBe 45540.toDateTime + qDot should equalWithTolerance(0.011.asMegaWatt) indoorTemperature should equalWithTolerance( - 18.69584558965105.asDegreeCelsius + 18.81725389847177.asDegreeCelsius ) case CylindricalThermalStorageResult( @@ -665,11 +661,9 @@ class ThermalGridIT energy, ) => inputModel shouldBe typicalThermalStorage.getUuid - time shouldBe 45000.toDateTime - qDot should equalWithTolerance((-0.01044).asMegaWatt) - energy should equalWithTolerance( - 0.00156599999999999.asMegaWattHour - ) + time shouldBe 45540.toDateTime + qDot should equalWithTolerance(0.asMegaWatt) + energy should equalWithTolerance(0.asMegaWattHour) case _ => fail( "Expected a ThermalHouseResult and a ThermalStorageResult but got something else" @@ -680,12 +674,12 @@ class ThermalGridIT scheduler.expectMessage(Completion(heatPumpAgent, Some(45540))) /* TICK 57600 -New weather data: it's getting warmer again -House demand heating : requiredDemand = 0.00 kWh, additionalDemand = 1.70 kWh -House demand water : tba -ThermalStorage : requiredDemand = 10.44 kWh, additionalDemand = 10.44 kWh -DomesticWaterStorage : tba -Heat pump: stays on + New weather data: it's getting warmer again + House demand heating : requiredDemand = 0.00 kWh, additionalDemand = 1.70 kWh + House demand water : tba + ThermalStorage : requiredDemand = 10.44 kWh, additionalDemand = 10.44 kWh + DomesticWaterStorage : tba + Heat pump: stays on */ heatPumpAgent ! Activation(57600) @@ -712,7 +706,6 @@ Heat pump: stays on hpResult.getQ should equalWithTolerance(qRunningHp) } - // Results of 45540 for house and storage Range(0, 2) .map { _ => resultListener.expectMessageType[ResultEvent] @@ -726,10 +719,10 @@ Heat pump: stays on indoorTemperature, ) => inputModel shouldBe typicalThermalHouse.getUuid - time shouldBe 45540.toDateTime + time shouldBe 57600.toDateTime qDot should equalWithTolerance(0.011.asMegaWatt) indoorTemperature should equalWithTolerance( - 18.81725389847177.asDegreeCelsius + 21.77341655767336.asDegreeCelsius ) case CylindricalThermalStorageResult( @@ -739,15 +732,14 @@ Heat pump: stays on energy, ) => inputModel shouldBe typicalThermalStorage.getUuid - time shouldBe 45540.toDateTime + time shouldBe 57600.toDateTime qDot should equalWithTolerance(0.asMegaWatt) - energy should equalWithTolerance(0.asMegaWattHour) - case _ => - fail( - "Expected a ThermalHouseResult and a ThermalStorageResult but got something else" + energy should equalWithTolerance( + 0.asMegaWattHour ) } } + scheduler.expectMessage(Completion(heatPumpAgent, Some(57600))) /* TICK 58256 @@ -769,7 +761,6 @@ Heat pump: stays on hpResult.getQ should equalWithTolerance(qRunningHp) } - // Results of 57600 for house and storage Range(0, 2) .map { _ => resultListener.expectMessageType[ResultEvent] @@ -783,10 +774,10 @@ Heat pump: stays on indoorTemperature, ) => inputModel shouldBe typicalThermalHouse.getUuid - time shouldBe 57600.toDateTime - qDot should equalWithTolerance(0.011.asMegaWatt) + time shouldBe 58256.toDateTime + qDot should equalWithTolerance(0.asMegaWatt) indoorTemperature should equalWithTolerance( - 21.77341655767336.asDegreeCelsius + 21.999922627074.asDegreeCelsius ) case CylindricalThermalStorageResult( @@ -796,8 +787,8 @@ Heat pump: stays on energy, ) => inputModel shouldBe typicalThermalStorage.getUuid - time shouldBe 57600.toDateTime - qDot should equalWithTolerance(0.asMegaWatt) + time shouldBe 58256.toDateTime + qDot should equalWithTolerance(0.011.asMegaWatt) energy should equalWithTolerance( 0.asMegaWattHour ) @@ -825,7 +816,6 @@ Heat pump: stays on hpResult.getQ should equalWithTolerance(0.asMegaVar) } - // Results of 58256 for house and storage Range(0, 2) .map { _ => resultListener.expectMessageType[ResultEvent] @@ -839,10 +829,10 @@ Heat pump: stays on indoorTemperature, ) => inputModel shouldBe typicalThermalHouse.getUuid - time shouldBe 58256.toDateTime + time shouldBe 61673.toDateTime qDot should equalWithTolerance(0.asMegaWatt) indoorTemperature should equalWithTolerance( - 21.999922627074.asDegreeCelsius + 21.7847791618269.asDegreeCelsius ) case CylindricalThermalStorageResult( @@ -852,10 +842,10 @@ Heat pump: stays on energy, ) => inputModel shouldBe typicalThermalStorage.getUuid - time shouldBe 58256.toDateTime - qDot should equalWithTolerance(0.011.asMegaWatt) + time shouldBe 61673.toDateTime + qDot should equalWithTolerance(0.asMegaWatt) energy should equalWithTolerance( - 0.asMegaWattHour + 0.01044.asMegaWattHour ) } } From 0e28577931934fb3b3898a0e827a34565e4963bc Mon Sep 17 00:00:00 2001 From: danielfeismann Date: Tue, 29 Oct 2024 18:10:14 +0100 Subject: [PATCH 11/65] rollback changes at ThermalGrid.results() --- .../scala/edu/ie3/simona/model/thermal/ThermalGrid.scala | 8 ++++---- 1 file changed, 4 insertions(+), 4 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 aa70a2a80c..9926c4aa5a 100644 --- a/src/main/scala/edu/ie3/simona/model/thermal/ThermalGrid.scala +++ b/src/main/scala/edu/ie3/simona/model/thermal/ThermalGrid.scala @@ -643,7 +643,7 @@ final case class ThermalGrid( /** Convert the given state of the thermal grid into result models of it's * constituent models * - * @param lastTick + * @param currentTick * Actual simulation tick * @param state * State to be converted @@ -652,13 +652,13 @@ final case class ThermalGrid( * @return * A [[Seq]] of results of the constituent thermal model */ - def results(lastTick: Long, state: ThermalGridState)(implicit + def results(currentTick: Long, state: ThermalGridState)(implicit startDateTime: ZonedDateTime ): Seq[ResultEntity] = { val maybeHouseResult = house .zip(state.houseState) - .filter { case (_, state) => state.tick == lastTick } + .filter { case (_, state) => state.tick == currentTick } .map { case ( thermalHouse, @@ -674,7 +674,7 @@ final case class ThermalGrid( val maybeStorageResult = storage .zip(state.storageState) - .filter { case (_, state) => state.tick == lastTick } + .filter { case (_, state) => state.tick == currentTick } .map { case ( storage: CylindricalThermalStorage, From a669b5bb5caad4d0da12da3df0432c3ca7a0152f Mon Sep 17 00:00:00 2001 From: danielfeismann Date: Thu, 14 Nov 2024 15:14:32 +0100 Subject: [PATCH 12/65] remove double entry in changelog --- CHANGELOG.md | 1 - 1 file changed, 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 201736009b..ae3049db15 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -130,7 +130,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Fix power flow calculation with em agents [#962](https://github.com/ie3-institute/simona/issues/962) - Fix scheduling at Evcs with more than one Ev at a time without Em [#787](https://github.com/ie3-institute/simona/issues/787) - Fix CheckWindow duration [#921](https://github.com/ie3-institute/simona/issues/921) -- Fixed ThermalStorageResults having multiple entries [#924](https://github.com/ie3-institute/simona/issues/924) - Fix filter for thermal result checking for lastTick not for currentTick [#1008](https://github.com/ie3-institute/simona/issues/1008) - Fixed `CHANGELOG` entry for issue ([#103](https://github.com/ie3-institute/simona/issues/103)) [#941](https://github.com/ie3-institute/simona/issues/941) - Refactoring of `ThermalGrid.handleInfeed` to fix thermal storage recharge correctly when empty [#930](https://github.com/ie3-institute/simona/issues/930) From 78845417a5343532013e3f725ae4d2938de731d1 Mon Sep 17 00:00:00 2001 From: danielfeismann Date: Thu, 14 Nov 2024 15:16:24 +0100 Subject: [PATCH 13/65] Revert "remove double entry in changelog" This reverts commit a669b5bb5caad4d0da12da3df0432c3ca7a0152f. --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ae3049db15..201736009b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -130,6 +130,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Fix power flow calculation with em agents [#962](https://github.com/ie3-institute/simona/issues/962) - Fix scheduling at Evcs with more than one Ev at a time without Em [#787](https://github.com/ie3-institute/simona/issues/787) - Fix CheckWindow duration [#921](https://github.com/ie3-institute/simona/issues/921) +- Fixed ThermalStorageResults having multiple entries [#924](https://github.com/ie3-institute/simona/issues/924) - Fix filter for thermal result checking for lastTick not for currentTick [#1008](https://github.com/ie3-institute/simona/issues/1008) - Fixed `CHANGELOG` entry for issue ([#103](https://github.com/ie3-institute/simona/issues/103)) [#941](https://github.com/ie3-institute/simona/issues/941) - Refactoring of `ThermalGrid.handleInfeed` to fix thermal storage recharge correctly when empty [#930](https://github.com/ie3-institute/simona/issues/930) From 86b3256c41ea8e140cccedd0684c7745fbade8c9 Mon Sep 17 00:00:00 2001 From: danielfeismann Date: Thu, 14 Nov 2024 15:16:48 +0100 Subject: [PATCH 14/65] remove correct double entry in changelog --- CHANGELOG.md | 1 - 1 file changed, 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 201736009b..1c7137c826 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -122,7 +122,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Fixed FixedFeedModelSpec [#861](https://github.com/ie3-institute/simona/issues/861) - Fixing duration calculation in result events [#801](https://github.com/ie3-institute/simona/issues/801) - Handle MobSim requests for current prices [#892](https://github.com/ie3-institute/simona/issues/892) -- Fixed ThermalStorageResults having multiple entries [#924](https://github.com/ie3-institute/simona/issues/924) - Fix determineState of ThermalHouse [#926](https://github.com/ie3-institute/simona/issues/926) - Fix activation of Hp when not under control of an EM [#922](https://github.com/ie3-institute/simona/issues/922) - Fix expected secondaryData in baseStateData [#955](https://github.com/ie3-institute/simona/issues/955) From f4fa8108db07bbfaf3cc68690d2cc5a53ba80c30 Mon Sep 17 00:00:00 2001 From: danielfeismann Date: Thu, 14 Nov 2024 15:38:09 +0100 Subject: [PATCH 15/65] reintroduce demand booleans indicating demand when handle thermal infeed --- .../simona/model/participant/HpModel.scala | 46 ++++++++++++++++--- 1 file changed, 40 insertions(+), 6 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 c64e677ad6..52e1462681 100644 --- a/src/main/scala/edu/ie3/simona/model/participant/HpModel.scala +++ b/src/main/scala/edu/ie3/simona/model/participant/HpModel.scala @@ -143,7 +143,7 @@ final case class HpModel( ) // Determining the operation point and limitations at this tick - val (turnOn, canOperate, canBeOutOfOperation) = + val (turnOn, canOperate, canBeOutOfOperation, houseDemand, storageDemand) = operatesInNextState( lastHpState, currentThermalGridState, @@ -154,7 +154,7 @@ final case class HpModel( // Updating the HpState val updatedState = - calcState(lastHpState, relevantData, turnOn) + calcState(lastHpState, relevantData, turnOn, houseDemand, storageDemand) (canOperate, canBeOutOfOperation, updatedState) } @@ -176,7 +176,8 @@ final case class HpModel( * ThermalEnergyDemand of 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 + * operation and can be out of operation plus the demand of house and + * storage */ private def operatesInNextState( lastState: HpState, @@ -184,7 +185,7 @@ final case class HpModel( relevantData: HpRelevantData, demandHouse: ThermalEnergyDemand, demandThermalStorage: ThermalEnergyDemand, - ): (Boolean, Boolean, Boolean) = { + ): (Boolean, Boolean, Boolean, Boolean, Boolean) = { val ( houseHasDemand, @@ -206,7 +207,13 @@ final case class HpModel( val canBeOutOfOperation = !(demandHouse.hasRequiredDemand && noThermalStorageOrThermalStorageIsEmpty) - (turnHpOn, canOperate, canBeOutOfOperation) + ( + turnHpOn, + canOperate, + canBeOutOfOperation, + houseHasDemand, + heatStorageHasDemand, + ) } /** This method will return booleans whether there is a heat demand of house @@ -268,6 +275,8 @@ final case class HpModel( lastState: HpState, relevantData: HpRelevantData, isRunning: Boolean, + houseDemand: Boolean, + storageDemand: Boolean, ): HpState = { val lastStateStorageQDot = lastState.thermalGridState.storageState .map(_.qDot) @@ -353,13 +362,38 @@ 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 * cosPhiRated * 0.5) + val ( + thermalEnergyDemandHouse, + thermalEnergyDemandStorage, + updatedThermalGridState, + ) = + thermalGrid.energyDemandAndUpdatedState( + data.currentTick, + lastState.ambientTemperature.getOrElse(data.ambientTemperature), + data.ambientTemperature, + lastState.thermalGridState, + ) + + val ( + houseDemand, + heatStorageDemand, + _, + ) = determineDemandBooleans( + lastState, + updatedThermalGridState, + thermalEnergyDemandHouse, + thermalEnergyDemandStorage, + ) + val updatedHpState = calcState( lastState, data, turnOn, + houseDemand, + heatStorageDemand, ) ( From 963670c1013283559080bbc96256d9ff02a28cf0 Mon Sep 17 00:00:00 2001 From: danielfeismann Date: Thu, 14 Nov 2024 15:49:21 +0100 Subject: [PATCH 16/65] add some scala doc --- .../edu/ie3/simona/model/thermal/ThermalGrid.scala | 10 ++++++++++ 1 file changed, 10 insertions(+) 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 cd2c19691c..b59663a783 100644 --- a/src/main/scala/edu/ie3/simona/model/thermal/ThermalGrid.scala +++ b/src/main/scala/edu/ie3/simona/model/thermal/ThermalGrid.scala @@ -485,6 +485,16 @@ final case class ThermalGrid( } } + /** Determines the most recent threshold of two given input thresholds + * + * @param maybeHouseThreshold + * Option of a possible next threshold of the thermal house + * @param maybeStorageThreshold + * Option of a possible next threshold of the thermal storage + * @return + * The next threshold + */ + private def determineMostRecentThreshold( maybeHouseThreshold: Option[ThermalThreshold], maybeStorageThreshold: Option[ThermalThreshold], From bfd633b6e7194f2ffe8de6f9ac59c3927d4e4a65 Mon Sep 17 00:00:00 2001 From: danielfeismann Date: Thu, 14 Nov 2024 15:54:32 +0100 Subject: [PATCH 17/65] handle sonar code smell --- .../ie3/simona/model/thermal/ThermalGrid.scala | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 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 b59663a783..82b9e9535c 100644 --- a/src/main/scala/edu/ie3/simona/model/thermal/ThermalGrid.scala +++ b/src/main/scala/edu/ie3/simona/model/thermal/ThermalGrid.scala @@ -244,7 +244,7 @@ final case class ThermalGrid( } if ( - (qDotHouseLastState > zeroKW && !(qDotStorageLastState < zeroKW)) | (qDotStorageLastState > zeroKW & heatStorageDemand) + (qDotHouseLastState >= zeroKW && !(qDotStorageLastState >= zeroKW)) | (qDotStorageLastState >= zeroKW & heatStorageDemand) ) { val (updatedHouseState, thermalHouseThreshold, remainingQDotHouse) = handleInfeedHouse( @@ -486,14 +486,14 @@ final case class ThermalGrid( } /** Determines the most recent threshold of two given input thresholds - * - * @param maybeHouseThreshold - * Option of a possible next threshold of the thermal house - * @param maybeStorageThreshold - * Option of a possible next threshold of the thermal storage - * @return - * The next threshold - */ + * + * @param maybeHouseThreshold + * Option of a possible next threshold of the thermal house + * @param maybeStorageThreshold + * Option of a possible next threshold of the thermal storage + * @return + * The next threshold + */ private def determineMostRecentThreshold( maybeHouseThreshold: Option[ThermalThreshold], From 9eee88668b0bab34914708d075170411092faac9 Mon Sep 17 00:00:00 2001 From: danielfeismann Date: Thu, 14 Nov 2024 16:07:48 +0100 Subject: [PATCH 18/65] Revert "handle sonar code smell" This reverts commit bfd633b6e7194f2ffe8de6f9ac59c3927d4e4a65. --- .../ie3/simona/model/thermal/ThermalGrid.scala | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 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 82b9e9535c..b59663a783 100644 --- a/src/main/scala/edu/ie3/simona/model/thermal/ThermalGrid.scala +++ b/src/main/scala/edu/ie3/simona/model/thermal/ThermalGrid.scala @@ -244,7 +244,7 @@ final case class ThermalGrid( } if ( - (qDotHouseLastState >= zeroKW && !(qDotStorageLastState >= zeroKW)) | (qDotStorageLastState >= zeroKW & heatStorageDemand) + (qDotHouseLastState > zeroKW && !(qDotStorageLastState < zeroKW)) | (qDotStorageLastState > zeroKW & heatStorageDemand) ) { val (updatedHouseState, thermalHouseThreshold, remainingQDotHouse) = handleInfeedHouse( @@ -486,14 +486,14 @@ final case class ThermalGrid( } /** Determines the most recent threshold of two given input thresholds - * - * @param maybeHouseThreshold - * Option of a possible next threshold of the thermal house - * @param maybeStorageThreshold - * Option of a possible next threshold of the thermal storage - * @return - * The next threshold - */ + * + * @param maybeHouseThreshold + * Option of a possible next threshold of the thermal house + * @param maybeStorageThreshold + * Option of a possible next threshold of the thermal storage + * @return + * The next threshold + */ private def determineMostRecentThreshold( maybeHouseThreshold: Option[ThermalThreshold], From 122cb6991bf069c948024c7ad330d2c1d93dc0fe Mon Sep 17 00:00:00 2001 From: danielfeismann Date: Thu, 14 Nov 2024 16:11:37 +0100 Subject: [PATCH 19/65] handle sonar code smell --- src/main/scala/edu/ie3/simona/model/thermal/ThermalGrid.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 b59663a783..6b1af8ac04 100644 --- a/src/main/scala/edu/ie3/simona/model/thermal/ThermalGrid.scala +++ b/src/main/scala/edu/ie3/simona/model/thermal/ThermalGrid.scala @@ -244,7 +244,7 @@ final case class ThermalGrid( } if ( - (qDotHouseLastState > zeroKW && !(qDotStorageLastState < zeroKW)) | (qDotStorageLastState > zeroKW & heatStorageDemand) + (qDotHouseLastState > zeroKW && (qDotStorageLastState >= zeroKW)) | (qDotStorageLastState > zeroKW & heatStorageDemand) ) { val (updatedHouseState, thermalHouseThreshold, remainingQDotHouse) = handleInfeedHouse( From eba3b89c350226b3cb3df4492ee8c3cf189865c2 Mon Sep 17 00:00:00 2001 From: danielfeismann Date: Fri, 15 Nov 2024 08:17:35 +0100 Subject: [PATCH 20/65] fmt --- .../ie3/simona/model/thermal/ThermalGrid.scala | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 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 6b1af8ac04..fe90773420 100644 --- a/src/main/scala/edu/ie3/simona/model/thermal/ThermalGrid.scala +++ b/src/main/scala/edu/ie3/simona/model/thermal/ThermalGrid.scala @@ -486,14 +486,14 @@ final case class ThermalGrid( } /** Determines the most recent threshold of two given input thresholds - * - * @param maybeHouseThreshold - * Option of a possible next threshold of the thermal house - * @param maybeStorageThreshold - * Option of a possible next threshold of the thermal storage - * @return - * The next threshold - */ + * + * @param maybeHouseThreshold + * Option of a possible next threshold of the thermal house + * @param maybeStorageThreshold + * Option of a possible next threshold of the thermal storage + * @return + * The next threshold + */ private def determineMostRecentThreshold( maybeHouseThreshold: Option[ThermalThreshold], From 1f6311fe7f0a0da1434e8fd604a503bdfc2c821f Mon Sep 17 00:00:00 2001 From: danielfeismann Date: Fri, 15 Nov 2024 08:26:51 +0100 Subject: [PATCH 21/65] fix scala doc --- .../scala/edu/ie3/simona/model/thermal/ThermalGrid.scala | 6 ++---- 1 file changed, 2 insertions(+), 4 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 fe90773420..b20742d5b6 100644 --- a/src/main/scala/edu/ie3/simona/model/thermal/ThermalGrid.scala +++ b/src/main/scala/edu/ie3/simona/model/thermal/ThermalGrid.scala @@ -213,7 +213,7 @@ final case class ThermalGrid( * Infeed to the grid * @param houseDemand * determines if the thermal house has heat demand - * @param storageDemand + * @param heatStorageDemand * determines if the thermal storage has heat demand * @return * Updated thermal grid state @@ -360,7 +360,7 @@ final case class ThermalGrid( * Current state of the thermal grid * @param qDotHouse * Infeed to the house - * @param qDotStorage + * @param qDotHeatStorage * Infeed to the heat storage * @return * Updated thermal grid state and the next threshold if there is one @@ -459,8 +459,6 @@ final case class ThermalGrid( * grid (negative qDot). * @param tick * Current tick - * @param ambientTemperature - * Ambient temperature * @param state * Current state of the houses * @param qDot From 95d0562cb88694ba44145dd968307f7c5c850f8e Mon Sep 17 00:00:00 2001 From: danielfeismann Date: Fri, 15 Nov 2024 11:12:20 +0100 Subject: [PATCH 22/65] refactor demand booleans of thermal units into ThermalDemandIndicator class --- .../simona/model/participant/HpModel.scala | 46 +++++++++---------- .../simona/model/thermal/ThermalGrid.scala | 41 ++++++++++------- .../model/thermal/ThermalGridTestData.scala | 9 +++- .../ThermalGridWithHouseAndStorageSpec.scala | 6 +-- .../ThermalGridWithHouseOnlySpec.scala | 12 ++--- .../ThermalGridWithStorageOnlySpec.scala | 10 ++-- 6 files changed, 62 insertions(+), 62 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 52e1462681..a7a585820b 100644 --- a/src/main/scala/edu/ie3/simona/model/participant/HpModel.scala +++ b/src/main/scala/edu/ie3/simona/model/participant/HpModel.scala @@ -12,6 +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.{ + ThermalDemandIndicator, ThermalEnergyDemand, ThermalGridState, } @@ -143,7 +144,7 @@ final case class HpModel( ) // Determining the operation point and limitations at this tick - val (turnOn, canOperate, canBeOutOfOperation, houseDemand, storageDemand) = + val (turnOn, canOperate, canBeOutOfOperation, demandIndicator) = operatesInNextState( lastHpState, currentThermalGridState, @@ -154,7 +155,7 @@ final case class HpModel( // Updating the HpState val updatedState = - calcState(lastHpState, relevantData, turnOn, houseDemand, storageDemand) + calcState(lastHpState, relevantData, turnOn, demandIndicator) (canOperate, canBeOutOfOperation, updatedState) } @@ -176,8 +177,8 @@ final case class HpModel( * ThermalEnergyDemand of 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 plus the demand of house and - * storage + * operation and can be out of operation plus the + * [[ThermalDemandIndicator]] of the thermal units */ private def operatesInNextState( lastState: HpState, @@ -185,11 +186,10 @@ final case class HpModel( relevantData: HpRelevantData, demandHouse: ThermalEnergyDemand, demandThermalStorage: ThermalEnergyDemand, - ): (Boolean, Boolean, Boolean, Boolean, Boolean) = { + ): (Boolean, Boolean, Boolean, ThermalDemandIndicator) = { val ( - houseHasDemand, - heatStorageHasDemand, + demandIndicator, noThermalStorageOrThermalStorageIsEmpty, ) = determineDemandBooleans( lastState, @@ -198,8 +198,8 @@ final case class HpModel( demandThermalStorage, ) - val turnHpOn: Boolean = - houseHasDemand || heatStorageHasDemand + val turnHpOn = + demandIndicator.houseDemand || demandIndicator.heatStorageDemand val canOperate = demandHouse.hasRequiredDemand || demandHouse.hasAdditionalDemand || @@ -211,8 +211,7 @@ final case class HpModel( turnHpOn, canOperate, canBeOutOfOperation, - houseHasDemand, - heatStorageHasDemand, + demandIndicator, ) } @@ -239,7 +238,7 @@ final case class HpModel( updatedGridState: ThermalGridState, demandHouse: ThermalEnergyDemand, demandThermalStorage: ThermalEnergyDemand, - ): (Boolean, Boolean, Boolean) = { + ): (ThermalDemandIndicator, Boolean) = { implicit val tolerance: Energy = KilowattHours(1e-3) val noThermalStorageOrThermalStorageIsEmpty: Boolean = updatedGridState.storageState.isEmpty || updatedGridState.storageState @@ -251,7 +250,9 @@ final case class HpModel( (demandHouse.hasRequiredDemand && noThermalStorageOrThermalStorageIsEmpty) || (lastHpState.isRunning && demandHouse.hasAdditionalDemand) val heatStorageDemand = demandThermalStorage.hasRequiredDemand || (lastHpState.isRunning && demandThermalStorage.hasAdditionalDemand) - (houseDemand, heatStorageDemand, noThermalStorageOrThermalStorageIsEmpty) + + val demandIndicator = ThermalDemandIndicator(houseDemand, heatStorageDemand) + (demandIndicator, noThermalStorageOrThermalStorageIsEmpty) } /** Calculate state depending on whether heat pump is needed or not. Also @@ -264,10 +265,9 @@ 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 houseDemand - * determines if the thermal house has heat demand - * @param storageDemand - * determines if the thermal storage has heat demand + * @param demandIndicator + * determines if the thermal units (house, storage) having some heat demand + * or not * @return * next [[HpState]] */ @@ -275,8 +275,7 @@ final case class HpModel( lastState: HpState, relevantData: HpRelevantData, isRunning: Boolean, - houseDemand: Boolean, - storageDemand: Boolean, + demandIndicator: ThermalDemandIndicator, ): HpState = { val lastStateStorageQDot = lastState.thermalGridState.storageState .map(_.qDot) @@ -298,8 +297,7 @@ final case class HpModel( relevantData.ambientTemperature, isRunning, newThermalPower, - houseDemand, - storageDemand, + demandIndicator, ) HpState( @@ -378,8 +376,7 @@ final case class HpModel( ) val ( - houseDemand, - heatStorageDemand, + demandIndicator, _, ) = determineDemandBooleans( lastState, @@ -392,8 +389,7 @@ final case class HpModel( lastState, data, turnOn, - houseDemand, - heatStorageDemand, + demandIndicator, ) ( 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 b20742d5b6..8e603fe512 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.{ + ThermalDemandIndicator, ThermalEnergyDemand, ThermalGridState, } @@ -160,10 +161,9 @@ final case class ThermalGrid( * determines whether the heat pump is running or not * @param qDot * Thermal energy balance - * @param houseDemand - * determines if the thermal house has heat demand - * @param storageDemand - * determines if the thermal storage has heat demand + * @param demandIndicator + * determines if the thermal units (house, storage) having some heat demand + * or not * @return * The updated state of the grid */ @@ -174,8 +174,7 @@ final case class ThermalGrid( ambientTemperature: Temperature, isRunning: Boolean, qDot: Power, - houseDemand: Boolean, - storageDemand: Boolean, + demandIndicator: ThermalDemandIndicator, ): (ThermalGridState, Option[ThermalThreshold]) = if (qDot > zeroKW) handleInfeed( tick, @@ -184,8 +183,7 @@ final case class ThermalGrid( state, isRunning, qDot, - houseDemand, - storageDemand, + demandIndicator, ) else handleConsumption( @@ -211,10 +209,9 @@ final case class ThermalGrid( * determines whether the heat pump is running or not * @param qDot * Infeed to the grid - * @param houseDemand - * determines if the thermal house has heat demand - * @param heatStorageDemand - * determines if the thermal storage has heat demand + * @param demandIndicator + * determines if the thermal units (house, storage) having some heat demand + * or not * @return * Updated thermal grid state */ @@ -225,8 +222,7 @@ final case class ThermalGrid( state: ThermalGridState, isRunning: Boolean, qDot: Power, - houseDemand: Boolean, - heatStorageDemand: Boolean, + demandIndicator: ThermalDemandIndicator, ): (ThermalGridState, Option[ThermalThreshold]) = { // TODO: We would need to issue a storage result model here... @@ -244,7 +240,7 @@ final case class ThermalGrid( } if ( - (qDotHouseLastState > zeroKW && (qDotStorageLastState >= zeroKW)) | (qDotStorageLastState > zeroKW & heatStorageDemand) + (qDotHouseLastState > zeroKW && (qDotStorageLastState >= zeroKW)) | (qDotStorageLastState > zeroKW & demandIndicator.heatStorageDemand) ) { val (updatedHouseState, thermalHouseThreshold, remainingQDotHouse) = handleInfeedHouse( @@ -307,7 +303,7 @@ final case class ThermalGrid( } } else { - (houseDemand, heatStorageDemand) match { + (demandIndicator.houseDemand, demandIndicator.heatStorageDemand) match { case (true, _) => // house first then heatStorage after heating House @@ -742,6 +738,19 @@ object ThermalGrid { thermalGrid.storage.map(_.startingState), ) + /** Wraps booleans indicating the demand of thermal units (thermal house, + * thermal storage). + * + * @param houseDemand + * Boolean indicating the demand of the thermal house + * @param heatStorageDemand + * Boolean indicating the demand of the thermal heat storage + */ + final case class ThermalDemandIndicator private ( + houseDemand: Boolean, + heatStorageDemand: Boolean, + ) + /** 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 0a016d4566..21f27b1892 100644 --- a/src/test/scala/edu/ie3/simona/model/thermal/ThermalGridTestData.scala +++ b/src/test/scala/edu/ie3/simona/model/thermal/ThermalGridTestData.scala @@ -9,6 +9,7 @@ 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 edu.ie3.simona.model.thermal.ThermalGrid.ThermalDemandIndicator import squants.energy.{Kilowatts, Power} import squants.thermal.{Celsius, Temperature} @@ -25,8 +26,12 @@ trait ThermalGridTestData { protected val testGridQDotInfeed: Power = Kilowatts(15d) protected val testGridQDotConsumption: Power = Kilowatts(-42d) protected val testGridQDotConsumptionHigh: Power = Kilowatts(-200d) - protected val noThermalDemand: Boolean = false - protected val thermalDemand: Boolean = true + protected val noThermalDemand: ThermalDemandIndicator = + ThermalDemandIndicator(false, false) + protected val onlyThermalDemandOfHouse: ThermalDemandIndicator = + ThermalDemandIndicator(true, false) + protected val onlyThermalDemandOfHeatStorage: ThermalDemandIndicator = + ThermalDemandIndicator(false, true) 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 b2b732ae0e..ce58b6a52a 100644 --- a/src/test/scala/edu/ie3/simona/model/thermal/ThermalGridWithHouseAndStorageSpec.scala +++ b/src/test/scala/edu/ie3/simona/model/thermal/ThermalGridWithHouseAndStorageSpec.scala @@ -498,8 +498,7 @@ class ThermalGridWithHouseAndStorageSpec initialGridState, isNotRunning, externalQDot, - thermalDemand, - noThermalDemand, + onlyThermalDemandOfHouse, ) updatedGridState match { @@ -547,8 +546,7 @@ class ThermalGridWithHouseAndStorageSpec gridState, isNotRunning, externalQDot, - noThermalDemand, - thermalDemand, + onlyThermalDemandOfHeatStorage, ) updatedGridState match { 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 4ecbfc819b..39a614e92c 100644 --- a/src/test/scala/edu/ie3/simona/model/thermal/ThermalGridWithHouseOnlySpec.scala +++ b/src/test/scala/edu/ie3/simona/model/thermal/ThermalGridWithHouseOnlySpec.scala @@ -181,8 +181,7 @@ class ThermalGridWithHouseOnlySpec extends UnitSpec with ThermalHouseTestData { gridState, isNotRunning, testGridQDotInfeed, - thermalDemand, - noThermalDemand, + onlyThermalDemandOfHouse, ) updatedGridState match { @@ -210,8 +209,7 @@ class ThermalGridWithHouseOnlySpec extends UnitSpec with ThermalHouseTestData { testGridAmbientTemperature, isRunning, testGridQDotInfeed, - thermalDemand, - noThermalDemand, + onlyThermalDemandOfHouse, ) match { case ( ThermalGridState( @@ -236,8 +234,7 @@ class ThermalGridWithHouseOnlySpec extends UnitSpec with ThermalHouseTestData { testGridAmbientTemperature, isNotRunning, testGridQDotConsumption, - thermalDemand, - noThermalDemand, + onlyThermalDemandOfHouse, ) match { case ( ThermalGridState( @@ -262,8 +259,7 @@ class ThermalGridWithHouseOnlySpec extends UnitSpec with ThermalHouseTestData { testGridAmbientTemperature, isNotRunning, Megawatts(0d), - thermalDemand, - noThermalDemand, + onlyThermalDemandOfHouse, ) match { case ( ThermalGridState( 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 c1ded4c6dc..c2c1880fef 100644 --- a/src/test/scala/edu/ie3/simona/model/thermal/ThermalGridWithStorageOnlySpec.scala +++ b/src/test/scala/edu/ie3/simona/model/thermal/ThermalGridWithStorageOnlySpec.scala @@ -184,8 +184,7 @@ class ThermalGridWithStorageOnlySpec gridState, isNotRunning, testGridQDotInfeed, - noThermalDemand, - thermalDemand, + onlyThermalDemandOfHeatStorage, ) updatedGridState match { @@ -211,8 +210,7 @@ class ThermalGridWithStorageOnlySpec testGridAmbientTemperature, isRunning, testGridQDotInfeed, - noThermalDemand, - thermalDemand, + onlyThermalDemandOfHeatStorage, ) nextThreshold shouldBe Some(StorageFull(276000L)) @@ -247,8 +245,7 @@ class ThermalGridWithStorageOnlySpec testGridAmbientTemperature, isRunning, testGridQDotConsumptionHigh, - thermalDemand, - noThermalDemand, + onlyThermalDemandOfHouse, ) match { case ( ThermalGridState( @@ -274,7 +271,6 @@ class ThermalGridWithStorageOnlySpec isRunning, Kilowatts(0d), noThermalDemand, - noThermalDemand, ) updatedState match { case ( From 96a7dfba7b8364c5a202000929f04495f2739fa5 Mon Sep 17 00:00:00 2001 From: danielfeismann Date: Fri, 15 Nov 2024 12:04:45 +0100 Subject: [PATCH 23/65] fmt --- src/main/scala/edu/ie3/simona/model/participant/HpModel.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 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 097f5ae200..962142e077 100644 --- a/src/main/scala/edu/ie3/simona/model/participant/HpModel.scala +++ b/src/main/scala/edu/ie3/simona/model/participant/HpModel.scala @@ -291,8 +291,8 @@ final case class HpModel( val (newActivePower, newThermalPower) = if (isRunning) (pRated, pThermal) - else if (lastStateStorageqDot < zeroKW) - (zeroKW, lastStateStorageqDot * (-1)) + else if (lastStateStorageQDot < zeroKW) + (zeroKW, lastStateStorageQDot * (-1)) else (zeroKW, zeroKW) /* Push thermal energy to the thermal grid and get its updated state in return */ From 6631b67a6f9cda69e9bbb1453123b400971fe153 Mon Sep 17 00:00:00 2001 From: danielfeismann Date: Fri, 15 Nov 2024 12:14:38 +0100 Subject: [PATCH 24/65] remove comment from merge conflict --- .../scala/edu/ie3/simona/model/participant/HpModel.scala | 7 ------- 1 file changed, 7 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 962142e077..bc413ba76b 100644 --- a/src/main/scala/edu/ie3/simona/model/participant/HpModel.scala +++ b/src/main/scala/edu/ie3/simona/model/participant/HpModel.scala @@ -132,13 +132,6 @@ final case class HpModel( relevantData: HpRelevantData, ): (Boolean, Boolean, HpState) = { - /* 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( From 7341e10ac9e99eb80fb765ce6424be51557a13d5 Mon Sep 17 00:00:00 2001 From: danielfeismann Date: Fri, 15 Nov 2024 14:09:46 +0100 Subject: [PATCH 25/65] remove unnecessary value --- src/main/scala/edu/ie3/simona/model/thermal/ThermalGrid.scala | 3 +-- 1 file changed, 1 insertion(+), 2 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 b74c8ffa04..f50b493b9c 100644 --- a/src/main/scala/edu/ie3/simona/model/thermal/ThermalGrid.scala +++ b/src/main/scala/edu/ie3/simona/model/thermal/ThermalGrid.scala @@ -369,8 +369,7 @@ final case class ThermalGrid( qDotHouse: Power, qDotHeatStorage: Power, ): (ThermalGridState, Option[ThermalThreshold]) = { - // FIXME: Is there any case where we get back some remainingQDotHouse? - val (updatedHouseState, thermalHouseThreshold, remainingQDotHouse) = + val (updatedHouseState, thermalHouseThreshold, _) = handleInfeedHouse( tick, lastAmbientTemperature, From fa36523ce73f8c0074a5ed9bb7fa03f4917690d0 Mon Sep 17 00:00:00 2001 From: staudtMarius Date: Fri, 15 Nov 2024 15:08:22 +0100 Subject: [PATCH 26/65] Removing logs in `logs/simona`. --- CHANGELOG.md | 1 + src/main/resources/logback.xml | 8 -------- .../simona/logging/logback/LogbackConfiguration.scala | 11 +++++------ 3 files changed, 6 insertions(+), 14 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2a2cb7b066..cb4a719cb1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -99,6 +99,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - 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) - Refactoring to use zeroKW and zeroKWH in thermal grid unit tests [#1023](https://github.com/ie3-institute/simona/issues/1023) +- Removing logs in `logs/simona` [#1017](https://github.com/ie3-institute/simona/issues/1017) ### Fixed - Fix rendering of references in documentation [#505](https://github.com/ie3-institute/simona/issues/505) diff --git a/src/main/resources/logback.xml b/src/main/resources/logback.xml index e5abb4e391..3448ebab83 100644 --- a/src/main/resources/logback.xml +++ b/src/main/resources/logback.xml @@ -18,14 +18,6 @@ - logs/simona/simona.log - - - logs/simona/archive/simona-%d{yyyyMMdd'T'HHmmss}-${bySecond}.log - - 10 - 3GB - %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n diff --git a/src/main/scala/edu/ie3/simona/logging/logback/LogbackConfiguration.scala b/src/main/scala/edu/ie3/simona/logging/logback/LogbackConfiguration.scala index 3d236a1f41..58f982c2c7 100644 --- a/src/main/scala/edu/ie3/simona/logging/logback/LogbackConfiguration.scala +++ b/src/main/scala/edu/ie3/simona/logging/logback/LogbackConfiguration.scala @@ -43,12 +43,11 @@ object LogbackConfiguration extends LazyLogging { ) ) - rootLogger.iteratorForAppenders().asScala.foreach { - case rf: RollingFileAppender[_] => - rf.getTriggeringPolicy.start() - rf.start() - case appender => appender.start() - } + rootLogger + .iteratorForAppenders() + .asScala + .foreach(appender => appender.start()) + case factory => logger.error( s"Cannot configure simulation run logger! Invalid factory: $factory" From 30c4c95a9fa0842b0250e8251b518fee786a469f Mon Sep 17 00:00:00 2001 From: Sebastian Peter Date: Mon, 18 Nov 2024 21:06:15 +0100 Subject: [PATCH 27/65] Configurable log level Completely refactored ResultFileHierarchy Signed-off-by: Sebastian Peter --- .../resources/config/config-template.conf | 2 + src/main/resources/logback.xml | 10 +- .../edu/ie3/simona/config/SimonaConfig.scala | 23 ++ .../event/listener/ResultEventListener.scala | 2 +- .../logback/LogbackConfiguration.scala | 59 ++-- .../scala/edu/ie3/simona/main/RunSimona.scala | 4 +- .../ie3/simona/sim/setup/SetupHelper.scala | 9 +- .../ie3/simona/sim/setup/SimonaSetup.scala | 4 +- .../sim/setup/SimonaStandaloneSetup.scala | 3 +- .../ie3/simona/util/ResultFileHierarchy.scala | 283 +++++++++--------- .../listener/ResultEventListenerSpec.scala | 85 +++--- .../integration/RunSimonaStandaloneIT.scala | 12 +- .../io/file/ResultFileHierarchySpec.scala | 64 +--- .../edu/ie3/simona/sim/SimonaSimSpec.scala | 3 +- .../simona/sim/setup/SimonaSetupSpec.scala | 3 +- 15 files changed, 271 insertions(+), 295 deletions(-) diff --git a/src/main/resources/config/config-template.conf b/src/main/resources/config/config-template.conf index 2d6c5c9194..6c9d886711 100644 --- a/src/main/resources/config/config-template.conf +++ b/src/main/resources/config/config-template.conf @@ -292,6 +292,8 @@ simona.output.thermal = { } simona.output.flex = Boolean | false +simona.output.log.level = String | "INFO" + ################################################################## # Runtime Configuration // todo refactor as this naming is misleading ################################################################## diff --git a/src/main/resources/logback.xml b/src/main/resources/logback.xml index 3448ebab83..909dc6bda8 100644 --- a/src/main/resources/logback.xml +++ b/src/main/resources/logback.xml @@ -17,16 +17,8 @@ INFO - - - %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n - - - INFO - - + - \ No newline at end of file diff --git a/src/main/scala/edu/ie3/simona/config/SimonaConfig.scala b/src/main/scala/edu/ie3/simona/config/SimonaConfig.scala index e31ccce104..0790a6efd9 100644 --- a/src/main/scala/edu/ie3/simona/config/SimonaConfig.scala +++ b/src/main/scala/edu/ie3/simona/config/SimonaConfig.scala @@ -1957,6 +1957,7 @@ object SimonaConfig { base: SimonaConfig.Simona.Output.Base, flex: scala.Boolean, grid: SimonaConfig.GridOutputConfig, + log: SimonaConfig.Simona.Output.Log, participant: SimonaConfig.Simona.Output.Participant, sink: SimonaConfig.Simona.Output.Sink, thermal: SimonaConfig.Simona.Output.Thermal, @@ -1997,6 +1998,22 @@ object SimonaConfig { } + final case class Log( + level: java.lang.String + ) + object Log { + def apply( + c: com.typesafe.config.Config, + parentPath: java.lang.String, + $tsCfgValidator: $TsCfgValidator, + ): SimonaConfig.Simona.Output.Log = { + SimonaConfig.Simona.Output.Log( + level = + if (c.hasPathOrNull("level")) c.getString("level") else "INFO" + ) + } + } + final case class Participant( defaultConfig: SimonaConfig.ParticipantBaseOutputConfig, individualConfigs: scala.List[ @@ -2236,6 +2253,12 @@ object SimonaConfig { parentPath + "grid.", $tsCfgValidator, ), + log = SimonaConfig.Simona.Output.Log( + if (c.hasPathOrNull("log")) c.getConfig("log") + else com.typesafe.config.ConfigFactory.parseString("log{}"), + parentPath + "log.", + $tsCfgValidator, + ), participant = SimonaConfig.Simona.Output.Participant( if (c.hasPathOrNull("participant")) c.getConfig("participant") else com.typesafe.config.ConfigFactory.parseString("participant{}"), diff --git a/src/main/scala/edu/ie3/simona/event/listener/ResultEventListener.scala b/src/main/scala/edu/ie3/simona/event/listener/ResultEventListener.scala index 8e7dd12abd..d9c910ef1a 100644 --- a/src/main/scala/edu/ie3/simona/event/listener/ResultEventListener.scala +++ b/src/main/scala/edu/ie3/simona/event/listener/ResultEventListener.scala @@ -88,7 +88,7 @@ object ResultEventListener extends Transformer3wResultSupport { filePathFuture.map { fileName => val finalFileName = - fileName match { + fileName.toString match { case name if name.endsWith(".csv.gz") && enableCompression => name.replace(".gz", "") case name if name.endsWith(".csv") => name diff --git a/src/main/scala/edu/ie3/simona/logging/logback/LogbackConfiguration.scala b/src/main/scala/edu/ie3/simona/logging/logback/LogbackConfiguration.scala index 58f982c2c7..31c23e4568 100644 --- a/src/main/scala/edu/ie3/simona/logging/logback/LogbackConfiguration.scala +++ b/src/main/scala/edu/ie3/simona/logging/logback/LogbackConfiguration.scala @@ -10,56 +10,44 @@ import ch.qos.logback.classic.LoggerContext import ch.qos.logback.classic.encoder.PatternLayoutEncoder import ch.qos.logback.classic.filter.ThresholdFilter import ch.qos.logback.classic.spi.ILoggingEvent -import ch.qos.logback.core.rolling.RollingFileAppender import ch.qos.logback.core.FileAppender -import ch.qos.logback.core.filter.Filter import com.typesafe.scalalogging.LazyLogging import org.slf4j.LoggerFactory -import java.io.File -import scala.jdk.CollectionConverters._ +import java.nio.file.Path object LogbackConfiguration extends LazyLogging { - def default(logPath: String): Unit = { + def default(logPath: Path, logLevel: String): Unit = { LoggerFactory.getILoggerFactory match { case loggerContext: LoggerContext => val rootLogger = loggerContext.getLogger("root") - val log = logPath.concat(File.separator).concat("simona.log") - // stop all appenders - rootLogger.iteratorForAppenders().asScala.foreach(_.stop()) - /* Identify the filters of existing rolling file appender */ - val fileLoggerFilterList = rootLogger - .iteratorForAppenders() - .asScala - .find(_.getName == "RF") - .map(_.getCopyOfAttachedFiltersList.asScala.toSeq) + + // Since logback configuration is static + val logFile = logPath.resolve("simona.log") + rootLogger.addAppender( - fileAppender( - log, + createFileAppender( + logFile, + logLevel, "simona-default", - fileLoggerFilterList, loggerContext, ) ) - rootLogger - .iteratorForAppenders() - .asScala - .foreach(appender => appender.start()) - case factory => logger.error( s"Cannot configure simulation run logger! Invalid factory: $factory" ) } - } - private def fileAppender( - logPath: String, + /** Creates a FileAppender that logs to given path + */ + private def createFileAppender( + logPath: Path, + logLevel: String, appenderName: String, - maybeFilterList: Option[Seq[Filter[ILoggingEvent]]], loggerContext: LoggerContext, ): FileAppender[ILoggingEvent] = { @@ -68,23 +56,16 @@ object LogbackConfiguration extends LazyLogging { layoutEncoder.setContext(loggerContext) layoutEncoder.start() + val filter = new ThresholdFilter() + filter.setLevel(logLevel) + filter.start() + val fileAppender = new FileAppender[ILoggingEvent] - fileAppender.setFile(logPath) + fileAppender.setFile(logPath.toString) fileAppender.setEncoder(layoutEncoder) fileAppender.setContext(loggerContext) fileAppender.setName(appenderName) - /* If applicable, apply the filters from existing file logger else log with "INFO"-Level */ - maybeFilterList match { - case Some(filterList) => - if (filterList.isEmpty) { // No filters in appenders -> Empty List - val filter = new ThresholdFilter() - filter.setLevel("INFO") - filter.start() - fileAppender.addFilter(filter) - } else { - filterList.foreach(fileAppender.addFilter) - } - } + fileAppender.addFilter(filter) fileAppender.start() fileAppender diff --git a/src/main/scala/edu/ie3/simona/main/RunSimona.scala b/src/main/scala/edu/ie3/simona/main/RunSimona.scala index c255990a05..9a4e26fdb2 100644 --- a/src/main/scala/edu/ie3/simona/main/RunSimona.scala +++ b/src/main/scala/edu/ie3/simona/main/RunSimona.scala @@ -57,7 +57,7 @@ trait RunSimona[T <: SimonaSetup] extends LazyLogging { private def printGoodbye( successful: Boolean, - outputPath: String = "", + logOutputDir: Path, ): Unit = { val myWords = Array( "\"Vielleicht ist heute ein besonders guter Tag zum Sterben.\" - Worf (in Star Trek: Der erste Kontakt)", @@ -78,7 +78,7 @@ trait RunSimona[T <: SimonaSetup] extends LazyLogging { // to ensure that the link to the log is printed last Thread.sleep(1000) - val path = Path.of(outputPath).resolve("simona.log").toUri + val path = logOutputDir.resolve("simona.log").toUri logger.error( s"Simulation stopped due to the occurrence of an error! The full log can be found here: $path" diff --git a/src/main/scala/edu/ie3/simona/sim/setup/SetupHelper.scala b/src/main/scala/edu/ie3/simona/sim/setup/SetupHelper.scala index 535b140798..835a6f2599 100644 --- a/src/main/scala/edu/ie3/simona/sim/setup/SetupHelper.scala +++ b/src/main/scala/edu/ie3/simona/sim/setup/SetupHelper.scala @@ -220,7 +220,7 @@ trait SetupHelper extends LazyLogging { val modelsToWrite = SetupHelper.allResultEntitiesToWrite(simonaConfig.simona.output) - val resultFileHierarchy = ResultFileHierarchy( + ResultFileHierarchy( simonaConfig.simona.output.base.dir, simonaConfig.simona.simulationName, ResultEntityPathConfig( @@ -230,15 +230,10 @@ trait SetupHelper extends LazyLogging { simonaConfig.simona.simulationName, ), ), + config = Some(config), addTimeStampToOutputDir = simonaConfig.simona.output.base.addTimestampToOutputDir, - createDirs = createDirs, ) - - // copy config data to output directory - ResultFileHierarchy.prepareDirectories(config, resultFileHierarchy) - - resultFileHierarchy } } diff --git a/src/main/scala/edu/ie3/simona/sim/setup/SimonaSetup.scala b/src/main/scala/edu/ie3/simona/sim/setup/SimonaSetup.scala index 9c6effa859..4a03b2b905 100644 --- a/src/main/scala/edu/ie3/simona/sim/setup/SimonaSetup.scala +++ b/src/main/scala/edu/ie3/simona/sim/setup/SimonaSetup.scala @@ -21,6 +21,8 @@ import org.apache.pekko.actor.typed.ActorRef import org.apache.pekko.actor.typed.scaladsl.ActorContext import org.apache.pekko.actor.{ActorRef => ClassicRef} +import java.nio.file.Path + /** Trait that can be used to setup a customized simona simulation by providing * implementations for all setup information required by a * [[edu.ie3.simona.sim.SimonaSim]]. Most of the time, using or extending @@ -40,7 +42,7 @@ trait SimonaSetup { /** Directory of the log output. */ - def logOutputDir: String + def logOutputDir: Path /** Creates the runtime event listener * diff --git a/src/main/scala/edu/ie3/simona/sim/setup/SimonaStandaloneSetup.scala b/src/main/scala/edu/ie3/simona/sim/setup/SimonaStandaloneSetup.scala index 65d47863cf..55848d2ced 100644 --- a/src/main/scala/edu/ie3/simona/sim/setup/SimonaStandaloneSetup.scala +++ b/src/main/scala/edu/ie3/simona/sim/setup/SimonaStandaloneSetup.scala @@ -50,6 +50,7 @@ import org.apache.pekko.actor.typed.scaladsl.adapter.{ } import org.apache.pekko.actor.{ActorRef => ClassicRef} +import java.nio.file.Path import java.util.UUID import java.util.concurrent.LinkedBlockingQueue import scala.jdk.CollectionConverters._ @@ -68,7 +69,7 @@ class SimonaStandaloneSetup( override val args: Array[String], ) extends SimonaSetup { - override def logOutputDir: String = resultFileHierarchy.logOutputDir + override def logOutputDir: Path = resultFileHierarchy.logOutputDir override def gridAgents( context: ActorContext[_], diff --git a/src/main/scala/edu/ie3/simona/util/ResultFileHierarchy.scala b/src/main/scala/edu/ie3/simona/util/ResultFileHierarchy.scala index 171c5835a2..aceab43822 100644 --- a/src/main/scala/edu/ie3/simona/util/ResultFileHierarchy.scala +++ b/src/main/scala/edu/ie3/simona/util/ResultFileHierarchy.scala @@ -7,7 +7,7 @@ package edu.ie3.simona.util import java.io.{BufferedWriter, File, FileWriter} -import java.nio.file.{Files, Paths} +import java.nio.file.{Files, Path, Paths} import java.text.SimpleDateFormat import com.typesafe.config.{ConfigRenderOptions, Config => TypesafeConfig} import com.typesafe.scalalogging.LazyLogging @@ -20,130 +20,135 @@ import edu.ie3.simona.exceptions.FileHierarchyException import edu.ie3.simona.io.result.ResultSinkType import edu.ie3.simona.io.result.ResultSinkType.Csv import edu.ie3.simona.logging.logback.LogbackConfiguration -import edu.ie3.simona.util.ResultFileHierarchy.ResultEntityPathConfig import edu.ie3.util.io.FileIOUtils import org.apache.commons.io.FilenameUtils._ import scala.jdk.OptionConverters.RichOptional -/** Represents the output directory where the results will be materialized. If - * new directories are added please remember to add them to the dirsToBeCreated - * Vector if they should be created. Otherwise they will not be created! - * - * @version 0.1 - * @since 12.01.20 +/** Represents the output directory where the results will be materialized. */ -final case class ResultFileHierarchy( - private val outputDir: String, - private val simulationName: String, - private val resultEntityPathConfig: ResultEntityPathConfig, - private val configureLogger: String => Unit = LogbackConfiguration.default, - private val addTimeStampToOutputDir: Boolean = true, - private val createDirs: Boolean = false, -) extends LazyLogging { +final case class ResultFileHierarchy private ( + runOutputDir: Path, + rawOutputDataFilePaths: Map[Class[_ <: ResultEntity], Path], + configOutputDir: Path, + logOutputDir: Path, + tmpDir: Path, + resultSinkType: ResultSinkType, + resultEntitiesToConsider: Set[Class[_ <: ResultEntity]], +) - private val fileSeparator: String = File.separator - - val runStartTimeUTC: String = - new SimpleDateFormat("yyyy-MM-dd_HH-mm-ss").format(new java.util.Date()) - - val baseOutputDir: String = buildBaseOutputDir - - val runOutputDir: String = buildRunOutputDir +object ResultFileHierarchy extends LazyLogging { - val configOutputDir: String = - runOutputDir.concat(fileSeparator).concat("configs") + /** Creates the [[ResultFileHierarchy]] and relevant directories + */ + def apply( + outputDir: String, + simulationName: String, + resultEntityPathConfig: ResultEntityPathConfig, + configureLogger: (Path, String) => Unit = LogbackConfiguration.default, + config: Option[TypesafeConfig] = None, + addTimeStampToOutputDir: Boolean = true, + ): ResultFileHierarchy = { + + val runStartTimeUTC = Option.when(addTimeStampToOutputDir)( + new SimpleDateFormat("yyyy-MM-dd_HH-mm-ss").format(new java.util.Date()) + ) - val rawOutputDataDir: String = - runOutputDir.concat(fileSeparator).concat("rawOutputData") + val baseOutputDir = buildBaseOutputDir(outputDir) - val logOutputDir: String = - runOutputDir.concat(fileSeparator).concat("log") + val runOutputDir = buildRunOutputDir( + baseOutputDir, + simulationName, + runStartTimeUTC, + ) - val resultSinkType: ResultSinkType = resultEntityPathConfig.resultSinkType + val configOutputDir = runOutputDir.resolve("configs") + val rawOutputDataDir = runOutputDir.resolve("rawOutputData") + val logOutputDir = runOutputDir.resolve("log") + val tmpDir = runOutputDir.resolve("tmp") - val resultEntitiesToConsider: Set[Class[_ <: ResultEntity]] = - resultEntityPathConfig.resultEntitiesToConsider + val resultSinkType: ResultSinkType = resultEntityPathConfig.resultSinkType - val rawOutputDataFilePaths: Map[Class[_ <: ResultEntity], String] = { - resultSinkType match { - case csv: Csv => - resultEntityPathConfig.resultEntitiesToConsider - .map(resultEntityClass => - ( - resultEntityClass, - ResultFileHierarchy.buildRawOutputFilePath( + val rawOutputDataFilePaths: Map[Class[_ <: ResultEntity], Path] = { + resultSinkType match { + case csv: Csv => + resultEntityPathConfig.resultEntitiesToConsider + .map(resultEntityClass => + ( resultEntityClass, - csv, - rawOutputDataDir, - fileSeparator, - ), + ResultFileHierarchy.buildRawOutputFilePath( + resultEntityClass, + csv, + rawOutputDataDir, + ), + ) ) - ) - .toMap - case _ => - Map.empty + .toMap + case _ => + Map.empty + } } - } - - val graphOutputDir: String = - runOutputDir.concat(fileSeparator).concat("graphs") - val kpiOutputDir: String = runOutputDir.concat(fileSeparator).concat("kpi") - - val tmpDir: String = runOutputDir.concat(fileSeparator).concat("tmp") + val dirsToBeCreated = Seq( + baseOutputDir, + runOutputDir, + configOutputDir, + rawOutputDataDir, + logOutputDir, + tmpDir, + ) - private val dirsToBeCreated: Vector[String] = Vector( - baseOutputDir, - runOutputDir, - configOutputDir, - rawOutputDataDir, - graphOutputDir, - kpiOutputDir, - tmpDir, - logOutputDir, - ) + val resultFileHierarchy = ResultFileHierarchy( + runOutputDir, + rawOutputDataFilePaths, + configOutputDir, + logOutputDir, + tmpDir, + resultSinkType, + resultEntityPathConfig.resultEntitiesToConsider, + ) + prepareDirectories( + baseOutputDir, + dirsToBeCreated, + resultFileHierarchy, + config, + ) - // needs to be the latest call because otherwise the values are null as they are not initialized yet - if (createDirs) - ResultFileHierarchy.createOutputDirectories(this) + // needs to be done after dir creation + configureLogger(logOutputDir, "INFO") // todo - // needs to be done after dir creation - configureLogger(logOutputDir) + resultFileHierarchy + } - /** Builds the base output directory string + /** Builds the base output directory * * @return - * the filepath string to the directory + * the filepath of the directory */ - private def buildBaseOutputDir: String = { - + private def buildBaseOutputDir( + outputDir: String + ): Path = { // clean file string if necessary val cleanedBaseOutputDir = { val normalizedOutputDir = normalize(outputDir) - (fileSeparator + "$").r.replaceAllIn(normalizedOutputDir, "") + (File.separator + "$").r.replaceAllIn(normalizedOutputDir, "") } - // create base output dir if non-existent - Paths.get(cleanedBaseOutputDir).toFile.getAbsolutePath + Paths.get(cleanedBaseOutputDir) } - /** Builds the output directory string for this specific run - * - * @return + /** Builds the output directory for this specific run */ - private def buildRunOutputDir: String = { + private def buildRunOutputDir( + baseOutputDir: Path, + simulationName: String, + runStartTimeUTC: Option[String], + ): Path = { val optionalSuffix = - if (addTimeStampToOutputDir) s"_$runStartTimeUTC" else "" - baseOutputDir - .concat(fileSeparator) - .concat(simulationName) - .concat(optionalSuffix) - } + runStartTimeUTC.map(pattern => s"_$pattern").getOrElse("") -} - -object ResultFileHierarchy extends LazyLogging { + baseOutputDir.resolve(s"$simulationName$optionalSuffix") + } /** @param resultEntitiesToConsider * [[ResultEntity]] s to consider to be written out @@ -161,8 +166,6 @@ object ResultFileHierarchy extends LazyLogging { * the csv sink type parameters * @param rawOutputDataDir * the directory of the raw output data - * @param fileSeparator - * the file separator to be used * @return * an absolute file path as string for the provided model class incl. file * name + extension @@ -170,9 +173,8 @@ object ResultFileHierarchy extends LazyLogging { private def buildRawOutputFilePath( modelClass: Class[_ <: ResultEntity], csvSink: Csv, - rawOutputDataDir: String, - fileSeparator: String, - ): String = { + rawOutputDataDir: Path, + ): Path = { val fileEnding = if (csvSink.fileFormat.startsWith(".")) csvSink.fileFormat @@ -192,64 +194,72 @@ object ResultFileHierarchy extends LazyLogging { ) } - rawOutputDataDir - .concat(fileSeparator) - .concat(filename.toString) - .concat(fileEnding) + rawOutputDataDir.resolve(s"${filename.toString}$fileEnding") } /** Prepares the output directories to be ready to hold the output data. This * includes creating the run directory with all subsequent directories as * well as copying the simulation configuration to the output dir * - * @param config + * @param baseOutputDir + * The base output directory + * @param dirsToBeCreated + * The directories that need to be created + * @param maybeConfig * the config of the current simulation * @param resultFileHierarchy * the output file hierarchy of the current simulation */ - def prepareDirectories( - config: TypesafeConfig, + private def prepareDirectories( + baseOutputDir: Path, + dirsToBeCreated: Seq[Path], resultFileHierarchy: ResultFileHierarchy, + maybeConfig: Option[TypesafeConfig], ): Unit = { // create output directories if they are not present yet if (!runOutputDirExists(resultFileHierarchy)) - ResultFileHierarchy.createOutputDirectories(resultFileHierarchy) + createOutputDirectories( + baseOutputDir, + dirsToBeCreated, + resultFileHierarchy, + ) - logger.info( - "Processing configs for simulation: {}.", - config.getString("simona.simulationName"), - ) + maybeConfig.foreach { config => + logger.info( + "Processing configs for simulation: {}.", + config.getString("simona.simulationName"), + ) - val outFile = - Paths.get(resultFileHierarchy.configOutputDir, "vn_simona.conf").toFile - val bw = new BufferedWriter(new FileWriter(outFile)) - bw.write( - config - .root() - .render( - ConfigRenderOptions - .defaults() - .setOriginComments(false) - .setComments(false) - ) - ) - bw.close() - logger.info("Config '{}' written to '{}'.", outFile.getPath, outFile) + val outFile = + resultFileHierarchy.configOutputDir.resolve("vn_simona.conf").toFile + val bw = new BufferedWriter(new FileWriter(outFile)) + bw.write( + config + .root() + .render( + ConfigRenderOptions + .defaults() + .setOriginComments(false) + .setComments(false) + ) + ) + bw.close() + logger.info("Config '{}' written to '{}'.", outFile.getPath, outFile) + } } /** Checks if the directory of the current run already exists * - * @param outputFileHierarchy + * @param fileHierarchy * the [[ResultFileHierarchy]] that holds information on the run directory * path * @return * true if it exists, false if not */ - def runOutputDirExists(outputFileHierarchy: ResultFileHierarchy): Boolean = { - new File(outputFileHierarchy.runOutputDir).exists() && new File( - outputFileHierarchy.runOutputDir - ).listFiles().length > 0 + def runOutputDirExists(fileHierarchy: ResultFileHierarchy): Boolean = { + val outputDir = fileHierarchy.runOutputDir.toFile + outputDir.exists() && outputDir.listFiles().length > 0 } /** Creates all output directories of the provided [[ResultFileHierarchy]] @@ -257,11 +267,12 @@ object ResultFileHierarchy extends LazyLogging { * @param outputFileHierarchy * the [[ResultFileHierarchy]] the directories should be created for */ - def createOutputDirectories( - outputFileHierarchy: ResultFileHierarchy + private def createOutputDirectories( + baseOutputDir: Path, + dirsToBeCreated: Seq[Path], + outputFileHierarchy: ResultFileHierarchy, ): Unit = { // try to create base output dir - val baseOutputDir = Paths.get(outputFileHierarchy.baseOutputDir) // / check for existence of the provided baseOutputDir, if not create it if (Files.exists(baseOutputDir) && baseOutputDir.toFile.isFile) { throw new FileHierarchyException( @@ -270,17 +281,17 @@ object ResultFileHierarchy extends LazyLogging { } // check if there is data inside the runOutputDir taking into account the provided FileHandling - val runOutputDir = new File(outputFileHierarchy.runOutputDir) + val runOutputDir = outputFileHierarchy.runOutputDir.toFile if (runOutputDir.exists() && runOutputDir.listFiles().length > 0) { // files inside the runOutputDir -> fail throw new FileHierarchyException( - s"The runOutputDir ${outputFileHierarchy.runOutputDir} already exists and is NOT empty! " + + s"The runOutputDir ${outputFileHierarchy.runOutputDir.toString} already exists and is NOT empty! " + s"Please either delete or empty the directory." ) } // create the output directories for the specific run - outputFileHierarchy.dirsToBeCreated.foreach(createDir) + dirsToBeCreated.foreach(createDir) } @@ -289,8 +300,8 @@ object ResultFileHierarchy extends LazyLogging { * @param dir * the full path where the directory should be created (incl. it's name) */ - private def createDir(dir: String): Unit = { - val dirFile = new File(dir) + private def createDir(dir: Path): Unit = { + val dirFile = dir.toFile if (!dirFile.mkdirs() && !dirFile.exists()) throw new FileHierarchyException( "The output directory path " + dir diff --git a/src/test/scala/edu/ie3/simona/event/listener/ResultEventListenerSpec.scala b/src/test/scala/edu/ie3/simona/event/listener/ResultEventListenerSpec.scala index f110feca8e..7498e270dc 100644 --- a/src/test/scala/edu/ie3/simona/event/listener/ResultEventListenerSpec.scala +++ b/src/test/scala/edu/ie3/simona/event/listener/ResultEventListenerSpec.scala @@ -84,7 +84,6 @@ class ResultEventListenerSpec classes, resultSinkType, ), - createDirs = true, ) } @@ -122,14 +121,14 @@ class ResultEventListenerSpec ) // after the creation of the listener, it is expected that a corresponding raw result data file is present - val outputFile = new File( - fileHierarchy.rawOutputDataFilePaths.getOrElse( + val outputFile = fileHierarchy.rawOutputDataFilePaths + .getOrElse( classOf[PvResult], fail( s"Cannot get filepath for raw result file of class '${classOf[PvResult].getSimpleName}' from outputFileHierarchy!'" ), ) - ) + .toFile assert(outputFile.exists) assert(outputFile.isFile) @@ -162,14 +161,14 @@ class ResultEventListenerSpec listenerRef ! ParticipantResultEvent(dummyPvResult) - val outputFile = new File( - specificOutputFileHierarchy.rawOutputDataFilePaths.getOrElse( + val outputFile = specificOutputFileHierarchy.rawOutputDataFilePaths + .getOrElse( classOf[PvResult], fail( s"Cannot get filepath for raw result file of class '${classOf[PvResult].getSimpleName}' from outputFileHierarchy!'" ), ) - ) + .toFile // wait until output file exists (headers are flushed out immediately): awaitCond( @@ -219,39 +218,37 @@ class ResultEventListenerSpec ) val outputFiles = Map( - dummyNodeResultString -> new File( - specificOutputFileHierarchy.rawOutputDataFilePaths.getOrElse( + dummyNodeResultString -> specificOutputFileHierarchy.rawOutputDataFilePaths + .getOrElse( classOf[NodeResult], fail( s"Cannot get filepath for raw result file of class '${classOf[NodeResult].getSimpleName}' from outputFileHierarchy!'" ), - ) - ), - dummySwitchResultString -> new File( + ), + dummySwitchResultString -> specificOutputFileHierarchy.rawOutputDataFilePaths.getOrElse( classOf[SwitchResult], fail( s"Cannot get filepath for raw result file of class '${classOf[SwitchResult].getSimpleName}' from outputFileHierarchy!'" ), - ) - ), - dummyLineResultDataString -> new File( - specificOutputFileHierarchy.rawOutputDataFilePaths.getOrElse( + ), + dummyLineResultDataString -> specificOutputFileHierarchy.rawOutputDataFilePaths + .getOrElse( classOf[LineResult], fail( s"Cannot get filepath for raw result file of class '${classOf[LineResult].getSimpleName}' from outputFileHierarchy!'" ), - ) - ), - dummyTrafo2wResultDataString -> new File( - specificOutputFileHierarchy.rawOutputDataFilePaths.getOrElse( + ), + dummyTrafo2wResultDataString -> specificOutputFileHierarchy.rawOutputDataFilePaths + .getOrElse( classOf[Transformer2WResult], fail( s"Cannot get filepath for raw result file of class '${classOf[Transformer2WResult].getSimpleName}' from outputFileHierarchy!'" ), - ) - ), - ) + ), + ).map { case (dummyString, path) => + (dummyString, path.toFile) + } // wait until all output files exist (headers are flushed out immediately): awaitCond( @@ -308,14 +305,15 @@ class ResultEventListenerSpec ) ) - val outputFile = new File( - fileHierarchy.rawOutputDataFilePaths.getOrElse( + val outputFile = fileHierarchy.rawOutputDataFilePaths + .getOrElse( classOf[Transformer3WResult], fail( s"Cannot get filepath for raw result file of class '${classOf[Transformer3WResult].getSimpleName}' from outputFileHierarchy!'" ), ) - ) + .toFile + /* The result file is created at start up and only contains a head line. */ awaitCond( outputFile.exists(), @@ -386,12 +384,14 @@ class ResultEventListenerSpec val outputFile = new File( ".gz$".r.replaceAllIn( - specificOutputFileHierarchy.rawOutputDataFilePaths.getOrElse( - classOf[PvResult], - fail( - s"Cannot get filepath for raw result file of class '${classOf[PvResult].getSimpleName}' from outputFileHierarchy!'" - ), - ), + specificOutputFileHierarchy.rawOutputDataFilePaths + .getOrElse( + classOf[PvResult], + fail( + s"Cannot get filepath for raw result file of class '${classOf[PvResult].getSimpleName}' from outputFileHierarchy!'" + ), + ) + .toString, "", ) ) @@ -413,26 +413,29 @@ class ResultEventListenerSpec // wait until file exists awaitCond( - new File( - specificOutputFileHierarchy.rawOutputDataFilePaths.getOrElse( + specificOutputFileHierarchy.rawOutputDataFilePaths + .getOrElse( classOf[PvResult], fail( s"Cannot get filepath for raw result file of class '${classOf[PvResult].getSimpleName}' from outputFileHierarchy!'" ), ) - ).exists, + .toFile + .exists, timeoutDuration, ) val resultFileSource = Source.fromInputStream( new GZIPInputStream( new FileInputStream( - specificOutputFileHierarchy.rawOutputDataFilePaths.getOrElse( - classOf[PvResult], - fail( - s"Cannot get filepath for raw result file of class '${classOf[PvResult].getSimpleName}' from outputFileHierarchy!'" - ), - ) + specificOutputFileHierarchy.rawOutputDataFilePaths + .getOrElse( + classOf[PvResult], + fail( + s"Cannot get filepath for raw result file of class '${classOf[PvResult].getSimpleName}' from outputFileHierarchy!'" + ), + ) + .toFile ) ) ) diff --git a/src/test/scala/edu/ie3/simona/integration/RunSimonaStandaloneIT.scala b/src/test/scala/edu/ie3/simona/integration/RunSimonaStandaloneIT.scala index 4e53f0e078..083b9681e3 100644 --- a/src/test/scala/edu/ie3/simona/integration/RunSimonaStandaloneIT.scala +++ b/src/test/scala/edu/ie3/simona/integration/RunSimonaStandaloneIT.scala @@ -90,7 +90,7 @@ class RunSimonaStandaloneIT /* check the results */ // check configs - val configOutputDir = new File(resultFileHierarchy.configOutputDir) + val configOutputDir = resultFileHierarchy.configOutputDir.toFile configOutputDir.isDirectory shouldBe true configOutputDir.listFiles.toVector.size shouldBe 1 @@ -118,10 +118,12 @@ class RunSimonaStandaloneIT entityClass: Class[_ <: ResultEntity], ): BufferedSource = { Source.fromFile( - resultFileHierarchy.rawOutputDataFilePaths.getOrElse( - entityClass, - fail(s"Unable to get output path for result entity: $entityClass"), - ) + resultFileHierarchy.rawOutputDataFilePaths + .getOrElse( + entityClass, + fail(s"Unable to get output path for result entity: $entityClass"), + ) + .toFile ) } diff --git a/src/test/scala/edu/ie3/simona/io/file/ResultFileHierarchySpec.scala b/src/test/scala/edu/ie3/simona/io/file/ResultFileHierarchySpec.scala index 3b89d418f2..c273634fbe 100644 --- a/src/test/scala/edu/ie3/simona/io/file/ResultFileHierarchySpec.scala +++ b/src/test/scala/edu/ie3/simona/io/file/ResultFileHierarchySpec.scala @@ -51,37 +51,15 @@ class ResultFileHierarchySpec ), ) - val runOutputDirWithDate = - "vn_simona_".concat(validOutputFileHierarchy.runStartTimeUTC) - - relativizePath( - validOutputFileHierarchy.baseOutputDir - ).toString shouldBe baseOutputDir - relativizePath( - validOutputFileHierarchy.runOutputDir - ).toString shouldBe baseOutputDir + fileSeparator + runOutputDirWithDate - relativizePath( - validOutputFileHierarchy.tmpDir - ).toString shouldBe baseOutputDir + fileSeparator + runOutputDirWithDate + fileSeparator + "tmp" - relativizePath( - validOutputFileHierarchy.configOutputDir - ).toString shouldBe baseOutputDir + fileSeparator + runOutputDirWithDate + fileSeparator + "configs" - relativizePath( - validOutputFileHierarchy.rawOutputDataDir - ).toString shouldBe baseOutputDir + fileSeparator + runOutputDirWithDate + fileSeparator + "rawOutputData" - relativizePath( - validOutputFileHierarchy.graphOutputDir - ).toString shouldBe baseOutputDir + fileSeparator + runOutputDirWithDate + fileSeparator + "graphs" - relativizePath( - validOutputFileHierarchy.kpiOutputDir - ).toString shouldBe baseOutputDir + fileSeparator + runOutputDirWithDate + fileSeparator + "kpi" - - relativizePath( - validOutputFileHierarchy.rawOutputDataFilePaths(classOf[PvResult]) - ).toString shouldBe baseOutputDir + fileSeparator + runOutputDirWithDate + fileSeparator + "rawOutputData" + fileSeparator + "pref_pv_res_suff.csv" + validOutputFileHierarchy.tmpDir.toString shouldBe validOutputFileHierarchy.runOutputDir.toString + fileSeparator + "tmp" + validOutputFileHierarchy.configOutputDir.toString shouldBe validOutputFileHierarchy.runOutputDir.toString + fileSeparator + "configs" + validOutputFileHierarchy.logOutputDir.toString shouldBe validOutputFileHierarchy.runOutputDir.toString + fileSeparator + "log" + + validOutputFileHierarchy + .rawOutputDataFilePaths(classOf[PvResult]) + .toString shouldBe validOutputFileHierarchy.runOutputDir.toString + fileSeparator + "rawOutputData" + fileSeparator + "pref_pv_res_suff.csv" } - "not write directories automatically on instantiation" in {} // todo "write directories automatically on instantiation when requested so" in { // delete file if they exist @@ -97,7 +75,6 @@ class ResultFileHierarchySpec Set(classOf[PvResult]), ResultSinkType.Csv("csv", "pref", "suff"), ), - createDirs = true, ) // check for existence of run output dir @@ -106,26 +83,13 @@ class ResultFileHierarchySpec ) shouldBe true // check for existence of other folders - assert( - Files.exists(new File(validOutputFileHierarchy.baseOutputDir).toPath) - ) - assert(Files.exists(new File(validOutputFileHierarchy.tmpDir).toPath)) - assert( - Files.exists(new File(validOutputFileHierarchy.configOutputDir).toPath) - ) - assert( - Files.exists(new File(validOutputFileHierarchy.rawOutputDataDir).toPath) - ) - assert( - Files.exists(new File(validOutputFileHierarchy.graphOutputDir).toPath) - ) - assert( - Files.exists(new File(validOutputFileHierarchy.kpiOutputDir).toPath) - ) + assert(Files.exists(validOutputFileHierarchy.configOutputDir)) + assert(Files.exists(validOutputFileHierarchy.logOutputDir)) + assert(Files.exists(validOutputFileHierarchy.tmpDir)) // check if tmp directory can be deleted by output file hierarchy ResultFileHierarchy.deleteTmpDir(validOutputFileHierarchy) - assert(!Files.exists(new File(validOutputFileHierarchy.tmpDir).toPath)) + assert(!Files.exists(validOutputFileHierarchy.tmpDir)) } @@ -133,10 +97,8 @@ class ResultFileHierarchySpec } - private def relativizePath(fullPath: String): Path = { - new File(new File("").getAbsolutePath).toPath - .relativize(new File(fullPath).toPath) - } + private def relativizePath(fullPath: Path): Path = + new File(new File("").getAbsolutePath).toPath.relativize(fullPath) // todo output model path config compression should always be disabled -> test for this diff --git a/src/test/scala/edu/ie3/simona/sim/SimonaSimSpec.scala b/src/test/scala/edu/ie3/simona/sim/SimonaSimSpec.scala index 63be126c0f..9d1140be17 100644 --- a/src/test/scala/edu/ie3/simona/sim/SimonaSimSpec.scala +++ b/src/test/scala/edu/ie3/simona/sim/SimonaSimSpec.scala @@ -34,6 +34,7 @@ import org.apache.pekko.actor.typed.scaladsl.{ActorContext, Behaviors} import org.apache.pekko.actor.typed.{ActorRef, Behavior} import org.apache.pekko.actor.{ActorRef => ClassicRef} +import java.nio.file.Path import java.util.UUID class SimonaSimSpec extends ScalaTestWithActorTestKit with UnitSpec { @@ -398,7 +399,7 @@ object SimonaSimSpec { override val args: Array[String] = Array.empty[String] - override def logOutputDir: String = throw new NotImplementedError() + override def logOutputDir: Path = throw new NotImplementedError() override def runtimeEventListener( context: ActorContext[_] diff --git a/src/test/scala/edu/ie3/simona/sim/setup/SimonaSetupSpec.scala b/src/test/scala/edu/ie3/simona/sim/setup/SimonaSetupSpec.scala index 7fa3ab3469..0428ed765b 100644 --- a/src/test/scala/edu/ie3/simona/sim/setup/SimonaSetupSpec.scala +++ b/src/test/scala/edu/ie3/simona/sim/setup/SimonaSetupSpec.scala @@ -26,13 +26,14 @@ import org.apache.pekko.actor.typed.ActorRef import org.apache.pekko.actor.typed.scaladsl.ActorContext import org.apache.pekko.actor.{ActorRef => ClassicRef} +import java.nio.file.Path import java.util.UUID class SimonaSetupSpec extends UnitSpec with SimonaSetup with SubGridGateMokka { override val args: Array[String] = Array.empty[String] - override def logOutputDir: String = throw new NotImplementedError() + override def logOutputDir: Path = throw new NotImplementedError() override def runtimeEventListener( context: ActorContext[_] From adbb3f557b92ecf549e297b9ba8120fe6116d5ff Mon Sep 17 00:00:00 2001 From: Sebastian Peter Date: Tue, 19 Nov 2024 12:02:57 +0100 Subject: [PATCH 28/65] Validation of log level config Actually using the configuration Signed-off-by: Sebastian Peter --- .../ie3/simona/config/ConfigFailFast.scala | 52 ++++++++++++++----- .../logback/LogbackConfiguration.scala | 2 +- .../ie3/simona/sim/setup/SetupHelper.scala | 3 ++ .../ie3/simona/util/ResultFileHierarchy.scala | 4 +- .../simona/config/ConfigFailFastSpec.scala | 22 ++++++++ 5 files changed, 67 insertions(+), 16 deletions(-) diff --git a/src/main/scala/edu/ie3/simona/config/ConfigFailFast.scala b/src/main/scala/edu/ie3/simona/config/ConfigFailFast.scala index 0eaa7e30ba..c3b16d2ca7 100644 --- a/src/main/scala/edu/ie3/simona/config/ConfigFailFast.scala +++ b/src/main/scala/edu/ie3/simona/config/ConfigFailFast.scala @@ -47,7 +47,7 @@ import scala.util.{Failure, Success, Try} * missing config parameters where at least one is needed or check for invalid * or contradicting parameters */ -case object ConfigFailFast extends LazyLogging { +object ConfigFailFast extends LazyLogging { def check(typeSafeConfig: Config, simonaConfig: SimonaConfig): Unit = { check(typeSafeConfig) @@ -139,18 +139,7 @@ case object ConfigFailFast extends LazyLogging { /* Check if the provided combination of data source and parameters are valid */ checkWeatherDataSource(simonaConfig.simona.input.weather.datasource) - /* check if at least one data sink is defined */ - checkDataSink(simonaConfig.simona.output.sink) - - /* Check all output configurations for participant models */ - checkParticipantsOutputConfig( - simonaConfig.simona.output.participant - ) - - /* Check all output configurations for thermal models */ - checkThermalOutputConfig( - simonaConfig.simona.output.thermal - ) + checkOutputConfig(simonaConfig.simona.output) /* Check power flow resolution configuration */ checkPowerFlowResolutionConfiguration(simonaConfig.simona.powerflow) @@ -162,6 +151,28 @@ case object ConfigFailFast extends LazyLogging { checkStoragesConfig(simonaConfig.simona.runtime.participant.storage) } + /** Checks for valid output configuration + * + * @param outputConfig + * the output configuration that should be checked + */ + private def checkOutputConfig( + outputConfig: SimonaConfig.Simona.Output + ): Unit = { + + /* check if at least one data sink is defined */ + checkDataSink(outputConfig.sink) + + /* Check all output configurations for participant models */ + checkParticipantsOutputConfig(outputConfig.participant) + + /* Check all output configurations for thermal models */ + checkThermalOutputConfig(outputConfig.thermal) + + /* Check output configurations for log */ + checkLogOutputConfig(outputConfig.log) + } + /** Checks for valid sink configuration * * @param sink @@ -698,6 +709,21 @@ case object ConfigFailFast extends LazyLogging { checkIndividualOutputConfigs(subConfig.individualConfigs) } + /** Check the config subtree for log output parameterization + * + * @param subConfig + * Output sub config tree for log + */ + private def checkLogOutputConfig( + subConfig: SimonaConfig.Simona.Output.Log + ): Unit = { + val validLogLevels = Seq("TRACE", "DEBUG", "INFO", "WARN", "ERROR") + if (!validLogLevels.contains(subConfig.level)) + throw new InvalidConfigParameterException( + s"Invalid log level \"${subConfig.level}\". Valid log levels: ${validLogLevels.mkString(", ")}" + ) + } + /** Checks resolution of power flow calculation * * @param powerFlow diff --git a/src/main/scala/edu/ie3/simona/logging/logback/LogbackConfiguration.scala b/src/main/scala/edu/ie3/simona/logging/logback/LogbackConfiguration.scala index 31c23e4568..b28499074f 100644 --- a/src/main/scala/edu/ie3/simona/logging/logback/LogbackConfiguration.scala +++ b/src/main/scala/edu/ie3/simona/logging/logback/LogbackConfiguration.scala @@ -18,7 +18,7 @@ import java.nio.file.Path object LogbackConfiguration extends LazyLogging { - def default(logPath: Path, logLevel: String): Unit = { + def default(logLevel: String)(logPath: Path): Unit = { LoggerFactory.getILoggerFactory match { case loggerContext: LoggerContext => val rootLogger = loggerContext.getLogger("root") diff --git a/src/main/scala/edu/ie3/simona/sim/setup/SetupHelper.scala b/src/main/scala/edu/ie3/simona/sim/setup/SetupHelper.scala index 1b2d2df7e7..9b06f604da 100644 --- a/src/main/scala/edu/ie3/simona/sim/setup/SetupHelper.scala +++ b/src/main/scala/edu/ie3/simona/sim/setup/SetupHelper.scala @@ -20,6 +20,7 @@ import edu.ie3.simona.config.SimonaConfig import edu.ie3.simona.exceptions.InitializationException import edu.ie3.simona.exceptions.agent.GridAgentInitializationException import edu.ie3.simona.io.result.ResultSinkType +import edu.ie3.simona.logging.logback.LogbackConfiguration import edu.ie3.simona.model.grid.RefSystem import edu.ie3.simona.util.ConfigUtil.{GridOutputConfigUtil, OutputConfigUtil} import edu.ie3.simona.util.ResultFileHierarchy.ResultEntityPathConfig @@ -226,6 +227,8 @@ trait SetupHelper extends LazyLogging { simonaConfig.simona.simulationName, ), ), + configureLogger = + LogbackConfiguration.default(simonaConfig.simona.output.log.level), config = Some(config), addTimeStampToOutputDir = simonaConfig.simona.output.base.addTimestampToOutputDir, diff --git a/src/main/scala/edu/ie3/simona/util/ResultFileHierarchy.scala b/src/main/scala/edu/ie3/simona/util/ResultFileHierarchy.scala index 2020d74743..bb371308a6 100644 --- a/src/main/scala/edu/ie3/simona/util/ResultFileHierarchy.scala +++ b/src/main/scala/edu/ie3/simona/util/ResultFileHierarchy.scala @@ -45,7 +45,7 @@ object ResultFileHierarchy extends LazyLogging { outputDir: String, simulationName: String, resultEntityPathConfig: ResultEntityPathConfig, - configureLogger: (Path, String) => Unit = LogbackConfiguration.default, + configureLogger: Path => Unit = LogbackConfiguration.default("INFO"), config: Option[TypesafeConfig] = None, addTimeStampToOutputDir: Boolean = true, ): ResultFileHierarchy = { @@ -115,7 +115,7 @@ object ResultFileHierarchy extends LazyLogging { ) // needs to be done after dir creation - configureLogger(logOutputDir, "INFO") // todo + configureLogger(logOutputDir) resultFileHierarchy } diff --git a/src/test/scala/edu/ie3/simona/config/ConfigFailFastSpec.scala b/src/test/scala/edu/ie3/simona/config/ConfigFailFastSpec.scala index ae7bfae423..f86b0b47c0 100644 --- a/src/test/scala/edu/ie3/simona/config/ConfigFailFastSpec.scala +++ b/src/test/scala/edu/ie3/simona/config/ConfigFailFastSpec.scala @@ -897,6 +897,28 @@ class ConfigFailFastSpec extends UnitSpec with ConfigTestData { } } + "Checking log config" should { + val checkLogOutputConfig = + PrivateMethod[Unit](Symbol("checkLogOutputConfig")) + + "identify an unknown log level" in { + val invalidLogConfig = SimonaConfig.Simona.Output.Log("INVALID") + + intercept[InvalidConfigParameterException] { + ConfigFailFast invokePrivate checkLogOutputConfig(invalidLogConfig) + }.getMessage shouldBe "Invalid log level \"INVALID\". Valid log levels: TRACE, DEBUG, INFO, WARN, ERROR" + } + + "let valid log output configuration pass" in { + val validLogConfig = SimonaConfig.Simona.Output.Log("WARN") + + noException shouldBe thrownBy { + ConfigFailFast invokePrivate checkLogOutputConfig(validLogConfig) + } + } + + } + "Checking grid data sources" should { "identify a faulty csv separator" in { val csvParams = From 5c0750c58e8fd29e4d45b2f3e8590e670fb86060 Mon Sep 17 00:00:00 2001 From: Sebastian Peter Date: Tue, 19 Nov 2024 12:05:32 +0100 Subject: [PATCH 29/65] Also setting the log level in example grid, because most people will look there Signed-off-by: Sebastian Peter --- input/samples/vn_simona/vn_simona.conf | 2 ++ 1 file changed, 2 insertions(+) diff --git a/input/samples/vn_simona/vn_simona.conf b/input/samples/vn_simona/vn_simona.conf index 3a795e7cb5..b911acf2c1 100644 --- a/input/samples/vn_simona/vn_simona.conf +++ b/input/samples/vn_simona/vn_simona.conf @@ -101,6 +101,8 @@ simona.output.thermal = { ] } +simona.output.log.level = "INFO" + ################################################################## # Runtime Configuration // todo refactor as this naming is misleading and partly unneeded ################################################################## From 28d23371cdb8a7a71e85e73a5227642e9e408ec6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 20 Nov 2024 12:45:03 +0000 Subject: [PATCH 30/65] Bump org.sonarqube from 5.1.0.4882 to 6.0.0.5145 (#1037) --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index f611c25268..2b7b87ffe4 100644 --- a/build.gradle +++ b/build.gradle @@ -11,7 +11,7 @@ plugins { id "de.undercouch.download" version "5.6.0" // downloads plugin id "kr.motd.sphinx" version "2.10.1" // documentation generation id "com.github.johnrengelman.shadow" version "8.1.1" // fat jar - id "org.sonarqube" version "5.1.0.4882" // sonarqube + id "org.sonarqube" version "6.0.0.5145" // sonarqube id "org.scoverage" version "8.1" // scala code coverage scoverage id "com.github.maiflai.scalatest" version "0.32" // run scalatest without specific spec task id 'org.hidetake.ssh' version '2.11.2' From 741a2b2b5a7c54b7e59c1a9c71931f1eedb02e33 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 21 Nov 2024 07:27:31 +0100 Subject: [PATCH 31/65] Bump tech.units:indriya from 2.2 to 2.2.1 (#1039) Bumps [tech.units:indriya](https://github.com/unitsofmeasurement/indriya) from 2.2 to 2.2.1. - [Release notes](https://github.com/unitsofmeasurement/indriya/releases) - [Commits](https://github.com/unitsofmeasurement/indriya/compare/2.2...2.2.1) --- updated-dependencies: - dependency-name: tech.units:indriya dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 2b7b87ffe4..a69e01e63d 100644 --- a/build.gradle +++ b/build.gradle @@ -145,7 +145,7 @@ dependencies { implementation 'org.apache.commons:commons-math3:3.6.1' // apache commons math3 implementation 'org.apache.poi:poi-ooxml:5.3.0' // used for FilenameUtils implementation 'javax.measure:unit-api:2.2' - implementation 'tech.units:indriya:2.2' // quantities + implementation 'tech.units:indriya:2.2.1' // quantities implementation "org.typelevel:squants_${scalaVersion}:1.8.3" implementation 'org.apache.commons:commons-csv:1.12.0' implementation 'org.scalanlp:breeze_2.13:2.1.0' // scientific calculations (http://www.scalanlp.org/) From 04f02076aa17afccca1c800b14e38602011fe948 Mon Sep 17 00:00:00 2001 From: danielfeismann Date: Thu, 21 Nov 2024 12:50:13 +0100 Subject: [PATCH 32/65] fix import --- src/main/scala/edu/ie3/simona/model/thermal/ThermalGrid.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 b98b42d445..f20bebfc04 100644 --- a/src/main/scala/edu/ie3/simona/model/thermal/ThermalGrid.scala +++ b/src/main/scala/edu/ie3/simona/model/thermal/ThermalGrid.scala @@ -773,7 +773,7 @@ object ThermalGrid { def hasRequiredDemand: Boolean = required > zeroMWh - def hasAdditionalDemand: Boolean = possible > zeroMWH + def hasAdditionalDemand: Boolean = possible > zeroMWh } object ThermalEnergyDemand { From d44e19f49f6762f1684be8b5371ee2b50e6fa95c Mon Sep 17 00:00:00 2001 From: danielfeismann Date: Thu, 21 Nov 2024 13:48:22 +0100 Subject: [PATCH 33/65] fix thermal demand determination --- src/main/scala/edu/ie3/simona/model/participant/HpModel.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 3ab361d867..b03d2eed5e 100644 --- a/src/main/scala/edu/ie3/simona/model/participant/HpModel.scala +++ b/src/main/scala/edu/ie3/simona/model/participant/HpModel.scala @@ -247,7 +247,7 @@ final case class HpModel( ) val houseDemand = - (demandHouse.hasRequiredDemand && noThermalStorageOrThermalStorageIsEmpty) || (lastHpState.isRunning && demandHouse.hasAdditionalDemand) + demandHouse.hasRequiredDemand || (lastHpState.isRunning && demandHouse.hasAdditionalDemand) val heatStorageDemand = demandThermalStorage.hasRequiredDemand || (lastHpState.isRunning && demandThermalStorage.hasAdditionalDemand) From a567d0bb6813702567a5c1bfbbdab91c28613dfb Mon Sep 17 00:00:00 2001 From: danielfeismann Date: Thu, 21 Nov 2024 14:55:10 +0100 Subject: [PATCH 34/65] simplify methods to compare temperatures --- .../scala/edu/ie3/simona/model/thermal/ThermalHouse.scala | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/scala/edu/ie3/simona/model/thermal/ThermalHouse.scala b/src/main/scala/edu/ie3/simona/model/thermal/ThermalHouse.scala index e0fd7024f7..dad75f76ba 100644 --- a/src/main/scala/edu/ie3/simona/model/thermal/ThermalHouse.scala +++ b/src/main/scala/edu/ie3/simona/model/thermal/ThermalHouse.scala @@ -163,8 +163,8 @@ final case class ThermalHouse( innerTemperature: Temperature, boundaryTemperature: Temperature = upperBoundaryTemperature, ): Boolean = - innerTemperature > Kelvin( - boundaryTemperature.toKelvinScale - temperatureTolerance.toKelvinScale + innerTemperature > ( + boundaryTemperature - temperatureTolerance ) /** Check if inner temperature is lower than preferred minimum temperature @@ -176,8 +176,8 @@ final case class ThermalHouse( innerTemperature: Temperature, boundaryTemperature: Temperature = lowerBoundaryTemperature, ): Boolean = - innerTemperature < Kelvin( - boundaryTemperature.toKelvinScale + temperatureTolerance.toKelvinScale + innerTemperature < ( + boundaryTemperature + temperatureTolerance ) /** Calculate the new inner temperature of the thermal house. From 3cccc33c5c42c00697e40ec359cd4ddfec70cbef Mon Sep 17 00:00:00 2001 From: danielfeismann Date: Thu, 21 Nov 2024 14:55:20 +0100 Subject: [PATCH 35/65] fmt --- .../simona/model/participant/HpModel.scala | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 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 b03d2eed5e..7def63c216 100644 --- a/src/main/scala/edu/ie3/simona/model/participant/HpModel.scala +++ b/src/main/scala/edu/ie3/simona/model/participant/HpModel.scala @@ -216,22 +216,22 @@ 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 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. - */ + * 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, From 995818e7ccf065d63b636a374a8c0c250151a046 Mon Sep 17 00:00:00 2001 From: danielfeismann Date: Fri, 22 Nov 2024 09:36:49 +0100 Subject: [PATCH 36/65] correct test cases of HpModelSpec --- .../model/participant/HpModelSpec.scala | 56 +++++++++---------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/src/test/scala/edu/ie3/simona/model/participant/HpModelSpec.scala b/src/test/scala/edu/ie3/simona/model/participant/HpModelSpec.scala index 81c62e549d..0479798610 100644 --- a/src/test/scala/edu/ie3/simona/model/participant/HpModelSpec.scala +++ b/src/test/scala/edu/ie3/simona/model/participant/HpModelSpec.scala @@ -55,7 +55,7 @@ class HpModelSpec true, 95, 15.6, - Some(HouseTemperatureUpperBoundaryReached(31711L)), + Some(HouseTemperatureUpperBoundaryReached(31711)), ), ( HpState( @@ -70,7 +70,7 @@ class HpModelSpec true, 95, 16.4, - Some(HouseTemperatureUpperBoundaryReached(30642L)), + Some(HouseTemperatureUpperBoundaryReached(30642)), ), ( HpState( @@ -85,7 +85,7 @@ class HpModelSpec true, 95, 18.0, - Some(HouseTemperatureUpperBoundaryReached(27771L)), + Some(HouseTemperatureUpperBoundaryReached(27771)), ), ( HpState( @@ -100,7 +100,7 @@ class HpModelSpec false, 0, 19.6, - Some(HouseTemperatureLowerBoundaryReached(13200L)), + Some(HouseTemperatureLowerBoundaryReached(13200)), ), ( HpState( @@ -115,7 +115,7 @@ class HpModelSpec false, 0, 20.4, - Some(HouseTemperatureLowerBoundaryReached(15508L)), + Some(HouseTemperatureLowerBoundaryReached(15508)), ), ( HpState( @@ -124,13 +124,13 @@ class HpModelSpec Some(hpData.ambientTemperature), Kilowatts(95d), Kilowatts(80d), - thermalState(Celsius(17)), + thermalState(Celsius(17), Kilowatts(80d)), None, ), - true, - 95, - 15.6, - Some(HouseTemperatureUpperBoundaryReached(31711L)), + false, + 0, + 31.6, + Some(HouseTemperatureLowerBoundaryReached(29867)), ), ( HpState( @@ -139,13 +139,13 @@ class HpModelSpec Some(hpData.ambientTemperature), Kilowatts(95d), Kilowatts(80d), - thermalState(Celsius(18)), + thermalState(Celsius(18), Kilowatts(80d)), None, ), - true, - 95, - 16.4, - Some(HouseTemperatureUpperBoundaryReached(30642L)), + false, + 0, + 32.4, + Some(HouseTemperatureLowerBoundaryReached(30343)), ), ( HpState( @@ -154,13 +154,13 @@ class HpModelSpec Some(hpData.ambientTemperature), Kilowatts(95d), Kilowatts(80d), - thermalState(Celsius(20)), + thermalState(Celsius(20), Kilowatts(80d)), None, ), - true, - 95, - 18.0, - Some(HouseTemperatureUpperBoundaryReached(27771L)), + false, + 0, + 34.0, + Some(HouseTemperatureLowerBoundaryReached(31200)), ), ( HpState( @@ -169,13 +169,13 @@ class HpModelSpec Some(hpData.ambientTemperature), Kilowatts(95d), Kilowatts(80d), - thermalState(Celsius(22)), + thermalState(Celsius(22), Kilowatts(80d)), None, ), - true, - 95, - 19.6, - Some(HouseTemperatureUpperBoundaryReached(23200L)), + false, + 0, + 35.6, + Some(HouseTemperatureLowerBoundaryReached(31950)), ), ( HpState( @@ -184,13 +184,13 @@ class HpModelSpec Some(hpData.ambientTemperature), Kilowatts(95d), Kilowatts(80d), - thermalState(Celsius(25)), + thermalState(Celsius(25), Kilowatts(80d)), None, ), false, 0, - 22.0, - Some(HouseTemperatureLowerBoundaryReached(19200L)), + 38.0, + Some(HouseTemperatureLowerBoundaryReached(32914)), ), ) From 1b9375f10d420ade8b1253b2d9a428644cedce16 Mon Sep 17 00:00:00 2001 From: danielfeismann Date: Fri, 22 Nov 2024 09:45:37 +0100 Subject: [PATCH 37/65] refactoring thermal demand indicators --- .../simona/model/participant/HpModel.scala | 89 +-- .../simona/model/thermal/ThermalGrid.scala | 59 +- .../model/thermal/ThermalGridTestData.scala | 29 +- .../ThermalGridWithHouseAndStorageSpec.scala | 751 +++++++++--------- .../ThermalGridWithHouseOnlySpec.scala | 7 +- .../ThermalGridWithStorageOnlySpec.scala | 8 +- 6 files changed, 472 insertions(+), 471 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 7def63c216..447e1c09d4 100644 --- a/src/main/scala/edu/ie3/simona/model/participant/HpModel.scala +++ b/src/main/scala/edu/ie3/simona/model/participant/HpModel.scala @@ -12,8 +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.{ - ThermalDemandIndicator, - ThermalEnergyDemand, + ThermalDemandWrapper, ThermalGridState, } import edu.ie3.simona.model.thermal.{ThermalGrid, ThermalThreshold} @@ -133,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( @@ -144,18 +143,17 @@ final case class HpModel( ) // Determining the operation point and limitations at this tick - val (turnOn, canOperate, canBeOutOfOperation, demandIndicator) = + val (turnOn, canOperate, canBeOutOfOperation) = operatesInNextState( lastHpState, currentThermalGridState, relevantData, - demandHouse, - demandThermalStorage, + thermalDemandWrapper, ) // Updating the HpState val updatedState = - calcState(lastHpState, relevantData, turnOn, demandIndicator) + calcState(lastHpState, relevantData, turnOn, thermalDemandWrapper) (canOperate, canBeOutOfOperation, updatedState) } @@ -171,35 +169,31 @@ 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 plus the - * [[ThermalDemandIndicator]] of the thermal units + * operation and can be out of operation */ private def operatesInNextState( lastState: HpState, currentThermalGridState: ThermalGridState, relevantData: HpRelevantData, - demandHouse: ThermalEnergyDemand, - demandThermalStorage: ThermalEnergyDemand, - ): (Boolean, Boolean, Boolean, ThermalDemandIndicator) = { + thermalDemands: ThermalDemandWrapper, + ): (Boolean, Boolean, Boolean) = { - val ( - demandIndicator, - noThermalStorageOrThermalStorageIsEmpty, - ) = determineDemandBooleans( + val demandHouse = thermalDemands.houseDemand + val demandThermalStorage = thermalDemands.heatStorageDemand + + val noThermalStorageOrThermalStorageIsEmpty = determineThermalStorageStatus( lastState, currentThermalGridState, - demandHouse, - demandThermalStorage, ) val turnHpOn = - demandIndicator.houseDemand || demandIndicator.heatStorageDemand + demandHouse.hasRequiredDemand || + (demandHouse.hasAdditionalDemand && lastState.isRunning) + demandThermalStorage.hasRequiredDemand || (demandThermalStorage.hasAdditionalDemand && lastState.isRunning) val canOperate = demandHouse.hasRequiredDemand || demandHouse.hasAdditionalDemand || @@ -211,7 +205,6 @@ final case class HpModel( turnHpOn, canOperate, canBeOutOfOperation, - demandIndicator, ) } @@ -223,22 +216,14 @@ final case class HpModel( * 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( + private def determineThermalStorageStatus( lastHpState: HpState, updatedGridState: ThermalGridState, - demandHouse: ThermalEnergyDemand, - demandThermalStorage: ThermalEnergyDemand, - ): (ThermalDemandIndicator, Boolean) = { + ): Boolean = { implicit val tolerance: Energy = KilowattHours(1e-3) val noThermalStorageOrThermalStorageIsEmpty: Boolean = updatedGridState.storageState.isEmpty || updatedGridState.storageState @@ -246,13 +231,7 @@ final case class HpModel( _.storedEnergy =~ zeroKWh ) - val houseDemand = - demandHouse.hasRequiredDemand || (lastHpState.isRunning && demandHouse.hasAdditionalDemand) - val heatStorageDemand = - demandThermalStorage.hasRequiredDemand || (lastHpState.isRunning && demandThermalStorage.hasAdditionalDemand) - - val demandIndicator = ThermalDemandIndicator(houseDemand, heatStorageDemand) - (demandIndicator, noThermalStorageOrThermalStorageIsEmpty) + noThermalStorageOrThermalStorageIsEmpty } /** Calculate state depending on whether heat pump is needed or not. Also @@ -265,9 +244,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 demandIndicator - * determines if the thermal units (house, storage) having some heat demand - * or not + * @param demandWrapper + * holds the thermal demands of the thermal units (house, storage) * @return * next [[HpState]] */ @@ -275,7 +253,7 @@ final case class HpModel( lastState: HpState, relevantData: HpRelevantData, isRunning: Boolean, - demandIndicator: ThermalDemandIndicator, + demandWrapper: ThermalDemandWrapper, ): HpState = { val lastStateStorageQDot = lastState.thermalGridState.storageState .map(_.qDot) @@ -297,7 +275,7 @@ final case class HpModel( relevantData.ambientTemperature, isRunning, newThermalPower, - demandIndicator, + demandWrapper, ) HpState( @@ -364,9 +342,8 @@ final case class HpModel( val turnOn = setPower > (sRated.toActivePower(cosPhiRated) * 0.5) val ( - thermalEnergyDemandHouse, - thermalEnergyDemandStorage, - updatedThermalGridState, + thermalDemandWrapper, + _, ) = thermalGrid.energyDemandAndUpdatedState( data.currentTick, @@ -375,21 +352,11 @@ final case class HpModel( lastState.thermalGridState, ) - val ( - demandIndicator, - _, - ) = determineDemandBooleans( - lastState, - updatedThermalGridState, - thermalEnergyDemandHouse, - thermalEnergyDemandStorage, - ) - val updatedHpState = calcState( lastState, data, turnOn, - demandIndicator, + thermalDemandWrapper, ) ( 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 f20bebfc04..301ed651f3 100644 --- a/src/main/scala/edu/ie3/simona/model/thermal/ThermalGrid.scala +++ b/src/main/scala/edu/ie3/simona/model/thermal/ThermalGrid.scala @@ -16,7 +16,7 @@ import edu.ie3.datamodel.models.result.thermal.{ import edu.ie3.simona.exceptions.InvalidParameterException import edu.ie3.simona.exceptions.agent.InconsistentStateException import edu.ie3.simona.model.thermal.ThermalGrid.{ - ThermalDemandIndicator, + ThermalDemandWrapper, ThermalEnergyDemand, ThermalGridState, } @@ -65,7 +65,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) = @@ -136,13 +136,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), ) @@ -162,9 +164,8 @@ final case class ThermalGrid( * determines whether the heat pump is running or not * @param qDot * Thermal energy balance - * @param demandIndicator - * determines if the thermal units (house, storage) having some heat demand - * or not + * @param thermalDemands + * holds the thermal demands of the thermal units (house, storage) * @return * The updated state of the grid */ @@ -175,7 +176,7 @@ final case class ThermalGrid( ambientTemperature: Temperature, isRunning: Boolean, qDot: Power, - demandIndicator: ThermalDemandIndicator, + thermalDemands: ThermalDemandWrapper, ): (ThermalGridState, Option[ThermalThreshold]) = if (qDot > zeroKW) handleInfeed( tick, @@ -184,7 +185,7 @@ final case class ThermalGrid( state, isRunning, qDot, - demandIndicator, + thermalDemands, ) else handleConsumption( @@ -210,9 +211,8 @@ final case class ThermalGrid( * determines whether the heat pump is running or not * @param qDot * Infeed to the grid - * @param demandIndicator - * determines if the thermal units (house, storage) having some heat demand - * or not + * @param thermalDemands + * holds the thermal demands of the thermal units (house, storage) * @return * Updated thermal grid state */ @@ -223,7 +223,7 @@ final case class ThermalGrid( state: ThermalGridState, isRunning: Boolean, qDot: Power, - demandIndicator: ThermalDemandIndicator, + thermalDemands: ThermalDemandWrapper, ): (ThermalGridState, Option[ThermalThreshold]) = { // TODO: We would need to issue a storage result model here... @@ -241,7 +241,8 @@ final case class ThermalGrid( } if ( - (qDotHouseLastState > zeroKW && (qDotStorageLastState >= zeroKW)) | (qDotStorageLastState > zeroKW & demandIndicator.heatStorageDemand) + // todo: Check if it has to be hasRequiredDemand or hasAdditionalDemand + (qDotHouseLastState > zeroKW && (qDotStorageLastState >= zeroKW)) | (qDotStorageLastState > zeroKW & thermalDemands.heatStorageDemand.hasAdditionalDemand) ) { val (updatedHouseState, thermalHouseThreshold, remainingQDotHouse) = handleInfeedHouse( @@ -303,8 +304,11 @@ final case class ThermalGrid( ) } } else { - - (demandIndicator.houseDemand, demandIndicator.heatStorageDemand) match { + // todo: Check if it has to be hasRequiredDemand or hasAdditionalDemand + ( + thermalDemands.houseDemand.hasRequiredDemand, + thermalDemands.heatStorageDemand.hasRequiredDemand, + ) match { case (true, _) => // house first then heatStorage after heating House @@ -739,17 +743,16 @@ object ThermalGrid { thermalGrid.storage.map(_.startingState), ) - /** Wraps booleans indicating the demand of thermal units (thermal house, - * thermal storage). + /** Wraps the demand of thermal units (thermal house, thermal storage). * * @param houseDemand - * Boolean indicating the demand of the thermal house + * the demand of the thermal house * @param heatStorageDemand - * Boolean indicating the demand of the thermal heat storage + * the demand of the thermal heat storage */ - final case class ThermalDemandIndicator private ( - houseDemand: Boolean, - heatStorageDemand: Boolean, + final case class ThermalDemandWrapper private ( + houseDemand: ThermalEnergyDemand, + heatStorageDemand: ThermalEnergyDemand, ) /** Defines the thermal energy demand of a thermal grid. It comprises the 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 21f27b1892..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,8 +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 edu.ie3.simona.model.thermal.ThermalGrid.ThermalDemandIndicator -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 @@ -26,12 +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: ThermalDemandIndicator = - ThermalDemandIndicator(false, false) - protected val onlyThermalDemandOfHouse: ThermalDemandIndicator = - ThermalDemandIndicator(true, false) - protected val onlyThermalDemandOfHeatStorage: ThermalDemandIndicator = - ThermalDemandIndicator(false, true) + 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 2351774f20..cc28ff8505 100644 --- a/src/test/scala/edu/ie3/simona/model/thermal/ThermalGridWithHouseAndStorageSpec.scala +++ b/src/test/scala/edu/ie3/simona/model/thermal/ThermalGridWithHouseAndStorageSpec.scala @@ -13,13 +13,13 @@ 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, 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 +97,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 +123,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 +134,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)) @@ -143,436 +148,442 @@ class ThermalGridWithHouseAndStorageSpec ThermalStorageState(10800, zeroKWh, zeroKW) ) } - } - - "handling thermal energy consumption from grid" should { - val handleConsumption = - PrivateMethod[(ThermalGridState, Option[ThermalThreshold])]( - Symbol("handleConsumption") - ) - "return house threshold, if storage is in balance" in { - val tick = 0L - val initialGridState = ThermalGrid.startingState(thermalGrid) - val initialLoading = KilowattHours(430d) - val gridState = initialGridState.copy(storageState = - initialGridState.storageState.map(storageState => - storageState.copy(storedEnergy = initialLoading) + "handling thermal energy consumption from grid" should { + val handleConsumption = + PrivateMethod[(ThermalGridState, Option[ThermalThreshold])]( + Symbol("handleConsumption") ) - ) - val externalQDot = zeroKW - val (updatedGridState, reachedThreshold) = - thermalGrid invokePrivate handleConsumption( - tick, - testGridAmbientTemperature, - testGridAmbientTemperature, - gridState, - externalQDot, + "return house threshold, if storage is in balance" in { + val tick = 0L + val initialGridState = ThermalGrid.startingState(thermalGrid) + val initialLoading = KilowattHours(430d) + val gridState = initialGridState.copy(storageState = + initialGridState.storageState.map(storageState => + storageState.copy(storedEnergy = initialLoading) + ) ) + val externalQDot = zeroKW - updatedGridState match { - case ThermalGridState( - _, - Some( - ThermalStorageState(storageTick, storedEnergy, qDotStorage) - ), - ) => - storageTick shouldBe 0L - storedEnergy should approximate(initialLoading) - qDotStorage should approximate(externalQDot) - case _ => fail("Thermal grid state has been calculated wrong.") - } - reachedThreshold shouldBe Some( - HouseTemperatureLowerBoundaryReached(154285L) - ) - } + val (updatedGridState, reachedThreshold) = + thermalGrid invokePrivate handleConsumption( + tick, + testGridAmbientTemperature, + testGridAmbientTemperature, + gridState, + externalQDot, + ) - "take energy from storage, if there is actual consumption" in { - val tick = 0L - val initialGridState = ThermalGrid.startingState(thermalGrid) - val initialLoading = KilowattHours(200d) - val gridState = initialGridState.copy(storageState = - initialGridState.storageState.map(storageState => - storageState.copy(storedEnergy = initialLoading) + updatedGridState match { + case ThermalGridState( + _, + Some( + ThermalStorageState(storageTick, storedEnergy, qDotStorage) + ), + ) => + storageTick shouldBe 0L + storedEnergy should approximate(initialLoading) + qDotStorage should approximate(externalQDot) + case _ => fail("Thermal grid state has been calculated wrong.") + } + reachedThreshold shouldBe Some( + HouseTemperatureLowerBoundaryReached(154285L) ) - ) - val externalQDot = testGridQDotConsumption + } - val (updatedGridState, reachedThreshold) = - thermalGrid invokePrivate handleConsumption( - tick, - testGridAmbientTemperature, - testGridAmbientTemperature, - gridState, - externalQDot, + "take energy from storage, if there is actual consumption" in { + val tick = 0L + val initialGridState = ThermalGrid.startingState(thermalGrid) + val initialLoading = KilowattHours(200d) + val gridState = initialGridState.copy(storageState = + initialGridState.storageState.map(storageState => + storageState.copy(storedEnergy = initialLoading) + ) ) + val externalQDot = testGridQDotConsumption - updatedGridState match { - case ThermalGridState( - Some(ThermalHouseState(houseTick, innerTemperature, qDotHouse)), - Some( - ThermalStorageState(storageTick, storedEnergy, qDotStorage) - ), - ) => - houseTick shouldBe 0L - innerTemperature should approximate(Celsius(18.9999d)) - qDotHouse should approximate(zeroKW) - - storageTick shouldBe 0L - storedEnergy should approximate(initialLoading) - qDotStorage should approximate(externalQDot) - case _ => fail("Thermal grid state has been calculated wrong.") + val (updatedGridState, reachedThreshold) = + thermalGrid invokePrivate handleConsumption( + tick, + testGridAmbientTemperature, + testGridAmbientTemperature, + gridState, + externalQDot, + ) + + updatedGridState match { + case ThermalGridState( + Some( + ThermalHouseState(houseTick, innerTemperature, qDotHouse) + ), + Some( + ThermalStorageState(storageTick, storedEnergy, qDotStorage) + ), + ) => + houseTick shouldBe 0L + innerTemperature should approximate(Celsius(18.9999d)) + qDotHouse should approximate(zeroKW) + + storageTick shouldBe 0L + storedEnergy should approximate(initialLoading) + qDotStorage should approximate(externalQDot) + case _ => fail("Thermal grid state has been calculated wrong.") + } + reachedThreshold shouldBe Some(StorageEmpty(17143L)) } - reachedThreshold shouldBe Some(StorageEmpty(17143L)) } - } - "revising infeed from storage to house" should { - val zeroInflux = zeroKW - val tick = 3600L - val ambientTemperature = Celsius(14d) - "hand back unaltered information if needed information is missing" in { - val maybeHouseState = Some( - ( - ThermalHouseState( - tick, - Celsius( - thermalHouseInput.getTargetTemperature - .to(Units.CELSIUS) - .getValue - .doubleValue + "revising infeed from storage to house" should { + val zeroInflux = zeroKW + val tick = 3600L + val ambientTemperature = Celsius(14d) + "hand back unaltered information if needed information is missing" in { + val maybeHouseState = Some( + ( + ThermalHouseState( + tick, + Celsius( + thermalHouseInput.getTargetTemperature + .to(Units.CELSIUS) + .getValue + .doubleValue + ), + zeroInflux, ), - zeroInflux, - ), - None, + None, + ) ) - ) - val maybeStorageState = None - - thermalGrid.reviseInfeedFromStorage( - tick, - maybeHouseState, - maybeStorageState, - maybeHouseState.map(_._1), - None, - testGridAmbientTemperature, - testGridAmbientTemperature, - testGridQDotConsumption, - ) match { - case (maybeRevisedHouseState, maybeRevisedStorageState) => - maybeRevisedHouseState shouldBe maybeHouseState - maybeRevisedStorageState shouldBe maybeStorageState + val maybeStorageState = None + + thermalGrid.reviseInfeedFromStorage( + tick, + maybeHouseState, + maybeStorageState, + maybeHouseState.map(_._1), + None, + testGridAmbientTemperature, + testGridAmbientTemperature, + testGridQDotConsumption, + ) match { + case (maybeRevisedHouseState, maybeRevisedStorageState) => + maybeRevisedHouseState shouldBe maybeHouseState + maybeRevisedStorageState shouldBe maybeStorageState + } } - } - "hand back unaltered information if house temperature is above lower boundary temperature" in { - val maybeHouseState = Some( - ( - ThermalHouseState( - tick, - Celsius( - thermalHouseInput.getTargetTemperature - .to(Units.CELSIUS) - .getValue - .doubleValue + "hand back unaltered information if house temperature is above lower boundary temperature" in { + val maybeHouseState = Some( + ( + ThermalHouseState( + tick, + Celsius( + thermalHouseInput.getTargetTemperature + .to(Units.CELSIUS) + .getValue + .doubleValue + ), + zeroInflux, ), - zeroInflux, - ), - None, + None, + ) ) - ) - val maybeStorageState = Some( - ( - ThermalStorageState( - tick, - KilowattHours(50d), - zeroInflux, - ), - None, + val maybeStorageState = Some( + ( + ThermalStorageState( + tick, + KilowattHours(50d), + zeroInflux, + ), + None, + ) ) - ) - thermalGrid.reviseInfeedFromStorage( - tick, - maybeHouseState, - maybeStorageState, - maybeHouseState.map(_._1), - maybeStorageState.map(_._1), - ambientTemperature, - ambientTemperature, - zeroInflux, - ) match { - case (maybeRevisedHouseState, maybeRevisedStorageState) => - maybeRevisedHouseState shouldBe maybeHouseState - maybeRevisedStorageState shouldBe maybeStorageState + thermalGrid.reviseInfeedFromStorage( + tick, + maybeHouseState, + maybeStorageState, + maybeHouseState.map(_._1), + maybeStorageState.map(_._1), + ambientTemperature, + ambientTemperature, + zeroInflux, + ) match { + case (maybeRevisedHouseState, maybeRevisedStorageState) => + maybeRevisedHouseState shouldBe maybeHouseState + maybeRevisedStorageState shouldBe maybeStorageState + } } - } - "hand back unaltered information if house temperature is above lower boundary temperature, but has influx" in { - val maybeHouseState = Some( - ( - ThermalHouseState( - tick, - Celsius( - thermalHouseInput.getTargetTemperature - .to(Units.CELSIUS) - .getValue - .doubleValue + "hand back unaltered information if house temperature is above lower boundary temperature, but has influx" in { + val maybeHouseState = Some( + ( + ThermalHouseState( + tick, + Celsius( + thermalHouseInput.getTargetTemperature + .to(Units.CELSIUS) + .getValue + .doubleValue + ), + testGridQDotInfeed, ), - testGridQDotInfeed, - ), - Some(HouseTemperatureUpperBoundaryReached(3600L)), + Some(HouseTemperatureUpperBoundaryReached(3600L)), + ) ) - ) - val maybeStorageState = Some( - ( - ThermalStorageState( - tick, - KilowattHours(50d), - zeroInflux, - ), - None, + val maybeStorageState = Some( + ( + ThermalStorageState( + tick, + KilowattHours(50d), + zeroInflux, + ), + None, + ) ) - ) - thermalGrid.reviseInfeedFromStorage( - tick, - maybeHouseState, - maybeStorageState, - maybeHouseState.map(_._1), - maybeStorageState.map(_._1), - ambientTemperature, - ambientTemperature, - testGridQDotInfeed, - ) match { - case (maybeRevisedHouseState, maybeRevisedStorageState) => - maybeRevisedHouseState shouldBe maybeHouseState - maybeRevisedStorageState shouldBe maybeStorageState + thermalGrid.reviseInfeedFromStorage( + tick, + maybeHouseState, + maybeStorageState, + maybeHouseState.map(_._1), + maybeStorageState.map(_._1), + ambientTemperature, + ambientTemperature, + testGridQDotInfeed, + ) match { + case (maybeRevisedHouseState, maybeRevisedStorageState) => + maybeRevisedHouseState shouldBe maybeHouseState + maybeRevisedStorageState shouldBe maybeStorageState + } } - } - "hand back unaltered information if house temperature is at lower boundary temperature, but storage is empty" in { - val maybeHouseState = Some( - ( - ThermalHouseState( - tick, - Celsius( - thermalHouseInput.getLowerTemperatureLimit - .to(Units.CELSIUS) - .getValue - .doubleValue + "hand back unaltered information if house temperature is at lower boundary temperature, but storage is empty" in { + val maybeHouseState = Some( + ( + ThermalHouseState( + tick, + Celsius( + thermalHouseInput.getLowerTemperatureLimit + .to(Units.CELSIUS) + .getValue + .doubleValue + ), + zeroInflux, ), - zeroInflux, - ), - Some(HouseTemperatureLowerBoundaryReached(tick)), + Some(HouseTemperatureLowerBoundaryReached(tick)), + ) ) - ) - val maybeStorageState = Some( - ( - ThermalStorageState( - tick, - zeroKWh, - testGridQDotInfeed, - ), - Some(StorageEmpty(tick)), + val maybeStorageState = Some( + ( + ThermalStorageState( + tick, + zeroKWh, + testGridQDotInfeed, + ), + Some(StorageEmpty(tick)), + ) ) - ) - thermalGrid.reviseInfeedFromStorage( - tick, - maybeHouseState, - maybeStorageState, - maybeHouseState.map(_._1), - maybeStorageState.map(_._1), - ambientTemperature, - ambientTemperature, - zeroInflux, - ) match { - case (maybeRevisedHouseState, maybeRevisedStorageState) => - maybeRevisedHouseState shouldBe maybeHouseState - maybeRevisedStorageState shouldBe maybeStorageState + thermalGrid.reviseInfeedFromStorage( + tick, + maybeHouseState, + maybeStorageState, + maybeHouseState.map(_._1), + maybeStorageState.map(_._1), + ambientTemperature, + ambientTemperature, + zeroInflux, + ) match { + case (maybeRevisedHouseState, maybeRevisedStorageState) => + maybeRevisedHouseState shouldBe maybeHouseState + maybeRevisedStorageState shouldBe maybeStorageState + } } - } - "alter the given states as expected, when all conditions are met" in { - val maybeHouseState = Some( - ( + "alter the given states as expected, when all conditions are met" in { + val maybeHouseState = Some( + ( + ThermalHouseState( + tick, + Celsius( + thermalHouseInput.getLowerTemperatureLimit + .to(Units.CELSIUS) + .getValue + .doubleValue + ), + zeroInflux, + ), + Some(HouseTemperatureLowerBoundaryReached(tick)), + ) + ) + val maybeStorageState = Some( + ( + ThermalStorageState( + tick, + KilowattHours(20d), + testGridQDotInfeed, + ), + None, + ) + ) + val formerHouseState = Some( ThermalHouseState( - tick, + 0L, Celsius( - thermalHouseInput.getLowerTemperatureLimit + thermalHouseInput.getTargetTemperature .to(Units.CELSIUS) .getValue .doubleValue ), zeroInflux, - ), - Some(HouseTemperatureLowerBoundaryReached(tick)), + ) ) - ) - val maybeStorageState = Some( - ( + val formerStorageState = Some( ThermalStorageState( - tick, - KilowattHours(20d), - testGridQDotInfeed, - ), - None, - ) - ) - val formerHouseState = Some( - ThermalHouseState( - 0L, - Celsius( - thermalHouseInput.getTargetTemperature - .to(Units.CELSIUS) - .getValue - .doubleValue - ), - zeroInflux, - ) - ) - val formerStorageState = Some( - ThermalStorageState( - 0L, - KilowattHours(70d), - Kilowatts(-50d), + 0L, + KilowattHours(70d), + Kilowatts(-50d), + ) ) - ) - thermalGrid.reviseInfeedFromStorage( - tick, - maybeHouseState, - maybeStorageState, - formerHouseState, - formerStorageState, - ambientTemperature, - ambientTemperature, - zeroInflux, - ) match { - case ( - Some( - ( - ThermalHouseState(houseTick, _, revisedQDotHouse), - Some(HouseTemperatureUpperBoundaryReached(houseWarmTick)), - ) - ), - Some( - ( - ThermalStorageState(storageTick, _, revisedQDotStorage), - Some(StorageEmpty(storageEmptyTick)), - ) - ), - ) => - houseTick shouldBe tick - storageTick shouldBe tick - - revisedQDotHouse should approximate(thermalStorage.chargingPower) - revisedQDotStorage should approximate( - thermalStorage.chargingPower * (-1) - ) + thermalGrid.reviseInfeedFromStorage( + tick, + maybeHouseState, + maybeStorageState, + formerHouseState, + formerStorageState, + ambientTemperature, + ambientTemperature, + zeroInflux, + ) match { + case ( + Some( + ( + ThermalHouseState(houseTick, _, revisedQDotHouse), + Some(HouseTemperatureUpperBoundaryReached(houseWarmTick)), + ) + ), + Some( + ( + ThermalStorageState(storageTick, _, revisedQDotStorage), + Some(StorageEmpty(storageEmptyTick)), + ) + ), + ) => + houseTick shouldBe tick + storageTick shouldBe tick + + revisedQDotHouse should approximate(thermalStorage.chargingPower) + revisedQDotStorage should approximate( + thermalStorage.chargingPower * (-1) + ) - houseWarmTick shouldBe 3695L - storageEmptyTick shouldBe 3663L - case _ => fail("Revision of states failed") + houseWarmTick shouldBe 3695L + storageEmptyTick shouldBe 3663L + case _ => fail("Revision of states failed") + } } } - } - - "handling thermal infeed into the grid" should { - val handleInfeed = - PrivateMethod[(ThermalGridState, Option[ThermalThreshold])]( - Symbol("handleInfeed") - ) - - "heat the house, if the upper temperature in the house is not reached" in { - val tick = 0L - val initialGridState = ThermalGrid.startingState(thermalGrid) - val externalQDot = testGridQDotInfeed - val (updatedGridState, reachedThreshold) = - thermalGrid invokePrivate handleInfeed( - tick, - testGridAmbientTemperature, - testGridAmbientTemperature, - initialGridState, - isNotRunning, - externalQDot, - onlyThermalDemandOfHouse, + "handling thermal infeed into the grid" should { + val handleInfeed = + PrivateMethod[(ThermalGridState, Option[ThermalThreshold])]( + Symbol("handleInfeed") ) - updatedGridState match { - case ThermalGridState( - Some(ThermalHouseState(houseTick, innerTemperature, qDotHouse)), - Some( - ThermalStorageState(storageTick, storedEnergy, qDotStorage) - ), - ) => - houseTick shouldBe 0L - innerTemperature should approximate(Celsius(18.9999d)) - qDotHouse should approximate(externalQDot) + "heat the house, if the upper temperature in the house is not reached" in { + val tick = 0L + val initialGridState = ThermalGrid.startingState(thermalGrid) + val externalQDot = testGridQDotInfeed - storageTick shouldBe 0L - storedEnergy should approximate( - initialGridState.storageState - .map(_.storedEnergy) - .getOrElse(fail("No initial storage state found")) + val (updatedGridState, reachedThreshold) = + thermalGrid invokePrivate handleInfeed( + tick, + testGridAmbientTemperature, + testGridAmbientTemperature, + initialGridState, + isNotRunning, + externalQDot, + onlyThermalDemandOfHouse, ) - qDotStorage should approximate(zeroKW) + updatedGridState match { + case ThermalGridState( + Some( + ThermalHouseState(houseTick, innerTemperature, qDotHouse) + ), + Some( + ThermalStorageState(storageTick, storedEnergy, qDotStorage) + ), + ) => + houseTick shouldBe 0L + innerTemperature should approximate(Celsius(18.9999d)) + qDotHouse should approximate(externalQDot) + + storageTick shouldBe 0L + storedEnergy should approximate( + initialGridState.storageState + .map(_.storedEnergy) + .getOrElse(fail("No initial storage state found")) + ) - case _ => fail("Thermal grid state has been calculated wrong.") - } - reachedThreshold shouldBe Some( - HouseTemperatureUpperBoundaryReached(7372L) - ) - } + qDotStorage should approximate(zeroKW) - "load the storage, if the upper temperature in the house is reached" in { - val tick = 0L - val initialGridState = ThermalGrid.startingState(thermalGrid) - val gridState = initialGridState.copy(houseState = - initialGridState.houseState.map( - _.copy(innerTemperature = thermalHouse.upperBoundaryTemperature) + case _ => fail("Thermal grid state has been calculated wrong.") + } + reachedThreshold shouldBe Some( + HouseTemperatureUpperBoundaryReached(7372L) ) - ) - val externalQDot = testGridQDotInfeed + } - val (updatedGridState, reachedThreshold) = - thermalGrid invokePrivate handleInfeed( - tick, - testGridAmbientTemperature, - testGridAmbientTemperature, - gridState, - isNotRunning, - externalQDot, - onlyThermalDemandOfHeatStorage, + "load the storage, if the upper temperature in the house is reached" in { + val tick = 0L + val initialGridState = ThermalGrid.startingState(thermalGrid) + val gridState = initialGridState.copy(houseState = + initialGridState.houseState.map( + _.copy(innerTemperature = thermalHouse.upperBoundaryTemperature) + ) ) + val externalQDot = testGridQDotInfeed - updatedGridState match { - case ThermalGridState( - Some(ThermalHouseState(houseTick, innerTemperature, qDotHouse)), - Some( - ThermalStorageState(storageTick, storedEnergy, qDotStorage) - ), - ) => - houseTick shouldBe 0L - innerTemperature should approximate(Celsius(20.99999167d)) - qDotHouse should approximate(zeroKW) - - storageTick shouldBe 0L - storedEnergy should approximate( - gridState.storageState - .map(_.storedEnergy) - .getOrElse(fail("No initial storage state found")) + val (updatedGridState, reachedThreshold) = + thermalGrid invokePrivate handleInfeed( + tick, + testGridAmbientTemperature, + testGridAmbientTemperature, + gridState, + isNotRunning, + externalQDot, + onlyThermalDemandOfHeatStorage, ) - qDotStorage should approximate(externalQDot) - case _ => fail("Thermal grid state has been calculated wrong.") + + updatedGridState match { + case ThermalGridState( + Some( + ThermalHouseState(houseTick, innerTemperature, qDotHouse) + ), + Some( + ThermalStorageState(storageTick, storedEnergy, qDotStorage) + ), + ) => + houseTick shouldBe 0L + innerTemperature should approximate(Celsius(20.99999167d)) + qDotHouse should approximate(zeroKW) + + storageTick shouldBe 0L + storedEnergy should approximate( + gridState.storageState + .map(_.storedEnergy) + .getOrElse(fail("No initial storage state found")) + ) + qDotStorage should approximate(externalQDot) + case _ => fail("Thermal grid state has been calculated wrong.") + } + reachedThreshold shouldBe Some( + StorageFull(276000L) + ) } - reachedThreshold shouldBe Some( - StorageFull(276000L) - ) } } } 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 0c61b79ba8..34b37c2e03 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 d8b622e78c..c4e5339a54 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 ec0c36069be58383981fb164cf57435a777ec687 Mon Sep 17 00:00:00 2001 From: danielfeismann Date: Fri, 22 Nov 2024 10:01:01 +0100 Subject: [PATCH 38/65] Fix implausible test cases of HpModelSpec --- CHANGELOG.md | 1 + .../model/participant/HpModelSpec.scala | 56 +++++++++---------- 2 files changed, 29 insertions(+), 28 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4c42a0b055..1bd8545b67 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -102,6 +102,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Refactoring to use zeroKW and zeroKWH in thermal grid unit tests [#1023](https://github.com/ie3-institute/simona/issues/1023) - Refactor `ResultFileHierarchy` [#1031](https://github.com/ie3-institute/simona/issues/1031) - Removing logs in `logs/simona` [#1017](https://github.com/ie3-institute/simona/issues/1017) +- Fix implausible test cases of HpModelSpec [#1042](https://github.com/ie3-institute/simona/issues/1042) ### Fixed - Fix rendering of references in documentation [#505](https://github.com/ie3-institute/simona/issues/505) diff --git a/src/test/scala/edu/ie3/simona/model/participant/HpModelSpec.scala b/src/test/scala/edu/ie3/simona/model/participant/HpModelSpec.scala index 81c62e549d..0479798610 100644 --- a/src/test/scala/edu/ie3/simona/model/participant/HpModelSpec.scala +++ b/src/test/scala/edu/ie3/simona/model/participant/HpModelSpec.scala @@ -55,7 +55,7 @@ class HpModelSpec true, 95, 15.6, - Some(HouseTemperatureUpperBoundaryReached(31711L)), + Some(HouseTemperatureUpperBoundaryReached(31711)), ), ( HpState( @@ -70,7 +70,7 @@ class HpModelSpec true, 95, 16.4, - Some(HouseTemperatureUpperBoundaryReached(30642L)), + Some(HouseTemperatureUpperBoundaryReached(30642)), ), ( HpState( @@ -85,7 +85,7 @@ class HpModelSpec true, 95, 18.0, - Some(HouseTemperatureUpperBoundaryReached(27771L)), + Some(HouseTemperatureUpperBoundaryReached(27771)), ), ( HpState( @@ -100,7 +100,7 @@ class HpModelSpec false, 0, 19.6, - Some(HouseTemperatureLowerBoundaryReached(13200L)), + Some(HouseTemperatureLowerBoundaryReached(13200)), ), ( HpState( @@ -115,7 +115,7 @@ class HpModelSpec false, 0, 20.4, - Some(HouseTemperatureLowerBoundaryReached(15508L)), + Some(HouseTemperatureLowerBoundaryReached(15508)), ), ( HpState( @@ -124,13 +124,13 @@ class HpModelSpec Some(hpData.ambientTemperature), Kilowatts(95d), Kilowatts(80d), - thermalState(Celsius(17)), + thermalState(Celsius(17), Kilowatts(80d)), None, ), - true, - 95, - 15.6, - Some(HouseTemperatureUpperBoundaryReached(31711L)), + false, + 0, + 31.6, + Some(HouseTemperatureLowerBoundaryReached(29867)), ), ( HpState( @@ -139,13 +139,13 @@ class HpModelSpec Some(hpData.ambientTemperature), Kilowatts(95d), Kilowatts(80d), - thermalState(Celsius(18)), + thermalState(Celsius(18), Kilowatts(80d)), None, ), - true, - 95, - 16.4, - Some(HouseTemperatureUpperBoundaryReached(30642L)), + false, + 0, + 32.4, + Some(HouseTemperatureLowerBoundaryReached(30343)), ), ( HpState( @@ -154,13 +154,13 @@ class HpModelSpec Some(hpData.ambientTemperature), Kilowatts(95d), Kilowatts(80d), - thermalState(Celsius(20)), + thermalState(Celsius(20), Kilowatts(80d)), None, ), - true, - 95, - 18.0, - Some(HouseTemperatureUpperBoundaryReached(27771L)), + false, + 0, + 34.0, + Some(HouseTemperatureLowerBoundaryReached(31200)), ), ( HpState( @@ -169,13 +169,13 @@ class HpModelSpec Some(hpData.ambientTemperature), Kilowatts(95d), Kilowatts(80d), - thermalState(Celsius(22)), + thermalState(Celsius(22), Kilowatts(80d)), None, ), - true, - 95, - 19.6, - Some(HouseTemperatureUpperBoundaryReached(23200L)), + false, + 0, + 35.6, + Some(HouseTemperatureLowerBoundaryReached(31950)), ), ( HpState( @@ -184,13 +184,13 @@ class HpModelSpec Some(hpData.ambientTemperature), Kilowatts(95d), Kilowatts(80d), - thermalState(Celsius(25)), + thermalState(Celsius(25), Kilowatts(80d)), None, ), false, 0, - 22.0, - Some(HouseTemperatureLowerBoundaryReached(19200L)), + 38.0, + Some(HouseTemperatureLowerBoundaryReached(32914)), ), ) From be2452a73c5cb25e427dccddaf2c8529cfaa7d5b Mon Sep 17 00:00:00 2001 From: danielfeismann Date: Fri, 22 Nov 2024 10:25:53 +0100 Subject: [PATCH 39/65] fix turnHpOn determination --- src/main/scala/edu/ie3/simona/model/participant/HpModel.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 447e1c09d4..6cd616a160 100644 --- a/src/main/scala/edu/ie3/simona/model/participant/HpModel.scala +++ b/src/main/scala/edu/ie3/simona/model/participant/HpModel.scala @@ -192,7 +192,7 @@ final case class HpModel( val turnHpOn = demandHouse.hasRequiredDemand || - (demandHouse.hasAdditionalDemand && lastState.isRunning) + (demandHouse.hasAdditionalDemand && lastState.isRunning) || demandThermalStorage.hasRequiredDemand || (demandThermalStorage.hasAdditionalDemand && lastState.isRunning) val canOperate = From 1e25d3b88944589497b7935a7bd9e21b9edb0da8 Mon Sep 17 00:00:00 2001 From: danielfeismann Date: Fri, 22 Nov 2024 10:34:11 +0100 Subject: [PATCH 40/65] fix comment in HpModelSpec --- .../scala/edu/ie3/simona/model/participant/HpModelSpec.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/scala/edu/ie3/simona/model/participant/HpModelSpec.scala b/src/test/scala/edu/ie3/simona/model/participant/HpModelSpec.scala index 0479798610..9964f7af3f 100644 --- a/src/test/scala/edu/ie3/simona/model/participant/HpModelSpec.scala +++ b/src/test/scala/edu/ie3/simona/model/participant/HpModelSpec.scala @@ -265,7 +265,7 @@ class HpModelSpec (95.0, 95.0, 95.0), ), // 2. Same as before but heat storage is NOT empty - // should be possible to keep hp off + // should be possible to turn hp on ( ThermalGridState( Some(ThermalHouseState(0L, Celsius(15), Kilowatts(0))), From b4044496f61b1b6086d6e4c9f0d4c7e5a5dda69c Mon Sep 17 00:00:00 2001 From: Sebastian Peter Date: Fri, 22 Nov 2024 12:13:14 +0100 Subject: [PATCH 41/65] Unlimited fetch depth Signed-off-by: Sebastian Peter --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 182819f0a1..e09485924c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -28,7 +28,7 @@ jobs: - name: Checkout Source uses: actions/checkout@v4 with: - fetch-depth: 1 + fetch-depth: 0 ref: ${{ github.event.pull_request.head.ref || github.ref }} repository: ${{ github.event.pull_request.head.repo.full_name || github.repository }} From a728fa211a8f80d0edf52a3cc7012438693e73b3 Mon Sep 17 00:00:00 2001 From: Sebastian Peter Date: Fri, 22 Nov 2024 12:13:37 +0100 Subject: [PATCH 42/65] Test: removing ref and repository Signed-off-by: Sebastian Peter --- .github/workflows/ci.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e09485924c..6d58167b6c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -29,8 +29,6 @@ jobs: uses: actions/checkout@v4 with: fetch-depth: 0 - ref: ${{ github.event.pull_request.head.ref || github.ref }} - repository: ${{ github.event.pull_request.head.repo.full_name || github.repository }} - name: Setup Java uses: actions/setup-java@v4 From 823d5e0b0d65b28355e821a8f640f5dfdb338669 Mon Sep 17 00:00:00 2001 From: Sebastian Peter Date: Fri, 22 Nov 2024 12:30:56 +0100 Subject: [PATCH 43/65] Adding to changelog Signed-off-by: Sebastian Peter --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4c42a0b055..c5c51c6c00 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -140,6 +140,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Fix grammar and spelling in docs and comments [#1022](https://github.com/ie3-institute/simona/issues/1022) - Fix some minor issues and findings from inspections [#1019](https://github.com/ie3-institute/simona/issues/1019) - Fix initialisation freezing on empty primary data [#981](https://github.com/ie3-institute/simona/issues/981) +- Shallow fetch in CI [#1041](https://github.com/ie3-institute/simona/issues/1041) ## [3.0.0] - 2023-08-07 From 135ea159de5f5b99c7af701a379a0ccd3d988e81 Mon Sep 17 00:00:00 2001 From: danielfeismann Date: Fri, 22 Nov 2024 15:42:02 +0100 Subject: [PATCH 44/65] fix turnHpOn in HpModel --- src/main/scala/edu/ie3/simona/model/participant/HpModel.scala | 3 +-- 1 file changed, 1 insertion(+), 2 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 6cd616a160..04edf965c8 100644 --- a/src/main/scala/edu/ie3/simona/model/participant/HpModel.scala +++ b/src/main/scala/edu/ie3/simona/model/participant/HpModel.scala @@ -191,8 +191,7 @@ final case class HpModel( ) val turnHpOn = - demandHouse.hasRequiredDemand || - (demandHouse.hasAdditionalDemand && lastState.isRunning) || + (demandHouse.hasRequiredDemand && noThermalStorageOrThermalStorageIsEmpty) || demandThermalStorage.hasRequiredDemand || (demandThermalStorage.hasAdditionalDemand && lastState.isRunning) val canOperate = From debae480c15738ce24e34f4f5d72c82e0c96742c Mon Sep 17 00:00:00 2001 From: danielfeismann Date: Fri, 22 Nov 2024 17:01:34 +0100 Subject: [PATCH 45/65] refactoring thermal demand indicators --- .../simona/model/participant/HpModel.scala | 88 ++++++------------- .../simona/model/thermal/ThermalGrid.scala | 62 ++++++------- .../model/participant/HpModelSpec.scala | 2 +- .../model/thermal/ThermalGridTestData.scala | 29 ++++-- .../ThermalGridWithHouseAndStorageSpec.scala | 11 ++- .../ThermalGridWithHouseOnlySpec.scala | 7 +- .../ThermalGridWithStorageOnlySpec.scala | 8 +- 7 files changed, 100 insertions(+), 107 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 3400fa15c8..76e9f24651 100644 --- a/src/main/scala/edu/ie3/simona/model/participant/HpModel.scala +++ b/src/main/scala/edu/ie3/simona/model/participant/HpModel.scala @@ -12,8 +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.{ - ThermalDemandIndicator, - ThermalEnergyDemand, + ThermalDemandWrapper, ThermalGridState, } import edu.ie3.simona.model.thermal.{ThermalGrid, ThermalThreshold} @@ -133,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( @@ -144,18 +143,17 @@ final case class HpModel( ) // Determining the operation point and limitations at this tick - val (turnOn, canOperate, canBeOutOfOperation, demandIndicator) = + val (turnOn, canOperate, canBeOutOfOperation) = operatesInNextState( lastHpState, currentThermalGridState, relevantData, - demandHouse, - demandThermalStorage, + thermalDemandWrapper, ) // Updating the HpState val updatedState = - calcState(lastHpState, relevantData, turnOn, demandIndicator) + calcState(lastHpState, relevantData, turnOn, thermalDemandWrapper) (canOperate, canBeOutOfOperation, updatedState) } @@ -171,35 +169,30 @@ 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 plus the - * [[ThermalDemandIndicator]] of the thermal units + * operation and can be out of operation */ private def operatesInNextState( lastState: HpState, currentThermalGridState: ThermalGridState, relevantData: HpRelevantData, - demandHouse: ThermalEnergyDemand, - demandThermalStorage: ThermalEnergyDemand, - ): (Boolean, Boolean, Boolean, ThermalDemandIndicator) = { + thermalDemands: ThermalDemandWrapper, + ): (Boolean, Boolean, Boolean) = { - val ( - demandIndicator, - noThermalStorageOrThermalStorageIsEmpty, - ) = determineDemandBooleans( + val demandHouse = thermalDemands.houseDemand + val demandThermalStorage = thermalDemands.heatStorageDemand + + val noThermalStorageOrThermalStorageIsEmpty = determineThermalStorageStatus( lastState, currentThermalGridState, - demandHouse, - demandThermalStorage, ) val turnHpOn = - demandIndicator.houseDemand || demandIndicator.heatStorageDemand + (demandHouse.hasRequiredDemand && noThermalStorageOrThermalStorageIsEmpty) || + demandThermalStorage.hasRequiredDemand || (demandThermalStorage.hasAdditionalDemand && lastState.isRunning) val canOperate = demandHouse.hasRequiredDemand || demandHouse.hasAdditionalDemand || @@ -211,7 +204,6 @@ final case class HpModel( turnHpOn, canOperate, canBeOutOfOperation, - demandIndicator, ) } @@ -223,22 +215,14 @@ final case class HpModel( * 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( + private def determineThermalStorageStatus( lastHpState: HpState, updatedGridState: ThermalGridState, - demandHouse: ThermalEnergyDemand, - demandThermalStorage: ThermalEnergyDemand, - ): (ThermalDemandIndicator, Boolean) = { + ): Boolean = { implicit val tolerance: Energy = KilowattHours(1e-3) val noThermalStorageOrThermalStorageIsEmpty: Boolean = updatedGridState.storageState.isEmpty || updatedGridState.storageState @@ -246,13 +230,7 @@ final case class HpModel( _.storedEnergy =~ zeroKWh ) - val houseDemand = - (demandHouse.hasRequiredDemand && noThermalStorageOrThermalStorageIsEmpty) || (lastHpState.isRunning && demandHouse.hasAdditionalDemand) - val heatStorageDemand = - demandThermalStorage.hasRequiredDemand || (lastHpState.isRunning && demandThermalStorage.hasAdditionalDemand) - - val demandIndicator = ThermalDemandIndicator(houseDemand, heatStorageDemand) - (demandIndicator, noThermalStorageOrThermalStorageIsEmpty) + noThermalStorageOrThermalStorageIsEmpty } /** Calculate state depending on whether heat pump is needed or not. Also @@ -265,9 +243,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 demandIndicator - * determines if the thermal units (house, storage) having some heat demand - * or not + * @param demandWrapper + * holds the thermal demands of the thermal units (house, storage) * @return * next [[HpState]] */ @@ -275,7 +252,7 @@ final case class HpModel( lastState: HpState, relevantData: HpRelevantData, isRunning: Boolean, - demandIndicator: ThermalDemandIndicator, + demandWrapper: ThermalDemandWrapper, ): HpState = { val lastStateStorageQDot = lastState.thermalGridState.storageState .map(_.qDot) @@ -297,7 +274,7 @@ final case class HpModel( relevantData.ambientTemperature, isRunning, newThermalPower, - demandIndicator, + demandWrapper, ) HpState( @@ -364,9 +341,8 @@ final case class HpModel( val turnOn = setPower > (sRated.toActivePower(cosPhiRated) * 0.5) val ( - thermalEnergyDemandHouse, - thermalEnergyDemandStorage, - updatedThermalGridState, + thermalDemandWrapper, + _, ) = thermalGrid.energyDemandAndUpdatedState( data.currentTick, @@ -375,21 +351,11 @@ final case class HpModel( lastState.thermalGridState, ) - val ( - demandIndicator, - _, - ) = determineDemandBooleans( - lastState, - updatedThermalGridState, - thermalEnergyDemandHouse, - thermalEnergyDemandStorage, - ) - val updatedHpState = calcState( lastState, data, turnOn, - demandIndicator, + thermalDemandWrapper, ) ( 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 033f84e532..4e7b99ddc1 100644 --- a/src/main/scala/edu/ie3/simona/model/thermal/ThermalGrid.scala +++ b/src/main/scala/edu/ie3/simona/model/thermal/ThermalGrid.scala @@ -15,7 +15,7 @@ import edu.ie3.datamodel.models.result.thermal.{ } import edu.ie3.simona.exceptions.agent.InconsistentStateException import edu.ie3.simona.model.thermal.ThermalGrid.{ - ThermalDemandIndicator, + ThermalDemandWrapper, ThermalEnergyDemand, ThermalGridState, } @@ -64,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) = @@ -135,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), ) @@ -161,9 +163,8 @@ final case class ThermalGrid( * determines whether the heat pump is running or not * @param qDot * Thermal energy balance - * @param demandIndicator - * determines if the thermal units (house, storage) having some heat demand - * or not + * @param thermalDemands + * holds the thermal demands of the thermal units (house, storage) * @return * The updated state of the grid */ @@ -174,7 +175,7 @@ final case class ThermalGrid( ambientTemperature: Temperature, isRunning: Boolean, qDot: Power, - demandIndicator: ThermalDemandIndicator, + thermalDemands: ThermalDemandWrapper, ): (ThermalGridState, Option[ThermalThreshold]) = if (qDot > zeroKW) handleInfeed( tick, @@ -183,7 +184,7 @@ final case class ThermalGrid( state, isRunning, qDot, - demandIndicator, + thermalDemands, ) else handleConsumption( @@ -209,9 +210,8 @@ final case class ThermalGrid( * determines whether the heat pump is running or not * @param qDot * Infeed to the grid - * @param demandIndicator - * determines if the thermal units (house, storage) having some heat demand - * or not + * @param thermalDemands + * holds the thermal demands of the thermal units (house, storage) * @return * Updated thermal grid state */ @@ -222,7 +222,7 @@ final case class ThermalGrid( state: ThermalGridState, isRunning: Boolean, qDot: Power, - demandIndicator: ThermalDemandIndicator, + thermalDemands: ThermalDemandWrapper, ): (ThermalGridState, Option[ThermalThreshold]) = { // TODO: We would need to issue a storage result model here... @@ -240,7 +240,8 @@ final case class ThermalGrid( } if ( - (qDotHouseLastState > zeroKW && (qDotStorageLastState >= zeroKW)) | (qDotStorageLastState > zeroKW & demandIndicator.heatStorageDemand) + // todo: Check if it has to be hasRequiredDemand or hasAdditionalDemand + (qDotHouseLastState > zeroKW && (qDotStorageLastState >= zeroKW)) | (qDotStorageLastState > zeroKW & thermalDemands.heatStorageDemand.hasAdditionalDemand) ) { val (updatedHouseState, thermalHouseThreshold, remainingQDotHouse) = handleInfeedHouse( @@ -302,8 +303,11 @@ final case class ThermalGrid( ) } } else { - - (demandIndicator.houseDemand, demandIndicator.heatStorageDemand) match { + // todo: Check if it has to be hasRequiredDemand or hasAdditionalDemand + ( + thermalDemands.houseDemand.hasRequiredDemand, + thermalDemands.heatStorageDemand.hasRequiredDemand, + ) match { case (true, _) => // house first then heatStorage after heating House @@ -737,17 +741,16 @@ object ThermalGrid { thermalGrid.storage.map(_.startingState), ) - /** Wraps booleans indicating the demand of thermal units (thermal house, - * thermal storage). + /** Wraps the demand of thermal units (thermal house, thermal storage). * * @param houseDemand - * Boolean indicating the demand of the thermal house + * the demand of the thermal house * @param heatStorageDemand - * Boolean indicating the demand of the thermal heat storage + * the demand of the thermal heat storage */ - final case class ThermalDemandIndicator private ( - houseDemand: Boolean, - heatStorageDemand: Boolean, + final case class ThermalDemandWrapper private ( + houseDemand: ThermalEnergyDemand, + heatStorageDemand: ThermalEnergyDemand, ) /** Defines the thermal energy demand of a thermal grid. It comprises the @@ -776,8 +779,7 @@ object ThermalGrid { object ThermalEnergyDemand { /** Builds a new instance of [[ThermalEnergyDemand]]. If the possible energy - * is less than the required energy, this is considered to be a bad state - * and the required energy is curtailed to the possible energy. + * is less than the required energy, this is considered to be a bad state. * @param required * The absolutely required energy to reach target state * @param possible diff --git a/src/test/scala/edu/ie3/simona/model/participant/HpModelSpec.scala b/src/test/scala/edu/ie3/simona/model/participant/HpModelSpec.scala index 81c62e549d..7080cac327 100644 --- a/src/test/scala/edu/ie3/simona/model/participant/HpModelSpec.scala +++ b/src/test/scala/edu/ie3/simona/model/participant/HpModelSpec.scala @@ -265,7 +265,7 @@ class HpModelSpec (95.0, 95.0, 95.0), ), // 2. Same as before but heat storage is NOT empty - // should be possible to keep hp off + // should be possible to turn hp on ( ThermalGridState( Some(ThermalHouseState(0L, Celsius(15), Kilowatts(0))), 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 21f27b1892..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,8 +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 edu.ie3.simona.model.thermal.ThermalGrid.ThermalDemandIndicator -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 @@ -26,12 +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: ThermalDemandIndicator = - ThermalDemandIndicator(false, false) - protected val onlyThermalDemandOfHouse: ThermalDemandIndicator = - ThermalDemandIndicator(true, false) - protected val onlyThermalDemandOfHeatStorage: ThermalDemandIndicator = - ThermalDemandIndicator(false, true) + 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 2351774f20..6cd7558e3d 100644 --- a/src/test/scala/edu/ie3/simona/model/thermal/ThermalGridWithHouseAndStorageSpec.scala +++ b/src/test/scala/edu/ie3/simona/model/thermal/ThermalGridWithHouseAndStorageSpec.scala @@ -13,13 +13,13 @@ 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, 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 +97,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 +123,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 +134,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 0c61b79ba8..34b37c2e03 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 d8b622e78c..c4e5339a54 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 5c00a896c5ca9e086fa82e5384073e96a519482f Mon Sep 17 00:00:00 2001 From: danielfeismann Date: Fri, 22 Nov 2024 17:02:32 +0100 Subject: [PATCH 46/65] fix thermalPower determination in calcState of HpModel --- .../edu/ie3/simona/model/participant/HpModel.scala | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) 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 76e9f24651..3e5c8f4cd1 100644 --- a/src/main/scala/edu/ie3/simona/model/participant/HpModel.scala +++ b/src/main/scala/edu/ie3/simona/model/participant/HpModel.scala @@ -258,12 +258,20 @@ final case class HpModel( .map(_.qDot) .getOrElse(zeroKW) - val (newActivePower, newThermalPower) = + val (newActivePower, newThermalPower) = { if (isRunning) (pRated, pThermal) else if (lastStateStorageQDot < zeroKW) (zeroKW, lastStateStorageQDot * -1) + else if ( + lastStateStorageQDot == zeroKW && (demandWrapper.houseDemand.hasRequiredDemand || demandWrapper.heatStorageDemand.hasRequiredDemand) + ) + ( + zeroKW, + thermalGrid.storage.map(_.getChargingPower: squants.Power).get * -1, + ) else (zeroKW, zeroKW) + } /* Push thermal energy to the thermal grid and get its updated state in return */ val (thermalGridState, maybeThreshold) = From c44bcaef8f43ff11c8efc91322b32ace61a2fc7c Mon Sep 17 00:00:00 2001 From: Sebastian Peter Date: Fri, 22 Nov 2024 18:15:56 +0100 Subject: [PATCH 47/65] Correcting wrong use of "wall clock time" Signed-off-by: Sebastian Peter --- CHANGELOG.md | 1 + .../agent/grid/GridAgentController.scala | 24 +++++++++---------- .../agent/grid/GridResultsSupport.scala | 6 ++--- .../ParticipantAgentFundamentals.scala | 4 ++-- .../participant/hp/HpAgentFundamentals.scala | 4 ++-- .../participant/statedata/BaseStateData.scala | 6 ++--- .../service/primary/PrimaryServiceProxy.scala | 8 +++---- .../primary/PrimaryServiceWorker.scala | 12 +++++----- .../service/weather/SampleWeatherSource.scala | 10 ++++---- .../participant/ParticipantAgentMock.scala | 4 ++-- 10 files changed, 40 insertions(+), 39 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 76a0624832..701bb86ea4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -142,6 +142,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Fix some minor issues and findings from inspections [#1019](https://github.com/ie3-institute/simona/issues/1019) - Fix initialisation freezing on empty primary data [#981](https://github.com/ie3-institute/simona/issues/981) - Shallow fetch in CI [#1041](https://github.com/ie3-institute/simona/issues/1041) +- Correct wrong use of term "wall clock time" [#727](https://github.com/ie3-institute/simona/issues/727) ## [3.0.0] - 2023-08-07 diff --git a/src/main/scala/edu/ie3/simona/agent/grid/GridAgentController.scala b/src/main/scala/edu/ie3/simona/agent/grid/GridAgentController.scala index ad0d6c3b07..8d615ec3af 100644 --- a/src/main/scala/edu/ie3/simona/agent/grid/GridAgentController.scala +++ b/src/main/scala/edu/ie3/simona/agent/grid/GridAgentController.scala @@ -455,9 +455,9 @@ class GridAgentController( * @param primaryServiceProxy * Reference to the primary data service proxy * @param simulationStartDate - * First wall clock time in simulation + * The simulation time at which the simulation starts * @param simulationEndDate - * Last wall clock time in simulation + * The simulation time at which the simulation ends * @param resolution * Frequency of power flow calculations * @param requestVoltageDeviationThreshold @@ -512,9 +512,9 @@ class GridAgentController( * @param primaryServiceProxy * Reference to the primary data service proxy * @param simulationStartDate - * First wall clock time in simulation + * The simulation time at which the simulation starts * @param simulationEndDate - * Last wall clock time in simulation + * The simulation time at which the simulation ends * @param resolution * Frequency of power flow calculations * @param requestVoltageDeviationThreshold @@ -571,9 +571,9 @@ class GridAgentController( * @param weatherService * Reference to the weather service actor * @param simulationStartDate - * First wall clock time in simulation + * The simulation time at which the simulation starts * @param simulationEndDate - * Last wall clock time in simulation + * The simulation time at which the simulation ends * @param resolution * Frequency of power flow calculations * @param requestVoltageDeviationThreshold @@ -631,9 +631,9 @@ class GridAgentController( * @param evMovementsService * Reference to the ev movements service actor * @param simulationStartDate - * First wall clock time in simulation + * The simulation time at which the simulation starts * @param simulationEndDate - * Last wall clock time in simulation + * The simulation time at which the simulation ends * @param resolution * Frequency of power flow calculations * @param requestVoltageDeviationThreshold @@ -749,9 +749,9 @@ class GridAgentController( * @param weatherService * Reference to the weather service actor * @param simulationStartDate - * First wall clock time in simulation + * The simulation time at which the simulation starts * @param simulationEndDate - * Last wall clock time in simulation + * The simulation time at which the simulation ends * @param resolution * Frequency of power flow calculations * @param requestVoltageDeviationThreshold @@ -807,9 +807,9 @@ class GridAgentController( * @param primaryServiceProxy * Reference to the primary data service proxy * @param simulationStartDate - * First wall clock time in simulation + * The simulation time at which the simulation starts * @param simulationEndDate - * Last wall clock time in simulation + * The simulation time at which the simulation ends * @param resolution * Frequency of power flow calculations * @param requestVoltageDeviationThreshold diff --git a/src/main/scala/edu/ie3/simona/agent/grid/GridResultsSupport.scala b/src/main/scala/edu/ie3/simona/agent/grid/GridResultsSupport.scala index 59f0248f81..6b8b6520ff 100644 --- a/src/main/scala/edu/ie3/simona/agent/grid/GridResultsSupport.scala +++ b/src/main/scala/edu/ie3/simona/agent/grid/GridResultsSupport.scala @@ -617,7 +617,7 @@ object GridResultsSupport { /** Partial result for the port at the high voltage side * * @param time - * Wall clock time, the result does belong to + * Simulation time of the result * @param input * Unique identifier of the input model * @param currentMagnitude @@ -638,7 +638,7 @@ object GridResultsSupport { /** Partial result for the port at the medium voltage side * * @param time - * Wall clock time, the result does belong to + * Simulation time of the result * @param input * Unique identifier of the input model * @param currentMagnitude @@ -656,7 +656,7 @@ object GridResultsSupport { /** Partial result for the port at the low voltage side * * @param time - * Wall clock time, the result does belong to + * Simulation time of the result * @param input * Unique identifier of the input model * @param currentMagnitude diff --git a/src/main/scala/edu/ie3/simona/agent/participant/ParticipantAgentFundamentals.scala b/src/main/scala/edu/ie3/simona/agent/participant/ParticipantAgentFundamentals.scala index 12b0e9657c..a505696943 100644 --- a/src/main/scala/edu/ie3/simona/agent/participant/ParticipantAgentFundamentals.scala +++ b/src/main/scala/edu/ie3/simona/agent/participant/ParticipantAgentFundamentals.scala @@ -214,9 +214,9 @@ protected trait ParticipantAgentFundamentals[ * @param modelConfig * Configuration for the model * @param simulationStartDate - * Wall clock time of first instant in simulation + * Simulation time of first instant in simulation * @param simulationEndDate - * Wall clock time of last instant in simulation + * Simulation time of last instant in simulation * @return */ def buildModel( 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 6a9d80cf58..534126b986 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 @@ -440,9 +440,9 @@ trait HpAgentFundamentals * @param modelConfig * Configuration for the model * @param simulationStartDate - * Wall clock time of first instant in simulation + * Simulation time of first instant in simulation * @param simulationEndDate - * Wall clock time of last instant in simulation + * Simulation time of last instant in simulation * @return */ override def buildModel( diff --git a/src/main/scala/edu/ie3/simona/agent/participant/statedata/BaseStateData.scala b/src/main/scala/edu/ie3/simona/agent/participant/statedata/BaseStateData.scala index b6ebd99841..04650f4763 100644 --- a/src/main/scala/edu/ie3/simona/agent/participant/statedata/BaseStateData.scala +++ b/src/main/scala/edu/ie3/simona/agent/participant/statedata/BaseStateData.scala @@ -43,7 +43,7 @@ trait BaseStateData[+PD <: PrimaryDataWithApparentPower[PD]] */ val startDate: ZonedDateTime - /** The wall clock date, at which the simulation ends + /** The simulation time at which the simulation ends */ val endDate: ZonedDateTime @@ -132,7 +132,7 @@ object BaseStateData { * @param startDate * The date, that fits the tick 0 * @param endDate - * The wall clock date, at which the simulation ends + * The simulation time at which the simulation ends * @param outputConfig * Determines the output behaviour of this model * @param additionalActivationTicks @@ -179,7 +179,7 @@ object BaseStateData { * @param startDate * The date, that fits the tick 0 * @param endDate - * The wall clock date, at which the simulation ends + * The simulation time at which the simulation ends * @param model * Physical model of the load * @param services diff --git a/src/main/scala/edu/ie3/simona/service/primary/PrimaryServiceProxy.scala b/src/main/scala/edu/ie3/simona/service/primary/PrimaryServiceProxy.scala index 838608a514..45de3bae63 100644 --- a/src/main/scala/edu/ie3/simona/service/primary/PrimaryServiceProxy.scala +++ b/src/main/scala/edu/ie3/simona/service/primary/PrimaryServiceProxy.scala @@ -79,7 +79,7 @@ import scala.util.{Failure, Success, Try} * @param scheduler * Reference to the scheduler of the simulation * @param startDateTime - * Wall clock time of the first instant in simulation + * Simulation time of the first instant in simulation */ case class PrimaryServiceProxy( scheduler: ActorRef, @@ -132,7 +132,7 @@ case class PrimaryServiceProxy( * @param primaryConfig * Configuration for the primary source * @param simulationStart - * Wall clock time of first instant in simulation + * Simulation time of first instant in simulation * @return * State data, containing the known model and time series identifiers */ @@ -505,7 +505,7 @@ object PrimaryServiceProxy { * @param primaryConfig * Configuration for the primary source * @param simulationStart - * Wall clock time of the first instant in simulation + * Simulation time of the first instant in simulation */ final case class InitPrimaryServiceProxyStateData( primaryConfig: PrimaryConfig, @@ -519,7 +519,7 @@ object PrimaryServiceProxy { * @param timeSeriesToSourceRef * Mapping from time series identifier to [[SourceRef]] * @param simulationStart - * Wall clock time of the first instant in simulation + * Simulation time of the first instant in simulation * @param primaryConfig * The configuration for the sources * @param mappingSource diff --git a/src/main/scala/edu/ie3/simona/service/primary/PrimaryServiceWorker.scala b/src/main/scala/edu/ie3/simona/service/primary/PrimaryServiceWorker.scala index b20fdd834c..f61974392d 100644 --- a/src/main/scala/edu/ie3/simona/service/primary/PrimaryServiceWorker.scala +++ b/src/main/scala/edu/ie3/simona/service/primary/PrimaryServiceWorker.scala @@ -228,8 +228,8 @@ final case class PrimaryServiceWorker[V <: Value]( Option[Long], ) = { /* Get the information to distribute */ - val wallClockTime = tick.toDateTime(serviceBaseStateData.startDateTime) - serviceBaseStateData.source.getValue(wallClockTime).toScala match { + val simulationTime = tick.toDateTime(serviceBaseStateData.startDateTime) + serviceBaseStateData.source.getValue(simulationTime).toScala match { case Some(value) => processDataAndAnnounce(tick, value, serviceBaseStateData) case None => @@ -237,7 +237,7 @@ final case class PrimaryServiceWorker[V <: Value]( log.warning( s"I expected to get data for tick '{}' ({}), but data is not available", tick, - wallClockTime, + simulationTime, ) updateStateDataAndBuildTriggerMessages(serviceBaseStateData) } @@ -371,7 +371,7 @@ object PrimaryServiceWorker { * @param timeSeriesUuid * Unique identifier of the time series to read * @param simulationStart - * Wall clock time of the beginning of simulation time + * Simulation time of the beginning of simulation time * @param csvSep * Column separation character of the csv files * @param directoryPath @@ -400,7 +400,7 @@ object PrimaryServiceWorker { * @param timeSeriesUuid * Unique identifier of the time series to read * @param simulationStart - * Wall clock time of the beginning of simulation time + * Simulation time of the beginning of simulation time * @param sqlParams * Parameters regarding SQL connection and table selection * @param databaseNamingStrategy @@ -420,7 +420,7 @@ object PrimaryServiceWorker { * @param activationTicks * Linked collection of ticks, in which data is available * @param startDateTime - * Wall clock time of the first instant in simulation + * Simulation time of the first instant in simulation * @param source * Implementation of [[TimeSeriesSource]] to use for actual acquisition of * data diff --git a/src/main/scala/edu/ie3/simona/service/weather/SampleWeatherSource.scala b/src/main/scala/edu/ie3/simona/service/weather/SampleWeatherSource.scala index 0db3ded44c..7f3a7de525 100644 --- a/src/main/scala/edu/ie3/simona/service/weather/SampleWeatherSource.scala +++ b/src/main/scala/edu/ie3/simona/service/weather/SampleWeatherSource.scala @@ -65,15 +65,15 @@ final class SampleWeatherSource( private def getWeather( tick: Long ): WeatherData = { - val wallClockTime = tick.toDateTime - val month = wallClockTime.get(MONTH_OF_YEAR) - 1 - val hour = wallClockTime.get(HOUR_OF_DAY) + val simulationTime = tick.toDateTime + val month = simulationTime.get(MONTH_OF_YEAR) - 1 + val hour = simulationTime.get(HOUR_OF_DAY) val year = if ( - wallClockTime.get(YEAR) != 2011 && !(wallClockTime + simulationTime.get(YEAR) != 2011 && !(simulationTime .get(YEAR) == 2012 && month == 0) ) 2011 - else wallClockTime.get(YEAR) + else simulationTime.get(YEAR) val index = (((year - 2011) * 288) + (month * 24) + hour) + 1 WeatherData( WattsPerSquareMeter( diff --git a/src/test/scala/edu/ie3/simona/agent/participant/ParticipantAgentMock.scala b/src/test/scala/edu/ie3/simona/agent/participant/ParticipantAgentMock.scala index c3c77e2292..3e17e76ccb 100644 --- a/src/test/scala/edu/ie3/simona/agent/participant/ParticipantAgentMock.scala +++ b/src/test/scala/edu/ie3/simona/agent/participant/ParticipantAgentMock.scala @@ -260,9 +260,9 @@ class ParticipantAgentMock( * @param modelConfig * Configuration for the model * @param simulationStartDate - * Wall clock time of first instant in simulation + * The simulation time at which the simulation starts * @param simulationEndDate - * Wall clock time of last instant in simulation + * The simulation time at which the simulation ends * @return */ override def buildModel( From 2a72bc6a2bbe12aa60442b9278473d9eee101ada Mon Sep 17 00:00:00 2001 From: Sebastian Peter Date: Fri, 22 Nov 2024 18:59:27 +0100 Subject: [PATCH 48/65] Updating config documentation Signed-off-by: Sebastian Peter --- CHANGELOG.md | 1 + docs/readthedocs/config.md | 14 +++++++------- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 76a0624832..f588d40713 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -38,6 +38,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Add some quote to 'printGoodbye' [#997](https://github.com/ie3-institute/simona/issues/997) - 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) ### Changed - Adapted to changed data source in PSDM [#435](https://github.com/ie3-institute/simona/issues/435) diff --git a/docs/readthedocs/config.md b/docs/readthedocs/config.md index 45deee0778..f6e968659a 100644 --- a/docs/readthedocs/config.md +++ b/docs/readthedocs/config.md @@ -12,15 +12,13 @@ To create the output directory name, the name of the simulation is used as a str `simona.simulationName = "vn_simona"` ### Time parameters -Starting date and time of the simulation - - Format: "YYYY-MM-DD HH:MM:SS" +Starting date and time of the simulation in ISO-8601 date and time format with offset - `simona.time.startDateTime = "2011-01-01 00:00:00"` + `simona.time.startDateTime = "2011-01-01T00:00:00Z"` -Ending date and time of the simulation - - Format: "YYYY-MM-DD HH:MM:SS" +Ending date and time of the simulation in ISO-8601 date and time format with offset - `simona.time.endDateTime = "2011-01-01 02:00:00"` + `simona.time.endDateTime = "2011-01-01T02:00:00Z"` The preset ReadyCheckWindow should be maintained @@ -39,7 +37,9 @@ Setting of the data source `simona.input.grid.datasource.id = "csv"` -Specify the folder path containing the csv data of the grid components and the csv separator (e.g. "," or ";") +Specify the folder path containing the csv data of the grid components and the csv separator (e.g. "," or ";"). +The directory structure is determined by the boolean `isHierarchic`. +If files are placed within [a specific set of subdirectories](https://powersystemdatamodel.readthedocs.io/en/latest/io/csvfiles.html#default-directory-hierarchy), `isHierarchic: true` needs to be set. ``` simona.input.primary.csvParams = { From cbd997ebc8ef6f86d87e5653b08c528710d29d56 Mon Sep 17 00:00:00 2001 From: danielfeismann Date: Fri, 22 Nov 2024 23:39:58 +0100 Subject: [PATCH 49/65] fix EmAgentIT --- .../scala/edu/ie3/simona/agent/em/EmAgentIT.scala | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/test/scala/edu/ie3/simona/agent/em/EmAgentIT.scala b/src/test/scala/edu/ie3/simona/agent/em/EmAgentIT.scala index be1cee377f..b43b73f129 100644 --- a/src/test/scala/edu/ie3/simona/agent/em/EmAgentIT.scala +++ b/src/test/scala/edu/ie3/simona/agent/em/EmAgentIT.scala @@ -680,7 +680,7 @@ class EmAgentIT Celsius(0d), MetersPerSecond(0d), ), - Some(56000), + Some(28800), ) } @@ -692,26 +692,26 @@ class EmAgentIT emResult.getQ should equalWithTolerance(0.000088285537.asMegaVar) } - scheduler.expectMessage(Completion(emAgentActivation, Some(44398))) + scheduler.expectMessage(Completion(emAgentActivation, Some(26748))) - /* TICK 44398 + /* TICK 26748 LOAD: 0.000269 MW (unchanged) PV: -0.000032 MW (unchanged) Heat pump: Is turned on again and cannot be turned off -> flex signal is no control -> 0.00485 MW */ - emAgentActivation ! Activation(44398) + emAgentActivation ! Activation(26748) resultListener.expectMessageType[ParticipantResultEvent] match { case ParticipantResultEvent(emResult: EmResult) => emResult.getInputModel shouldBe emInput.getUuid - emResult.getTime shouldBe 44398.toDateTime + emResult.getTime shouldBe 26748.toDateTime emResult.getP should equalWithTolerance(0.005086768.asMegaWatt) emResult.getQ should equalWithTolerance(0.00107312004.asMegaVar) } - scheduler.expectMessage(Completion(emAgentActivation, Some(56000))) + scheduler.expectMessage(Completion(emAgentActivation, Some(28800))) } } From a07b6262cd8753532aebfcb6fdd9d0b5cd870e36 Mon Sep 17 00:00:00 2001 From: danielfeismann Date: Fri, 22 Nov 2024 23:51:58 +0100 Subject: [PATCH 50/65] also turn Hp on in case house has additional demand and the Hp was running in the last state --- src/main/scala/edu/ie3/simona/model/participant/HpModel.scala | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) 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 3e5c8f4cd1..74f80350d5 100644 --- a/src/main/scala/edu/ie3/simona/model/participant/HpModel.scala +++ b/src/main/scala/edu/ie3/simona/model/participant/HpModel.scala @@ -192,7 +192,9 @@ final case class HpModel( val turnHpOn = (demandHouse.hasRequiredDemand && noThermalStorageOrThermalStorageIsEmpty) || - demandThermalStorage.hasRequiredDemand || (demandThermalStorage.hasAdditionalDemand && lastState.isRunning) + (demandHouse.hasAdditionalDemand && lastState.isRunning) || + demandThermalStorage.hasRequiredDemand || + (demandThermalStorage.hasAdditionalDemand && lastState.isRunning) val canOperate = demandHouse.hasRequiredDemand || demandHouse.hasAdditionalDemand || From 7516d7b0f92eb702a3b57360f217215853e178b7 Mon Sep 17 00:00:00 2001 From: danielfeismann Date: Fri, 22 Nov 2024 23:53:31 +0100 Subject: [PATCH 51/65] fmt --- src/main/scala/edu/ie3/simona/model/participant/HpModel.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 8616d43ab6..152df70594 100644 --- a/src/main/scala/edu/ie3/simona/model/participant/HpModel.scala +++ b/src/main/scala/edu/ie3/simona/model/participant/HpModel.scala @@ -192,7 +192,7 @@ final case class HpModel( val turnHpOn = (demandHouse.hasRequiredDemand && noThermalStorageOrThermalStorageIsEmpty) || - (demandHouse.hasAdditionalDemand && lastState.isRunning) || + (demandHouse.hasAdditionalDemand && lastState.isRunning) || demandThermalStorage.hasRequiredDemand || (demandThermalStorage.hasAdditionalDemand && lastState.isRunning) From 31c73d9564264b90fc316664017b10d926438a35 Mon Sep 17 00:00:00 2001 From: danielfeismann Date: Sat, 23 Nov 2024 00:56:22 +0100 Subject: [PATCH 52/65] fix handleInfeed of thermal energy --- .../simona/model/thermal/ThermalGrid.scala | 41 +++++++++++++++---- 1 file changed, 32 insertions(+), 9 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 47710be1d7..ec61fe86cf 100644 --- a/src/main/scala/edu/ie3/simona/model/thermal/ThermalGrid.scala +++ b/src/main/scala/edu/ie3/simona/model/thermal/ThermalGrid.scala @@ -241,8 +241,7 @@ final case class ThermalGrid( } if ( - // todo: Check if it has to be hasRequiredDemand or hasAdditionalDemand - (qDotHouseLastState > zeroKW && (qDotStorageLastState >= zeroKW)) | (qDotStorageLastState > zeroKW & thermalDemands.heatStorageDemand.hasAdditionalDemand) + (qDotHouseLastState > zeroKW && (qDotStorageLastState >= zeroKW)) | (qDotStorageLastState > zeroKW && thermalDemands.heatStorageDemand.hasAdditionalDemand) ) { val (updatedHouseState, thermalHouseThreshold, remainingQDotHouse) = handleInfeedHouse( @@ -281,7 +280,7 @@ final case class ThermalGrid( nextThreshold, ) } - // Handle edge case where house get heated from storage and HP will be activated in between + // Handle edge case where house was heated from storage and HP will be activated in between else if ((qDotHouseLastState > zeroKW && qDotStorageLastState < zeroKW)) { if (isRunning) { handleCases( @@ -303,14 +302,28 @@ final case class ThermalGrid( qDotStorageLastState, ) } - } else { - // todo: Check if it has to be hasRequiredDemand or hasAdditionalDemand + } + // Handle edge case where house should be heated from storage + else if ((!isRunning && qDot > zeroKW)) { + handleCases( + tick, + lastAmbientTemperature, + ambientTemperature, + state, + qDot, + -qDot, + ) + } + + + else { ( thermalDemands.houseDemand.hasRequiredDemand, - thermalDemands.heatStorageDemand.hasRequiredDemand, + thermalDemands.houseDemand.hasAdditionalDemand, + thermalDemands.heatStorageDemand.hasAdditionalDemand, ) match { - case (true, _) => + case (true,_, _) => // house first then heatStorage after heating House handleCases( tick, @@ -321,7 +334,7 @@ final case class ThermalGrid( zeroKW, ) - case (false, true) => + case (false,_, true) => handleCases( tick, lastAmbientTemperature, @@ -331,7 +344,17 @@ final case class ThermalGrid( qDot, ) - case (false, false) => + case (false,true, false) => + handleCases( + tick, + lastAmbientTemperature, + ambientTemperature, + state, + qDot, + zeroKW, + ) + + case (false,false, false) => handleCases( tick, lastAmbientTemperature, From 85594d3838261fd8055cbf399b23833c7f637cb0 Mon Sep 17 00:00:00 2001 From: danielfeismann Date: Sat, 23 Nov 2024 00:56:58 +0100 Subject: [PATCH 53/65] correct direction of thermal power of thermal heat storage --- src/main/scala/edu/ie3/simona/model/participant/HpModel.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 152df70594..92ad195af9 100644 --- a/src/main/scala/edu/ie3/simona/model/participant/HpModel.scala +++ b/src/main/scala/edu/ie3/simona/model/participant/HpModel.scala @@ -270,7 +270,7 @@ final case class HpModel( ) ( zeroKW, - thermalGrid.storage.map(_.getChargingPower: squants.Power).get * (-1), + thermalGrid.storage.map(_.getChargingPower: squants.Power).get, ) else (zeroKW, zeroKW) } From 391b49df3ec201eae6d5976b7e5fb8543e69704d Mon Sep 17 00:00:00 2001 From: danielfeismann Date: Sat, 23 Nov 2024 00:57:22 +0100 Subject: [PATCH 54/65] fix ThermalGridWithStorageOnlySpec test where putting energy into storage --- .../simona/model/thermal/ThermalGridWithStorageOnlySpec.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 c4e5339a54..ba0c995778 100644 --- a/src/test/scala/edu/ie3/simona/model/thermal/ThermalGridWithStorageOnlySpec.scala +++ b/src/test/scala/edu/ie3/simona/model/thermal/ThermalGridWithStorageOnlySpec.scala @@ -187,7 +187,7 @@ class ThermalGridWithStorageOnlySpec testGridAmbientTemperature, testGridAmbientTemperature, gridState, - isNotRunning, + isRunning, testGridQDotInfeed, onlyThermalDemandOfHeatStorage, ) From ba9aeea48c1955932ab195bb92ebe6d74b2467c3 Mon Sep 17 00:00:00 2001 From: danielfeismann Date: Sat, 23 Nov 2024 00:57:38 +0100 Subject: [PATCH 55/65] add ThermalGridWithStorageOnlySpec test where taking energy from storage --- .../ThermalGridWithStorageOnlySpec.scala | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) 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 ba0c995778..be3a54e260 100644 --- a/src/test/scala/edu/ie3/simona/model/thermal/ThermalGridWithStorageOnlySpec.scala +++ b/src/test/scala/edu/ie3/simona/model/thermal/ThermalGridWithStorageOnlySpec.scala @@ -204,6 +204,45 @@ class ThermalGridWithStorageOnlySpec } reachedThreshold shouldBe Some(StorageFull(276000L)) } + + "properly take energy from storage" in { + val tick = 0L + val gridState = ThermalGrid + .startingState(thermalGrid) + .copy(storageState = + Some( + ThermalStorageState( + 0L, + KilowattHours(150d), + zeroKW, + ) + ) + ) + + val (updatedGridState, reachedThreshold) = + thermalGrid invokePrivate handleInfeed( + tick, + testGridAmbientTemperature, + testGridAmbientTemperature, + gridState, + isNotRunning, + testGridQDotInfeed, + onlyThermalDemandOfHeatStorage, + ) + + updatedGridState match { + case ThermalGridState( + None, + Some(ThermalStorageState(tick, storedEnergy, qDot)), + ) => + tick shouldBe 0L + storedEnergy should approximate(KilowattHours(150d)) + qDot should approximate(testGridQDotInfeed * (-1)) + case _ => fail("Thermal grid state has been calculated wrong.") + } + reachedThreshold shouldBe Some(StorageEmpty(36000L)) + } + } "updating the grid state dependent on the given thermal infeed" should { From f7b1c1d4b5b82557414ec87b3ea10feb572ba48d Mon Sep 17 00:00:00 2001 From: danielfeismann Date: Sat, 23 Nov 2024 01:03:49 +0100 Subject: [PATCH 56/65] fix ThermalGridWithHouseAndStorageSpec --- .../model/thermal/ThermalGridWithHouseAndStorageSpec.scala | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) 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 cc28ff8505..81c0b2d623 100644 --- a/src/test/scala/edu/ie3/simona/model/thermal/ThermalGridWithHouseAndStorageSpec.scala +++ b/src/test/scala/edu/ie3/simona/model/thermal/ThermalGridWithHouseAndStorageSpec.scala @@ -503,7 +503,8 @@ class ThermalGridWithHouseAndStorageSpec testGridAmbientTemperature, testGridAmbientTemperature, initialGridState, - isNotRunning, + // Fixme startingState and isRunning do not match here + isRunning, externalQDot, onlyThermalDemandOfHouse, ) @@ -553,7 +554,8 @@ class ThermalGridWithHouseAndStorageSpec testGridAmbientTemperature, testGridAmbientTemperature, gridState, - isNotRunning, + // Fixme startingState and isRunning do not match here + isRunning, externalQDot, onlyThermalDemandOfHeatStorage, ) From bdd63f6e25522440ffca4abd37ead40f6e11b3f2 Mon Sep 17 00:00:00 2001 From: danielfeismann Date: Sat, 23 Nov 2024 01:03:58 +0100 Subject: [PATCH 57/65] fmt --- .../simona/model/thermal/ThermalGrid.scala | 27 +++++++++---------- .../ThermalGridWithStorageOnlySpec.scala | 7 ++--- 2 files changed, 16 insertions(+), 18 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 ec61fe86cf..d23aa4798e 100644 --- a/src/main/scala/edu/ie3/simona/model/thermal/ThermalGrid.scala +++ b/src/main/scala/edu/ie3/simona/model/thermal/ThermalGrid.scala @@ -306,24 +306,21 @@ final case class ThermalGrid( // Handle edge case where house should be heated from storage else if ((!isRunning && qDot > zeroKW)) { handleCases( - tick, - lastAmbientTemperature, - ambientTemperature, - state, - qDot, - -qDot, - ) - } - - - else { + tick, + lastAmbientTemperature, + ambientTemperature, + state, + qDot, + -qDot, + ) + } else { ( thermalDemands.houseDemand.hasRequiredDemand, thermalDemands.houseDemand.hasAdditionalDemand, thermalDemands.heatStorageDemand.hasAdditionalDemand, ) match { - case (true,_, _) => + case (true, _, _) => // house first then heatStorage after heating House handleCases( tick, @@ -334,7 +331,7 @@ final case class ThermalGrid( zeroKW, ) - case (false,_, true) => + case (false, _, true) => handleCases( tick, lastAmbientTemperature, @@ -344,7 +341,7 @@ final case class ThermalGrid( qDot, ) - case (false,true, false) => + case (false, true, false) => handleCases( tick, lastAmbientTemperature, @@ -354,7 +351,7 @@ final case class ThermalGrid( zeroKW, ) - case (false,false, false) => + case (false, false, false) => handleCases( tick, lastAmbientTemperature, 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 be3a54e260..e896694f0d 100644 --- a/src/test/scala/edu/ie3/simona/model/thermal/ThermalGridWithStorageOnlySpec.scala +++ b/src/test/scala/edu/ie3/simona/model/thermal/ThermalGridWithStorageOnlySpec.scala @@ -187,6 +187,7 @@ class ThermalGridWithStorageOnlySpec testGridAmbientTemperature, testGridAmbientTemperature, gridState, + // Fixme startingState and isRunning do not match here isRunning, testGridQDotInfeed, onlyThermalDemandOfHeatStorage, @@ -232,9 +233,9 @@ class ThermalGridWithStorageOnlySpec updatedGridState match { case ThermalGridState( - None, - Some(ThermalStorageState(tick, storedEnergy, qDot)), - ) => + None, + Some(ThermalStorageState(tick, storedEnergy, qDot)), + ) => tick shouldBe 0L storedEnergy should approximate(KilowattHours(150d)) qDot should approximate(testGridQDotInfeed * (-1)) From aac429720d3d36532419560e48ea8956ab438e39 Mon Sep 17 00:00:00 2001 From: danielfeismann Date: Sat, 23 Nov 2024 09:49:49 +0100 Subject: [PATCH 58/65] adapt EmAgentIT --- .../edu/ie3/simona/agent/em/EmAgentIT.scala | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/src/test/scala/edu/ie3/simona/agent/em/EmAgentIT.scala b/src/test/scala/edu/ie3/simona/agent/em/EmAgentIT.scala index b43b73f129..37155b6cba 100644 --- a/src/test/scala/edu/ie3/simona/agent/em/EmAgentIT.scala +++ b/src/test/scala/edu/ie3/simona/agent/em/EmAgentIT.scala @@ -692,25 +692,6 @@ class EmAgentIT emResult.getQ should equalWithTolerance(0.000088285537.asMegaVar) } - scheduler.expectMessage(Completion(emAgentActivation, Some(26748))) - - /* TICK 26748 - LOAD: 0.000269 MW (unchanged) - PV: -0.000032 MW (unchanged) - Heat pump: Is turned on again and cannot be turned off - -> flex signal is no control -> 0.00485 MW - */ - - emAgentActivation ! Activation(26748) - - resultListener.expectMessageType[ParticipantResultEvent] match { - case ParticipantResultEvent(emResult: EmResult) => - emResult.getInputModel shouldBe emInput.getUuid - emResult.getTime shouldBe 26748.toDateTime - emResult.getP should equalWithTolerance(0.005086768.asMegaWatt) - emResult.getQ should equalWithTolerance(0.00107312004.asMegaVar) - } - scheduler.expectMessage(Completion(emAgentActivation, Some(28800))) } } From 092a6c93fc409d1336e2205c4656b05824f881dc Mon Sep 17 00:00:00 2001 From: danielfeismann Date: Sat, 23 Nov 2024 09:52:33 +0100 Subject: [PATCH 59/65] fmt --- .../ThermalGridWithHouseAndStorageSpec.scala | 750 +++++++++--------- 1 file changed, 375 insertions(+), 375 deletions(-) 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 81c0b2d623..cccb1e1bca 100644 --- a/src/test/scala/edu/ie3/simona/model/thermal/ThermalGridWithHouseAndStorageSpec.scala +++ b/src/test/scala/edu/ie3/simona/model/thermal/ThermalGridWithHouseAndStorageSpec.scala @@ -148,444 +148,444 @@ class ThermalGridWithHouseAndStorageSpec ThermalStorageState(10800, zeroKWh, zeroKW) ) } + } - "handling thermal energy consumption from grid" should { - val handleConsumption = - PrivateMethod[(ThermalGridState, Option[ThermalThreshold])]( - Symbol("handleConsumption") - ) - - "return house threshold, if storage is in balance" in { - val tick = 0L - val initialGridState = ThermalGrid.startingState(thermalGrid) - val initialLoading = KilowattHours(430d) - val gridState = initialGridState.copy(storageState = - initialGridState.storageState.map(storageState => - storageState.copy(storedEnergy = initialLoading) - ) - ) - val externalQDot = zeroKW - - val (updatedGridState, reachedThreshold) = - thermalGrid invokePrivate handleConsumption( - tick, - testGridAmbientTemperature, - testGridAmbientTemperature, - gridState, - externalQDot, - ) + "handling thermal energy consumption from grid" should { + val handleConsumption = + PrivateMethod[(ThermalGridState, Option[ThermalThreshold])]( + Symbol("handleConsumption") + ) - updatedGridState match { - case ThermalGridState( - _, - Some( - ThermalStorageState(storageTick, storedEnergy, qDotStorage) - ), - ) => - storageTick shouldBe 0L - storedEnergy should approximate(initialLoading) - qDotStorage should approximate(externalQDot) - case _ => fail("Thermal grid state has been calculated wrong.") - } - reachedThreshold shouldBe Some( - HouseTemperatureLowerBoundaryReached(154285L) + "return house threshold, if storage is in balance" in { + val tick = 0L + val initialGridState = ThermalGrid.startingState(thermalGrid) + val initialLoading = KilowattHours(430d) + val gridState = initialGridState.copy(storageState = + initialGridState.storageState.map(storageState => + storageState.copy(storedEnergy = initialLoading) ) - } + ) + val externalQDot = zeroKW - "take energy from storage, if there is actual consumption" in { - val tick = 0L - val initialGridState = ThermalGrid.startingState(thermalGrid) - val initialLoading = KilowattHours(200d) - val gridState = initialGridState.copy(storageState = - initialGridState.storageState.map(storageState => - storageState.copy(storedEnergy = initialLoading) - ) + val (updatedGridState, reachedThreshold) = + thermalGrid invokePrivate handleConsumption( + tick, + testGridAmbientTemperature, + testGridAmbientTemperature, + gridState, + externalQDot, ) - val externalQDot = testGridQDotConsumption - - val (updatedGridState, reachedThreshold) = - thermalGrid invokePrivate handleConsumption( - tick, - testGridAmbientTemperature, - testGridAmbientTemperature, - gridState, - externalQDot, - ) - updatedGridState match { - case ThermalGridState( - Some( - ThermalHouseState(houseTick, innerTemperature, qDotHouse) - ), - Some( - ThermalStorageState(storageTick, storedEnergy, qDotStorage) - ), - ) => - houseTick shouldBe 0L - innerTemperature should approximate(Celsius(18.9999d)) - qDotHouse should approximate(zeroKW) - - storageTick shouldBe 0L - storedEnergy should approximate(initialLoading) - qDotStorage should approximate(externalQDot) - case _ => fail("Thermal grid state has been calculated wrong.") - } - reachedThreshold shouldBe Some(StorageEmpty(17143L)) + updatedGridState match { + case ThermalGridState( + _, + Some( + ThermalStorageState(storageTick, storedEnergy, qDotStorage) + ), + ) => + storageTick shouldBe 0L + storedEnergy should approximate(initialLoading) + qDotStorage should approximate(externalQDot) + case _ => fail("Thermal grid state has been calculated wrong.") } + reachedThreshold shouldBe Some( + HouseTemperatureLowerBoundaryReached(154285L) + ) } - "revising infeed from storage to house" should { - val zeroInflux = zeroKW - val tick = 3600L - val ambientTemperature = Celsius(14d) - "hand back unaltered information if needed information is missing" in { - val maybeHouseState = Some( - ( - ThermalHouseState( - tick, - Celsius( - thermalHouseInput.getTargetTemperature - .to(Units.CELSIUS) - .getValue - .doubleValue - ), - zeroInflux, - ), - None, - ) + "take energy from storage, if there is actual consumption" in { + val tick = 0L + val initialGridState = ThermalGrid.startingState(thermalGrid) + val initialLoading = KilowattHours(200d) + val gridState = initialGridState.copy(storageState = + initialGridState.storageState.map(storageState => + storageState.copy(storedEnergy = initialLoading) ) - val maybeStorageState = None + ) + val externalQDot = testGridQDotConsumption - thermalGrid.reviseInfeedFromStorage( + val (updatedGridState, reachedThreshold) = + thermalGrid invokePrivate handleConsumption( tick, - maybeHouseState, - maybeStorageState, - maybeHouseState.map(_._1), - None, testGridAmbientTemperature, testGridAmbientTemperature, - testGridQDotConsumption, - ) match { - case (maybeRevisedHouseState, maybeRevisedStorageState) => - maybeRevisedHouseState shouldBe maybeHouseState - maybeRevisedStorageState shouldBe maybeStorageState - } - } + gridState, + externalQDot, + ) - "hand back unaltered information if house temperature is above lower boundary temperature" in { - val maybeHouseState = Some( - ( - ThermalHouseState( - tick, - Celsius( - thermalHouseInput.getTargetTemperature - .to(Units.CELSIUS) - .getValue - .doubleValue + updatedGridState match { + case ThermalGridState( + Some( + ThermalHouseState(houseTick, innerTemperature, qDotHouse) ), - zeroInflux, - ), - None, - ) - ) - val maybeStorageState = Some( - ( - ThermalStorageState( - tick, - KilowattHours(50d), - zeroInflux, + Some( + ThermalStorageState(storageTick, storedEnergy, qDotStorage) + ), + ) => + houseTick shouldBe 0L + innerTemperature should approximate(Celsius(18.9999d)) + qDotHouse should approximate(zeroKW) + + storageTick shouldBe 0L + storedEnergy should approximate(initialLoading) + qDotStorage should approximate(externalQDot) + case _ => fail("Thermal grid state has been calculated wrong.") + } + reachedThreshold shouldBe Some(StorageEmpty(17143L)) + } + } + + "revising infeed from storage to house" should { + val zeroInflux = zeroKW + val tick = 3600L + val ambientTemperature = Celsius(14d) + "hand back unaltered information if needed information is missing" in { + val maybeHouseState = Some( + ( + ThermalHouseState( + tick, + Celsius( + thermalHouseInput.getTargetTemperature + .to(Units.CELSIUS) + .getValue + .doubleValue ), - None, - ) + zeroInflux, + ), + None, ) - - thermalGrid.reviseInfeedFromStorage( - tick, - maybeHouseState, - maybeStorageState, - maybeHouseState.map(_._1), - maybeStorageState.map(_._1), - ambientTemperature, - ambientTemperature, - zeroInflux, - ) match { - case (maybeRevisedHouseState, maybeRevisedStorageState) => - maybeRevisedHouseState shouldBe maybeHouseState - maybeRevisedStorageState shouldBe maybeStorageState - } + ) + val maybeStorageState = None + + thermalGrid.reviseInfeedFromStorage( + tick, + maybeHouseState, + maybeStorageState, + maybeHouseState.map(_._1), + None, + testGridAmbientTemperature, + testGridAmbientTemperature, + testGridQDotConsumption, + ) match { + case (maybeRevisedHouseState, maybeRevisedStorageState) => + maybeRevisedHouseState shouldBe maybeHouseState + maybeRevisedStorageState shouldBe maybeStorageState } + } - "hand back unaltered information if house temperature is above lower boundary temperature, but has influx" in { - val maybeHouseState = Some( - ( - ThermalHouseState( - tick, - Celsius( - thermalHouseInput.getTargetTemperature - .to(Units.CELSIUS) - .getValue - .doubleValue - ), - testGridQDotInfeed, + "hand back unaltered information if house temperature is above lower boundary temperature" in { + val maybeHouseState = Some( + ( + ThermalHouseState( + tick, + Celsius( + thermalHouseInput.getTargetTemperature + .to(Units.CELSIUS) + .getValue + .doubleValue ), - Some(HouseTemperatureUpperBoundaryReached(3600L)), - ) + zeroInflux, + ), + None, ) - val maybeStorageState = Some( - ( - ThermalStorageState( - tick, - KilowattHours(50d), - zeroInflux, - ), - None, - ) + ) + val maybeStorageState = Some( + ( + ThermalStorageState( + tick, + KilowattHours(50d), + zeroInflux, + ), + None, ) + ) - thermalGrid.reviseInfeedFromStorage( - tick, - maybeHouseState, - maybeStorageState, - maybeHouseState.map(_._1), - maybeStorageState.map(_._1), - ambientTemperature, - ambientTemperature, - testGridQDotInfeed, - ) match { - case (maybeRevisedHouseState, maybeRevisedStorageState) => - maybeRevisedHouseState shouldBe maybeHouseState - maybeRevisedStorageState shouldBe maybeStorageState - } + thermalGrid.reviseInfeedFromStorage( + tick, + maybeHouseState, + maybeStorageState, + maybeHouseState.map(_._1), + maybeStorageState.map(_._1), + ambientTemperature, + ambientTemperature, + zeroInflux, + ) match { + case (maybeRevisedHouseState, maybeRevisedStorageState) => + maybeRevisedHouseState shouldBe maybeHouseState + maybeRevisedStorageState shouldBe maybeStorageState } + } - "hand back unaltered information if house temperature is at lower boundary temperature, but storage is empty" in { - val maybeHouseState = Some( - ( - ThermalHouseState( - tick, - Celsius( - thermalHouseInput.getLowerTemperatureLimit - .to(Units.CELSIUS) - .getValue - .doubleValue - ), - zeroInflux, + "hand back unaltered information if house temperature is above lower boundary temperature, but has influx" in { + val maybeHouseState = Some( + ( + ThermalHouseState( + tick, + Celsius( + thermalHouseInput.getTargetTemperature + .to(Units.CELSIUS) + .getValue + .doubleValue ), - Some(HouseTemperatureLowerBoundaryReached(tick)), - ) + testGridQDotInfeed, + ), + Some(HouseTemperatureUpperBoundaryReached(3600L)), ) - val maybeStorageState = Some( - ( - ThermalStorageState( - tick, - zeroKWh, - testGridQDotInfeed, - ), - Some(StorageEmpty(tick)), - ) + ) + val maybeStorageState = Some( + ( + ThermalStorageState( + tick, + KilowattHours(50d), + zeroInflux, + ), + None, ) + ) - thermalGrid.reviseInfeedFromStorage( - tick, - maybeHouseState, - maybeStorageState, - maybeHouseState.map(_._1), - maybeStorageState.map(_._1), - ambientTemperature, - ambientTemperature, - zeroInflux, - ) match { - case (maybeRevisedHouseState, maybeRevisedStorageState) => - maybeRevisedHouseState shouldBe maybeHouseState - maybeRevisedStorageState shouldBe maybeStorageState - } + thermalGrid.reviseInfeedFromStorage( + tick, + maybeHouseState, + maybeStorageState, + maybeHouseState.map(_._1), + maybeStorageState.map(_._1), + ambientTemperature, + ambientTemperature, + testGridQDotInfeed, + ) match { + case (maybeRevisedHouseState, maybeRevisedStorageState) => + maybeRevisedHouseState shouldBe maybeHouseState + maybeRevisedStorageState shouldBe maybeStorageState } + } - "alter the given states as expected, when all conditions are met" in { - val maybeHouseState = Some( - ( - ThermalHouseState( - tick, - Celsius( - thermalHouseInput.getLowerTemperatureLimit - .to(Units.CELSIUS) - .getValue - .doubleValue - ), - zeroInflux, + "hand back unaltered information if house temperature is at lower boundary temperature, but storage is empty" in { + val maybeHouseState = Some( + ( + ThermalHouseState( + tick, + Celsius( + thermalHouseInput.getLowerTemperatureLimit + .to(Units.CELSIUS) + .getValue + .doubleValue ), - Some(HouseTemperatureLowerBoundaryReached(tick)), - ) + zeroInflux, + ), + Some(HouseTemperatureLowerBoundaryReached(tick)), ) - val maybeStorageState = Some( - ( - ThermalStorageState( - tick, - KilowattHours(20d), - testGridQDotInfeed, - ), - None, - ) + ) + val maybeStorageState = Some( + ( + ThermalStorageState( + tick, + zeroKWh, + testGridQDotInfeed, + ), + Some(StorageEmpty(tick)), ) - val formerHouseState = Some( + ) + + thermalGrid.reviseInfeedFromStorage( + tick, + maybeHouseState, + maybeStorageState, + maybeHouseState.map(_._1), + maybeStorageState.map(_._1), + ambientTemperature, + ambientTemperature, + zeroInflux, + ) match { + case (maybeRevisedHouseState, maybeRevisedStorageState) => + maybeRevisedHouseState shouldBe maybeHouseState + maybeRevisedStorageState shouldBe maybeStorageState + } + } + + "alter the given states as expected, when all conditions are met" in { + val maybeHouseState = Some( + ( ThermalHouseState( - 0L, + tick, Celsius( - thermalHouseInput.getTargetTemperature + thermalHouseInput.getLowerTemperatureLimit .to(Units.CELSIUS) .getValue .doubleValue ), zeroInflux, - ) + ), + Some(HouseTemperatureLowerBoundaryReached(tick)), ) - val formerStorageState = Some( + ) + val maybeStorageState = Some( + ( ThermalStorageState( - 0L, - KilowattHours(70d), - Kilowatts(-50d), - ) + tick, + KilowattHours(20d), + testGridQDotInfeed, + ), + None, ) - - thermalGrid.reviseInfeedFromStorage( - tick, - maybeHouseState, - maybeStorageState, - formerHouseState, - formerStorageState, - ambientTemperature, - ambientTemperature, + ) + val formerHouseState = Some( + ThermalHouseState( + 0L, + Celsius( + thermalHouseInput.getTargetTemperature + .to(Units.CELSIUS) + .getValue + .doubleValue + ), zeroInflux, - ) match { - case ( - Some( - ( - ThermalHouseState(houseTick, _, revisedQDotHouse), - Some(HouseTemperatureUpperBoundaryReached(houseWarmTick)), - ) - ), - Some( - ( - ThermalStorageState(storageTick, _, revisedQDotStorage), - Some(StorageEmpty(storageEmptyTick)), - ) - ), - ) => - houseTick shouldBe tick - storageTick shouldBe tick - - revisedQDotHouse should approximate(thermalStorage.chargingPower) - revisedQDotStorage should approximate( - thermalStorage.chargingPower * (-1) - ) + ) + ) + val formerStorageState = Some( + ThermalStorageState( + 0L, + KilowattHours(70d), + Kilowatts(-50d), + ) + ) - houseWarmTick shouldBe 3695L - storageEmptyTick shouldBe 3663L - case _ => fail("Revision of states failed") - } + thermalGrid.reviseInfeedFromStorage( + tick, + maybeHouseState, + maybeStorageState, + formerHouseState, + formerStorageState, + ambientTemperature, + ambientTemperature, + zeroInflux, + ) match { + case ( + Some( + ( + ThermalHouseState(houseTick, _, revisedQDotHouse), + Some(HouseTemperatureUpperBoundaryReached(houseWarmTick)), + ) + ), + Some( + ( + ThermalStorageState(storageTick, _, revisedQDotStorage), + Some(StorageEmpty(storageEmptyTick)), + ) + ), + ) => + houseTick shouldBe tick + storageTick shouldBe tick + + revisedQDotHouse should approximate(thermalStorage.chargingPower) + revisedQDotStorage should approximate( + thermalStorage.chargingPower * (-1) + ) + + houseWarmTick shouldBe 3695L + storageEmptyTick shouldBe 3663L + case _ => fail("Revision of states failed") } } + } + + "handling thermal infeed into the grid" should { + val handleInfeed = + PrivateMethod[(ThermalGridState, Option[ThermalThreshold])]( + Symbol("handleInfeed") + ) + + "heat the house, if the upper temperature in the house is not reached" in { + val tick = 0L + val initialGridState = ThermalGrid.startingState(thermalGrid) + val externalQDot = testGridQDotInfeed - "handling thermal infeed into the grid" should { - val handleInfeed = - PrivateMethod[(ThermalGridState, Option[ThermalThreshold])]( - Symbol("handleInfeed") + val (updatedGridState, reachedThreshold) = + thermalGrid invokePrivate handleInfeed( + tick, + testGridAmbientTemperature, + testGridAmbientTemperature, + initialGridState, + // Fixme startingState and isRunning do not match here + isRunning, + externalQDot, + onlyThermalDemandOfHouse, ) - "heat the house, if the upper temperature in the house is not reached" in { - val tick = 0L - val initialGridState = ThermalGrid.startingState(thermalGrid) - val externalQDot = testGridQDotInfeed + updatedGridState match { + case ThermalGridState( + Some( + ThermalHouseState(houseTick, innerTemperature, qDotHouse) + ), + Some( + ThermalStorageState(storageTick, storedEnergy, qDotStorage) + ), + ) => + houseTick shouldBe 0L + innerTemperature should approximate(Celsius(18.9999d)) + qDotHouse should approximate(externalQDot) - val (updatedGridState, reachedThreshold) = - thermalGrid invokePrivate handleInfeed( - tick, - testGridAmbientTemperature, - testGridAmbientTemperature, - initialGridState, - // Fixme startingState and isRunning do not match here - isRunning, - externalQDot, - onlyThermalDemandOfHouse, + storageTick shouldBe 0L + storedEnergy should approximate( + initialGridState.storageState + .map(_.storedEnergy) + .getOrElse(fail("No initial storage state found")) ) - updatedGridState match { - case ThermalGridState( - Some( - ThermalHouseState(houseTick, innerTemperature, qDotHouse) - ), - Some( - ThermalStorageState(storageTick, storedEnergy, qDotStorage) - ), - ) => - houseTick shouldBe 0L - innerTemperature should approximate(Celsius(18.9999d)) - qDotHouse should approximate(externalQDot) - - storageTick shouldBe 0L - storedEnergy should approximate( - initialGridState.storageState - .map(_.storedEnergy) - .getOrElse(fail("No initial storage state found")) - ) + qDotStorage should approximate(zeroKW) - qDotStorage should approximate(zeroKW) + case _ => fail("Thermal grid state has been calculated wrong.") + } + reachedThreshold shouldBe Some( + HouseTemperatureUpperBoundaryReached(7372L) + ) + } - case _ => fail("Thermal grid state has been calculated wrong.") - } - reachedThreshold shouldBe Some( - HouseTemperatureUpperBoundaryReached(7372L) + "load the storage, if the upper temperature in the house is reached" in { + val tick = 0L + val initialGridState = ThermalGrid.startingState(thermalGrid) + val gridState = initialGridState.copy(houseState = + initialGridState.houseState.map( + _.copy(innerTemperature = thermalHouse.upperBoundaryTemperature) ) - } + ) + val externalQDot = testGridQDotInfeed - "load the storage, if the upper temperature in the house is reached" in { - val tick = 0L - val initialGridState = ThermalGrid.startingState(thermalGrid) - val gridState = initialGridState.copy(houseState = - initialGridState.houseState.map( - _.copy(innerTemperature = thermalHouse.upperBoundaryTemperature) - ) + val (updatedGridState, reachedThreshold) = + thermalGrid invokePrivate handleInfeed( + tick, + testGridAmbientTemperature, + testGridAmbientTemperature, + gridState, + // Fixme startingState and isRunning do not match here + isRunning, + externalQDot, + onlyThermalDemandOfHeatStorage, ) - val externalQDot = testGridQDotInfeed - val (updatedGridState, reachedThreshold) = - thermalGrid invokePrivate handleInfeed( - tick, - testGridAmbientTemperature, - testGridAmbientTemperature, - gridState, - // Fixme startingState and isRunning do not match here - isRunning, - externalQDot, - onlyThermalDemandOfHeatStorage, - ) + updatedGridState match { + case ThermalGridState( + Some( + ThermalHouseState(houseTick, innerTemperature, qDotHouse) + ), + Some( + ThermalStorageState(storageTick, storedEnergy, qDotStorage) + ), + ) => + houseTick shouldBe 0L + innerTemperature should approximate(Celsius(20.99999167d)) + qDotHouse should approximate(zeroKW) - updatedGridState match { - case ThermalGridState( - Some( - ThermalHouseState(houseTick, innerTemperature, qDotHouse) - ), - Some( - ThermalStorageState(storageTick, storedEnergy, qDotStorage) - ), - ) => - houseTick shouldBe 0L - innerTemperature should approximate(Celsius(20.99999167d)) - qDotHouse should approximate(zeroKW) - - storageTick shouldBe 0L - storedEnergy should approximate( - gridState.storageState - .map(_.storedEnergy) - .getOrElse(fail("No initial storage state found")) - ) - qDotStorage should approximate(externalQDot) - case _ => fail("Thermal grid state has been calculated wrong.") - } - reachedThreshold shouldBe Some( - StorageFull(276000L) - ) + storageTick shouldBe 0L + storedEnergy should approximate( + gridState.storageState + .map(_.storedEnergy) + .getOrElse(fail("No initial storage state found")) + ) + qDotStorage should approximate(externalQDot) + case _ => fail("Thermal grid state has been calculated wrong.") } + reachedThreshold shouldBe Some( + StorageFull(276000L) + ) } } } From 92e6c512f88c261af1e3216c4115f5512b7eeb7c Mon Sep 17 00:00:00 2001 From: danielfeismann Date: Sat, 23 Nov 2024 09:56:34 +0100 Subject: [PATCH 60/65] fix EmAgentIT --- .../edu/ie3/simona/agent/em/EmAgentIT.scala | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/src/test/scala/edu/ie3/simona/agent/em/EmAgentIT.scala b/src/test/scala/edu/ie3/simona/agent/em/EmAgentIT.scala index b43b73f129..37155b6cba 100644 --- a/src/test/scala/edu/ie3/simona/agent/em/EmAgentIT.scala +++ b/src/test/scala/edu/ie3/simona/agent/em/EmAgentIT.scala @@ -692,25 +692,6 @@ class EmAgentIT emResult.getQ should equalWithTolerance(0.000088285537.asMegaVar) } - scheduler.expectMessage(Completion(emAgentActivation, Some(26748))) - - /* TICK 26748 - LOAD: 0.000269 MW (unchanged) - PV: -0.000032 MW (unchanged) - Heat pump: Is turned on again and cannot be turned off - -> flex signal is no control -> 0.00485 MW - */ - - emAgentActivation ! Activation(26748) - - resultListener.expectMessageType[ParticipantResultEvent] match { - case ParticipantResultEvent(emResult: EmResult) => - emResult.getInputModel shouldBe emInput.getUuid - emResult.getTime shouldBe 26748.toDateTime - emResult.getP should equalWithTolerance(0.005086768.asMegaWatt) - emResult.getQ should equalWithTolerance(0.00107312004.asMegaVar) - } - scheduler.expectMessage(Completion(emAgentActivation, Some(28800))) } } From 481da32d59444f6eaf29b229db02a684f8996150 Mon Sep 17 00:00:00 2001 From: danielfeismann Date: Sat, 23 Nov 2024 09:56:57 +0100 Subject: [PATCH 61/65] fmt --- src/main/scala/edu/ie3/simona/model/participant/HpModel.scala | 2 +- src/main/scala/edu/ie3/simona/model/thermal/ThermalGrid.scala | 2 +- 2 files changed, 2 insertions(+), 2 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 74f80350d5..e32757d767 100644 --- a/src/main/scala/edu/ie3/simona/model/participant/HpModel.scala +++ b/src/main/scala/edu/ie3/simona/model/participant/HpModel.scala @@ -264,7 +264,7 @@ final case class HpModel( if (isRunning) (pRated, pThermal) else if (lastStateStorageQDot < zeroKW) - (zeroKW, lastStateStorageQDot * -1) + (zeroKW, lastStateStorageQDot * (-1)) else if ( lastStateStorageQDot == zeroKW && (demandWrapper.houseDemand.hasRequiredDemand || demandWrapper.heatStorageDemand.hasRequiredDemand) ) 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 4e7b99ddc1..08b48eda2b 100644 --- a/src/main/scala/edu/ie3/simona/model/thermal/ThermalGrid.scala +++ b/src/main/scala/edu/ie3/simona/model/thermal/ThermalGrid.scala @@ -51,7 +51,7 @@ final case class ThermalGrid( * @param lastAmbientTemperature * Ambient temperature until this tick * @param ambientTemperature - * Ambient temperature in the instance in question + * Current ambient temperature in the instance in question * @param state * Currently applicable state of the thermal grid * @return From b03b5760daacd16a9d15a9de0192565b0f0d853d Mon Sep 17 00:00:00 2001 From: danielfeismann Date: Sat, 23 Nov 2024 09:57:56 +0100 Subject: [PATCH 62/65] correct direction of thermal power of thermal heat storage --- src/main/scala/edu/ie3/simona/model/participant/HpModel.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 e32757d767..92ad195af9 100644 --- a/src/main/scala/edu/ie3/simona/model/participant/HpModel.scala +++ b/src/main/scala/edu/ie3/simona/model/participant/HpModel.scala @@ -270,7 +270,7 @@ final case class HpModel( ) ( zeroKW, - thermalGrid.storage.map(_.getChargingPower: squants.Power).get * -1, + thermalGrid.storage.map(_.getChargingPower: squants.Power).get, ) else (zeroKW, zeroKW) } From 7b58297793ec0b70fc5e3ad41bde132d703064a0 Mon Sep 17 00:00:00 2001 From: danielfeismann Date: Sat, 23 Nov 2024 09:59:05 +0100 Subject: [PATCH 63/65] fix handleInfeed of thermal energy --- .../simona/model/thermal/ThermalGrid.scala | 36 ++++++++++++++----- 1 file changed, 28 insertions(+), 8 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 08b48eda2b..d94cf89d2c 100644 --- a/src/main/scala/edu/ie3/simona/model/thermal/ThermalGrid.scala +++ b/src/main/scala/edu/ie3/simona/model/thermal/ThermalGrid.scala @@ -240,8 +240,7 @@ final case class ThermalGrid( } if ( - // todo: Check if it has to be hasRequiredDemand or hasAdditionalDemand - (qDotHouseLastState > zeroKW && (qDotStorageLastState >= zeroKW)) | (qDotStorageLastState > zeroKW & thermalDemands.heatStorageDemand.hasAdditionalDemand) + (qDotHouseLastState > zeroKW && (qDotStorageLastState >= zeroKW)) | (qDotStorageLastState > zeroKW && thermalDemands.heatStorageDemand.hasAdditionalDemand) ) { val (updatedHouseState, thermalHouseThreshold, remainingQDotHouse) = handleInfeedHouse( @@ -280,7 +279,7 @@ final case class ThermalGrid( nextThreshold, ) } - // Handle edge case where house get heated from storage and HP will be activated in between + // Handle edge case where house was heated from storage and HP will be activated in between else if ((qDotHouseLastState > zeroKW && qDotStorageLastState < zeroKW)) { if (isRunning) { handleCases( @@ -302,14 +301,25 @@ final case class ThermalGrid( qDotStorageLastState, ) } + } + // Handle edge case where house should be heated from storage + else if ((!isRunning && qDot > zeroKW)) { + handleCases( + tick, + lastAmbientTemperature, + ambientTemperature, + state, + qDot, + -qDot, + ) } else { - // todo: Check if it has to be hasRequiredDemand or hasAdditionalDemand ( thermalDemands.houseDemand.hasRequiredDemand, - thermalDemands.heatStorageDemand.hasRequiredDemand, + thermalDemands.houseDemand.hasAdditionalDemand, + thermalDemands.heatStorageDemand.hasAdditionalDemand, ) match { - case (true, _) => + case (true, _, _) => // house first then heatStorage after heating House handleCases( tick, @@ -320,7 +330,7 @@ final case class ThermalGrid( zeroKW, ) - case (false, true) => + case (false, _, true) => handleCases( tick, lastAmbientTemperature, @@ -330,7 +340,17 @@ final case class ThermalGrid( qDot, ) - case (false, false) => + case (false, true, false) => + handleCases( + tick, + lastAmbientTemperature, + ambientTemperature, + state, + qDot, + zeroKW, + ) + + case (false, false, false) => handleCases( tick, lastAmbientTemperature, From 8d452ece6d9990b127edabfb254c355988168d4b Mon Sep 17 00:00:00 2001 From: danielfeismann Date: Sat, 23 Nov 2024 10:11:26 +0100 Subject: [PATCH 64/65] enhance ThermalGridSpec when checking for required and additional demand --- .../edu/ie3/simona/model/thermal/ThermalGridSpec.scala | 9 +++++++++ 1 file changed, 9 insertions(+) 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..1a59aede8b 100644 --- a/src/test/scala/edu/ie3/simona/model/thermal/ThermalGridSpec.scala +++ b/src/test/scala/edu/ie3/simona/model/thermal/ThermalGridSpec.scala @@ -51,6 +51,15 @@ class ThermalGridSpec extends UnitSpec { } "checking for required and additional demand" should { + "return proper information, if no required and no additional demand is apparent" in { + val required = MegawattHours(0d) + val possible = MegawattHours(0d) + + val energyDemand = ThermalEnergyDemand(required, possible) + energyDemand.hasRequiredDemand shouldBe false + energyDemand.hasAdditionalDemand shouldBe false + } + "return proper information, if no required but additional demand is apparent" in { val required = MegawattHours(0d) val possible = MegawattHours(45d) From 07746a48d067c7ab5ca49fabbfcf071e5386f65c Mon Sep 17 00:00:00 2001 From: danielfeismann Date: Sat, 23 Nov 2024 10:12:29 +0100 Subject: [PATCH 65/65] fix inputs for thermalGridWith specs plus add another test case --- .../ThermalGridWithHouseAndStorageSpec.scala | 4 +- .../ThermalGridWithStorageOnlySpec.scala | 41 ++++++++++++++++++- 2 files changed, 42 insertions(+), 3 deletions(-) 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 6cd7558e3d..39bc5e6c18 100644 --- a/src/test/scala/edu/ie3/simona/model/thermal/ThermalGridWithHouseAndStorageSpec.scala +++ b/src/test/scala/edu/ie3/simona/model/thermal/ThermalGridWithHouseAndStorageSpec.scala @@ -502,7 +502,7 @@ class ThermalGridWithHouseAndStorageSpec testGridAmbientTemperature, testGridAmbientTemperature, initialGridState, - isNotRunning, + isRunning, externalQDot, onlyThermalDemandOfHouse, ) @@ -550,7 +550,7 @@ class ThermalGridWithHouseAndStorageSpec testGridAmbientTemperature, testGridAmbientTemperature, gridState, - isNotRunning, + isRunning, externalQDot, onlyThermalDemandOfHeatStorage, ) 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 c4e5339a54..8f0ba6cdfe 100644 --- a/src/test/scala/edu/ie3/simona/model/thermal/ThermalGridWithStorageOnlySpec.scala +++ b/src/test/scala/edu/ie3/simona/model/thermal/ThermalGridWithStorageOnlySpec.scala @@ -187,7 +187,7 @@ class ThermalGridWithStorageOnlySpec testGridAmbientTemperature, testGridAmbientTemperature, gridState, - isNotRunning, + isRunning, testGridQDotInfeed, onlyThermalDemandOfHeatStorage, ) @@ -204,6 +204,45 @@ class ThermalGridWithStorageOnlySpec } reachedThreshold shouldBe Some(StorageFull(276000L)) } + + "properly take energy from storage" in { + val tick = 0L + val gridState = ThermalGrid + .startingState(thermalGrid) + .copy(storageState = + Some( + ThermalStorageState( + 0L, + KilowattHours(150d), + zeroKW, + ) + ) + ) + + val (updatedGridState, reachedThreshold) = + thermalGrid invokePrivate handleInfeed( + tick, + testGridAmbientTemperature, + testGridAmbientTemperature, + gridState, + isNotRunning, + testGridQDotInfeed, + onlyThermalDemandOfHeatStorage, + ) + + updatedGridState match { + case ThermalGridState( + None, + Some(ThermalStorageState(tick, storedEnergy, qDot)), + ) => + tick shouldBe 0L + storedEnergy should approximate(KilowattHours(150d)) + qDot should approximate(testGridQDotInfeed * (-1)) + case _ => fail("Thermal grid state has been calculated wrong.") + } + reachedThreshold shouldBe Some(StorageEmpty(36000L)) + } + } "updating the grid state dependent on the given thermal infeed" should {