From 891ab8885e9ce79ef462aaaa58f3c66eec664083 Mon Sep 17 00:00:00 2001 From: danielfeismann Date: Wed, 17 Jul 2024 14:44:38 +0200 Subject: [PATCH 01/31] add maybeThermalThreshold to nextActivationTicks --- .../participant/hp/HpAgentFundamentals.scala | 22 +++++++++++++++++-- 1 file changed, 20 insertions(+), 2 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 9add5d8ddc..fa7281857d 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 @@ -261,8 +261,26 @@ trait HpAgentFundamentals currentTick, updatedState, ) - val updatedBaseStateData = - baseStateData.copy(stateDataStore = updatedStateDataStore) + + val updatedBaseStateData = { + updatedState.maybeThermalThreshold match { + case Some(nextThreshold) + if baseStateData.foreseenDataTicks.headOption.exists { + case (_, Some(tick)) => nextThreshold.tick < tick + case _ => false + } => + baseStateData.copy( + stateDataStore = updatedStateDataStore, + additionalActivationTicks = + baseStateData.additionalActivationTicks + nextThreshold.tick, + ) + case _ => + baseStateData.copy( + stateDataStore = updatedStateDataStore + ) + } + } + updateValueStoresInformListenersAndGoToIdleWithUpdatedBaseStateData( scheduler, updatedBaseStateData, From 5b9f652d01e5fb561e0608d6e905ca297b81c2a1 Mon Sep 17 00:00:00 2001 From: danielfeismann Date: Wed, 17 Jul 2024 14:56:12 +0200 Subject: [PATCH 02/31] changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 448476f1fa..4ed3db22f8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -77,6 +77,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Not stopping correctly on failed power flow if configured to stop [#800](https://github.com/ie3-institute/simona/issues/800) - Finally fixing `RuntimeEventListenerSpec` [#849](https://github.com/ie3-institute/simona/issues/849) - Fixed result output for thermal houses and cylindrical storages [#844](https://github.com/ie3-institute/simona/issues/844) +- Fixed Hp results leading to overheating house and other effects [#827](https://github.com/ie3-institute/simona/issues/827) ## [3.0.0] - 2023-08-07 From 989b7b61dc600fedeffc2a0347ba599d59a8aaaa Mon Sep 17 00:00:00 2001 From: danielfeismann Date: Wed, 17 Jul 2024 16:17:05 +0200 Subject: [PATCH 03/31] provide heat pump the latest entry of weather data --- .../edu/ie3/simona/agent/ValueStore.scala | 2 +- .../participant/hp/HpAgentFundamentals.scala | 43 +++++++++++++------ 2 files changed, 32 insertions(+), 13 deletions(-) diff --git a/src/main/scala/edu/ie3/simona/agent/ValueStore.scala b/src/main/scala/edu/ie3/simona/agent/ValueStore.scala index a21f7d0f58..fbae2ff77c 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, - private val store: SortedMap[Long, D] = SortedMap.empty[Long, D], + store: SortedMap[Long, D] = SortedMap.empty[Long, D], ) { /** Determine the lastly known data tick, if available. Includes the given 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 fa7281857d..e5d8e9601e 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 @@ -403,25 +403,44 @@ trait HpAgentFundamentals tick: Long, ): HpRelevantData = { /* extract weather data from secondary data, which should have been requested and received before */ - val weatherData = - baseStateData.receivedSecondaryDataStore + val weatherData = { + val currentWeatherData = baseStateData.receivedSecondaryDataStore .last(tick) .flatMap { case (receivedTick, receivedValues) => - 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 + 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 + } } } - .getOrElse( + .getOrElse { throw new InconsistentStateException( s"The model ${baseStateData.model} was not provided with needed weather data." ) - ) + } + } HpRelevantData( tick, From 8b41023e31584e9d15fb3b87ebffd2b2cead9fe4 Mon Sep 17 00:00:00 2001 From: danielfeismann Date: Wed, 17 Jul 2024 20:35:56 +0200 Subject: [PATCH 04/31] adapt handleInfeed to check for lastState qDot --- .../simona/model/thermal/ThermalGrid.scala | 207 ++++++++++++------ 1 file changed, 137 insertions(+), 70 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 85b41b35ff..b42efc0410 100644 --- a/src/main/scala/edu/ie3/simona/model/thermal/ThermalGrid.scala +++ b/src/main/scala/edu/ie3/simona/model/thermal/ThermalGrid.scala @@ -140,94 +140,161 @@ final case class ThermalGrid( ambientTemperature: Temperature, state: ThermalGridState, 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 + ): (ThermalGridState, Option[ThermalThreshold]) = { + + /* Handle the case we had some infeed the last state of where running Hp was heating up house in the last state*/ + val (qDotHouseLastState, qDotStorageLastState) = + (state.houseState, state.storageState) match { + case (Some(house), Some(storage)) => (house.qDot, storage.qDot) + case (Some(house), None) => (house.qDot, zeroKW) + case (None, Some(storage)) => (zeroKW, storage.qDot) + case (None, None) => (zeroKW, zeroKW) + } + + /* + if (qDotHouseLastState > zeroKW | qDotStorageLastState > zeroKW) { + val updatedState = state.copy( + // Update houseState and storageState with the same values as in lastState + houseState = state.houseState.map(_.copy(qDot = qDotHouseLastState)), + storageState = state.storageState.map(_.copy(qDot = qDotStorageLastState)) + ) + + */ + + // When there is a significant qDot from the last state + if (qDotHouseLastState > zeroKW || qDotStorageLastState > zeroKW) { + + // Handle updated house state + val (updatedHouseState, maybeFullHouseThreshold) = + house.zip(state.houseState) match { + case Some((thermalHouse, lastHouseState)) => + thermalHouse.determineState( + tick, + lastHouseState, + ambientTemperature, + qDotHouseLastState, ) - case _ => state.storageState + case None => + throw new InconsistentStateException("No house state available") } - val (updatedHouseState, maybeHouseThreshold) = - thermalHouse.determineState( - tick, - lastHouseState, - ambientTemperature, - qDot, - ) + // Handle updated storage state + val (updatedStorageState, maybeStorageThreshold) = + storage.zip(state.storageState) match { + case Some((thermalStorage, lastStorageState)) => + thermalStorage.updateState( + tick, + qDotStorageLastState, + lastStorageState, + ) + case None => + throw new InconsistentStateException("No storage state available") + } - if ( - thermalHouse.isInnerTemperatureTooHigh( - updatedHouseState.innerTemperature - ) - ) { - /* The house is already heated up fully, set back the infeed and put it into storage, if available */ - val (fullHouseState, maybeFullHouseThreshold) = + // Determine the most recent threshold + val nextThreshold = determineMostRecentThreshold( + maybeFullHouseThreshold, + maybeStorageThreshold, + ) + + ( + state.copy( + houseState = Some(updatedHouseState), + storageState = Some(updatedStorageState), + ), + nextThreshold, + ) + } else { + + 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 + } + + val (updatedHouseState, maybeHouseThreshold) = thermalHouse.determineState( tick, lastHouseState, ambientTemperature, - zeroKW, + qDot, ) - 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, + if ( + thermalHouse.isInnerTemperatureTooHigh( + updatedHouseState.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, + lastHouseState, + 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, + ) + } + } else { + /* The house can handle the infeed */ + ( + state.copy(houseState = Some(updatedHouseState)), + maybeHouseThreshold, + ) + } + + case None => + storage.zip(state.storageState) match { + case Some((thermalStorage, storageState)) => + val (updatedStorageState, maybeStorageThreshold) = + thermalStorage.updateState(tick, qDot, storageState) ( - state.copy( - houseState = Some(fullHouseState), - storageState = Some(updatedStorageState), - ), - nextThreshold, + state.copy(storageState = Some(updatedStorageState)), + maybeStorageThreshold, ) case None => - /* There is no storage, house determines the next activation */ - ( - state.copy(houseState = Some(fullHouseState)), - maybeFullHouseThreshold, + throw new InconsistentStateException( + "A thermal grid has to contain either at least a house or a storage." ) } - } else { - /* The house can handle the infeed */ - ( - state.copy(houseState = Some(updatedHouseState)), - maybeHouseThreshold, - ) - } - - 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." - ) - } + } } + } private def determineMostRecentThreshold( maybeHouseThreshold: Option[ThermalThreshold], From e4457dcec765882405c0ee55ceff347ecc1cea8d Mon Sep 17 00:00:00 2001 From: danielfeismann Date: Fri, 19 Jul 2024 10:27:42 +0200 Subject: [PATCH 05/31] fix energyDemand evaluation for demand only below target temperature --- .../ie3/simona/model/thermal/ThermalGrid.scala | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/src/main/scala/edu/ie3/simona/model/thermal/ThermalGrid.scala b/src/main/scala/edu/ie3/simona/model/thermal/ThermalGrid.scala index b42efc0410..25e2cbfa2e 100644 --- a/src/main/scala/edu/ie3/simona/model/thermal/ThermalGrid.scala +++ b/src/main/scala/edu/ie3/simona/model/thermal/ThermalGrid.scala @@ -58,8 +58,18 @@ final case class ThermalGrid( ambientTemperature: Temperature, state: ThermalGridState, ): ThermalEnergyDemand = { - /* First get the energy demand of the houses */ - val houseDemand = house + /* First get the energy demand of the houses but only if below target temperature */ + + val innerTemperatureBelowTarget: Boolean = house.zip(state.houseState).headOption match { + case Some((thermalHouse, lastHouseState)) => + lastHouseState.innerTemperature < thermalHouse.targetTemperature + case None => + false // Default value when the collections cannot be zipped + } + + val houseDemand = + if (innerTemperatureBelowTarget) { + house .zip(state.houseState) .map { case (house, state) => house.energyDemand( @@ -68,7 +78,8 @@ final case class ThermalGrid( state, ) } - .getOrElse(ThermalEnergyDemand.noDemand) + .getOrElse(ThermalEnergyDemand.noDemand)} + else {ThermalEnergyDemand.noDemand} /* Then go over the storages, see what they can provide and what they might be able to charge */ val (storedEnergy, remainingCapacity) = { From 374a2eea02742c11f9afd81da7f76a0ca3a0878d Mon Sep 17 00:00:00 2001 From: danielfeismann Date: Fri, 19 Jul 2024 10:30:44 +0200 Subject: [PATCH 06/31] format and exception --- .../simona/model/thermal/ThermalGrid.scala | 144 +++++++++--------- 1 file changed, 75 insertions(+), 69 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 25e2cbfa2e..6a5ffeb8b6 100644 --- a/src/main/scala/edu/ie3/simona/model/thermal/ThermalGrid.scala +++ b/src/main/scala/edu/ie3/simona/model/thermal/ThermalGrid.scala @@ -58,28 +58,31 @@ final case class ThermalGrid( ambientTemperature: Temperature, state: ThermalGridState, ): ThermalEnergyDemand = { - /* First get the energy demand of the houses but only if below target temperature */ + /* First get the energy demand of the houses but only if inner temperature is below target temperature */ - val innerTemperatureBelowTarget: Boolean = house.zip(state.houseState).headOption match { - case Some((thermalHouse, lastHouseState)) => - lastHouseState.innerTemperature < thermalHouse.targetTemperature - case None => - false // Default value when the collections cannot be zipped - } + val innerTemperatureBelowTarget: Boolean = + house.zip(state.houseState).headOption match { + case Some((thermalHouse, lastHouseState)) => + lastHouseState.innerTemperature < thermalHouse.targetTemperature + case None => + throw new RuntimeException( + "Can't progress thermal house last state to determin energy demand." + ) + } val houseDemand = if (innerTemperatureBelowTarget) { - house - .zip(state.houseState) - .map { case (house, state) => - house.energyDemand( - tick, - ambientTemperature, - state, - ) - } - .getOrElse(ThermalEnergyDemand.noDemand)} - else {ThermalEnergyDemand.noDemand} + house + .zip(state.houseState) + .map { case (house, state) => + house.energyDemand( + tick, + ambientTemperature, + state, + ) + } + .getOrElse(ThermalEnergyDemand.noDemand) + } else { ThermalEnergyDemand.noDemand } /* Then go over the storages, see what they can provide and what they might be able to charge */ val (storedEnergy, remainingCapacity) = { @@ -153,7 +156,6 @@ final case class ThermalGrid( qDot: Power, ): (ThermalGridState, Option[ThermalThreshold]) = { - /* Handle the case we had some infeed the last state of where running Hp was heating up house in the last state*/ val (qDotHouseLastState, qDotStorageLastState) = (state.houseState, state.storageState) match { case (Some(house), Some(storage)) => (house.qDot, storage.qDot) @@ -162,65 +164,69 @@ final case class ThermalGrid( case (None, None) => (zeroKW, zeroKW) } - /* - if (qDotHouseLastState > zeroKW | qDotStorageLastState > zeroKW) { - val updatedState = state.copy( - // Update houseState and storageState with the same values as in lastState - houseState = state.houseState.map(_.copy(qDot = qDotHouseLastState)), - storageState = state.storageState.map(_.copy(qDot = qDotStorageLastState)) - ) - - */ - - // When there is a significant qDot from the last state - if (qDotHouseLastState > zeroKW || qDotStorageLastState > zeroKW) { + if (qDotStorageLastState > zeroKW) { + { + storage.zip(state.storageState) match { + case Some((thermalStorage, lastStorageState)) => + val ( + updatedHouseState: Option[ThermalHouseState], + maybeHouseThreshold: Option[ThermalThreshold], + ) = + /* Set thermal power exchange with house to zero */ + house.zip(state.houseState) match { + case Some((thermalHouse, houseState)) => + val nextHouseState = + thermalHouse.determineState( + tick, + houseState, + ambientTemperature, + zeroKW, + ) + (Some(nextHouseState._1), nextHouseState._2) + + case _ => state.houseState + } + + val (updatedStorageState, maybeStorageThreshold) = + thermalStorage.updateState( + tick, + qDot, + lastStorageState, + ) - // Handle updated house state - val (updatedHouseState, maybeFullHouseThreshold) = - house.zip(state.houseState) match { - case Some((thermalHouse, lastHouseState)) => - thermalHouse.determineState( - tick, - lastHouseState, - ambientTemperature, - qDotHouseLastState, + /* Both house and storage are updated. Determine what reaches the next threshold */ + val nextThreshold = determineMostRecentThreshold( + maybeHouseThreshold, + maybeStorageThreshold, ) - case None => - throw new InconsistentStateException("No house state available") - } - // Handle updated storage state - val (updatedStorageState, maybeStorageThreshold) = - storage.zip(state.storageState) match { - case Some((thermalStorage, lastStorageState)) => - thermalStorage.updateState( - tick, - qDotStorageLastState, - lastStorageState, + ( + state.copy( + houseState = updatedHouseState, + storageState = Some(updatedStorageState), + ), + nextThreshold, ) + case None => - throw new InconsistentStateException("No storage state available") + 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." + ) + } } - - // Determine the most recent threshold - val nextThreshold = determineMostRecentThreshold( - maybeFullHouseThreshold, - maybeStorageThreshold, - ) - - ( - state.copy( - houseState = Some(updatedHouseState), - storageState = Some(updatedStorageState), - ), - nextThreshold, - ) + } } else { - 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( From 280fed546b4de4463020a8bbdb7374e18d2538ce Mon Sep 17 00:00:00 2001 From: danielfeismann Date: Fri, 19 Jul 2024 10:32:49 +0200 Subject: [PATCH 07/31] Revert "format and exception" This reverts commit 374a2eea02742c11f9afd81da7f76a0ca3a0878d. --- .../simona/model/thermal/ThermalGrid.scala | 144 +++++++++--------- 1 file changed, 69 insertions(+), 75 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 6a5ffeb8b6..25e2cbfa2e 100644 --- a/src/main/scala/edu/ie3/simona/model/thermal/ThermalGrid.scala +++ b/src/main/scala/edu/ie3/simona/model/thermal/ThermalGrid.scala @@ -58,31 +58,28 @@ final case class ThermalGrid( ambientTemperature: Temperature, state: ThermalGridState, ): ThermalEnergyDemand = { - /* First get the energy demand of the houses but only if inner temperature is below target temperature */ + /* First get the energy demand of the houses but only if below target temperature */ - val innerTemperatureBelowTarget: Boolean = - house.zip(state.houseState).headOption match { - case Some((thermalHouse, lastHouseState)) => - lastHouseState.innerTemperature < thermalHouse.targetTemperature - case None => - throw new RuntimeException( - "Can't progress thermal house last state to determin energy demand." - ) - } + val innerTemperatureBelowTarget: Boolean = house.zip(state.houseState).headOption match { + case Some((thermalHouse, lastHouseState)) => + lastHouseState.innerTemperature < thermalHouse.targetTemperature + case None => + false // Default value when the collections cannot be zipped + } val houseDemand = if (innerTemperatureBelowTarget) { - house - .zip(state.houseState) - .map { case (house, state) => - house.energyDemand( - tick, - ambientTemperature, - state, - ) - } - .getOrElse(ThermalEnergyDemand.noDemand) - } else { ThermalEnergyDemand.noDemand } + house + .zip(state.houseState) + .map { case (house, state) => + house.energyDemand( + tick, + ambientTemperature, + state, + ) + } + .getOrElse(ThermalEnergyDemand.noDemand)} + else {ThermalEnergyDemand.noDemand} /* Then go over the storages, see what they can provide and what they might be able to charge */ val (storedEnergy, remainingCapacity) = { @@ -156,6 +153,7 @@ final case class ThermalGrid( qDot: Power, ): (ThermalGridState, Option[ThermalThreshold]) = { + /* Handle the case we had some infeed the last state of where running Hp was heating up house in the last state*/ val (qDotHouseLastState, qDotStorageLastState) = (state.houseState, state.storageState) match { case (Some(house), Some(storage)) => (house.qDot, storage.qDot) @@ -164,69 +162,65 @@ final case class ThermalGrid( case (None, None) => (zeroKW, zeroKW) } - if (qDotStorageLastState > zeroKW) { - { - storage.zip(state.storageState) match { - case Some((thermalStorage, lastStorageState)) => - val ( - updatedHouseState: Option[ThermalHouseState], - maybeHouseThreshold: Option[ThermalThreshold], - ) = - /* Set thermal power exchange with house to zero */ - house.zip(state.houseState) match { - case Some((thermalHouse, houseState)) => - val nextHouseState = - thermalHouse.determineState( - tick, - houseState, - ambientTemperature, - zeroKW, - ) - (Some(nextHouseState._1), nextHouseState._2) - - case _ => state.houseState - } - - val (updatedStorageState, maybeStorageThreshold) = - thermalStorage.updateState( - tick, - qDot, - lastStorageState, - ) + /* + if (qDotHouseLastState > zeroKW | qDotStorageLastState > zeroKW) { + val updatedState = state.copy( + // Update houseState and storageState with the same values as in lastState + houseState = state.houseState.map(_.copy(qDot = qDotHouseLastState)), + storageState = state.storageState.map(_.copy(qDot = qDotStorageLastState)) + ) - /* Both house and storage are updated. Determine what reaches the next threshold */ - val nextThreshold = determineMostRecentThreshold( - maybeHouseThreshold, - maybeStorageThreshold, - ) + */ - ( - state.copy( - houseState = updatedHouseState, - storageState = Some(updatedStorageState), - ), - nextThreshold, + // When there is a significant qDot from the last state + if (qDotHouseLastState > zeroKW || qDotStorageLastState > zeroKW) { + + // Handle updated house state + val (updatedHouseState, maybeFullHouseThreshold) = + house.zip(state.houseState) match { + case Some((thermalHouse, lastHouseState)) => + thermalHouse.determineState( + tick, + lastHouseState, + ambientTemperature, + qDotHouseLastState, ) + case None => + throw new InconsistentStateException("No house state available") + } + // Handle updated storage state + val (updatedStorageState, maybeStorageThreshold) = + storage.zip(state.storageState) match { + case Some((thermalStorage, lastStorageState)) => + thermalStorage.updateState( + tick, + qDotStorageLastState, + lastStorageState, + ) 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." - ) - } + throw new InconsistentStateException("No storage state available") } - } + + // Determine the most recent threshold + val nextThreshold = determineMostRecentThreshold( + maybeFullHouseThreshold, + maybeStorageThreshold, + ) + + ( + state.copy( + houseState = Some(updatedHouseState), + storageState = Some(updatedStorageState), + ), + nextThreshold, + ) } else { + 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( From f51dd61ca65f73a616207154a62e1fc8447a3311 Mon Sep 17 00:00:00 2001 From: danielfeismann Date: Fri, 19 Jul 2024 10:35:38 +0200 Subject: [PATCH 08/31] format and exception --- .../simona/model/thermal/ThermalGrid.scala | 39 ++++++++++--------- 1 file changed, 21 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 25e2cbfa2e..f2d8c5926c 100644 --- a/src/main/scala/edu/ie3/simona/model/thermal/ThermalGrid.scala +++ b/src/main/scala/edu/ie3/simona/model/thermal/ThermalGrid.scala @@ -58,28 +58,31 @@ final case class ThermalGrid( ambientTemperature: Temperature, state: ThermalGridState, ): ThermalEnergyDemand = { - /* First get the energy demand of the houses but only if below target temperature */ + /* First get the energy demand of the houses but only if inner temperature is below target temperature */ - val innerTemperatureBelowTarget: Boolean = house.zip(state.houseState).headOption match { - case Some((thermalHouse, lastHouseState)) => - lastHouseState.innerTemperature < thermalHouse.targetTemperature - case None => - false // Default value when the collections cannot be zipped - } + val innerTemperatureBelowTarget: Boolean = + house.zip(state.houseState).headOption match { + case Some((thermalHouse, lastHouseState)) => + lastHouseState.innerTemperature < thermalHouse.targetTemperature + case None => + throw new RuntimeException( + "Can't progress thermal house last state to determin energy demand." + ) + } val houseDemand = if (innerTemperatureBelowTarget) { - house - .zip(state.houseState) - .map { case (house, state) => - house.energyDemand( - tick, - ambientTemperature, - state, - ) - } - .getOrElse(ThermalEnergyDemand.noDemand)} - else {ThermalEnergyDemand.noDemand} + house + .zip(state.houseState) + .map { case (house, state) => + house.energyDemand( + tick, + ambientTemperature, + state, + ) + } + .getOrElse(ThermalEnergyDemand.noDemand) + } else { ThermalEnergyDemand.noDemand } /* Then go over the storages, see what they can provide and what they might be able to charge */ val (storedEnergy, remainingCapacity) = { From a297697f1532ae044aee2b3b843687f249a4b6ef Mon Sep 17 00:00:00 2001 From: danielfeismann Date: Fri, 19 Jul 2024 10:38:26 +0200 Subject: [PATCH 09/31] refactor handleInfeed to comply with case where thermal storage has been charged within the last state --- .../simona/model/thermal/ThermalGrid.scala | 106 ++++++++++-------- 1 file changed, 57 insertions(+), 49 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 f2d8c5926c..fe731a4f02 100644 --- a/src/main/scala/edu/ie3/simona/model/thermal/ThermalGrid.scala +++ b/src/main/scala/edu/ie3/simona/model/thermal/ThermalGrid.scala @@ -156,7 +156,6 @@ final case class ThermalGrid( qDot: Power, ): (ThermalGridState, Option[ThermalThreshold]) = { - /* Handle the case we had some infeed the last state of where running Hp was heating up house in the last state*/ val (qDotHouseLastState, qDotStorageLastState) = (state.houseState, state.storageState) match { case (Some(house), Some(storage)) => (house.qDot, storage.qDot) @@ -165,61 +164,70 @@ final case class ThermalGrid( case (None, None) => (zeroKW, zeroKW) } - /* - if (qDotHouseLastState > zeroKW | qDotStorageLastState > zeroKW) { - val updatedState = state.copy( - // Update houseState and storageState with the same values as in lastState - houseState = state.houseState.map(_.copy(qDot = qDotHouseLastState)), - storageState = state.storageState.map(_.copy(qDot = qDotStorageLastState)) - ) - - */ - - // When there is a significant qDot from the last state - if (qDotHouseLastState > zeroKW || qDotStorageLastState > zeroKW) { + if (qDotStorageLastState > zeroKW) { + { + storage.zip(state.storageState) match { + case Some((thermalStorage, lastStorageState)) => + val ( + updatedHouseState: Option[ThermalHouseState], + maybeHouseThreshold: Option[ThermalThreshold], + ) = + /* Set thermal power exchange with house to zero */ + house.zip(state.houseState) match { + case Some((thermalHouse, houseState)) => + val nextHouseState = + thermalHouse.determineState( + tick, + houseState, + ambientTemperature, + zeroKW, + ) + (Some(nextHouseState._1), nextHouseState._2) + + case _ => state.houseState + } + + val (updatedStorageState, maybeStorageThreshold) = + thermalStorage.updateState( + tick, + qDot, + lastStorageState, + ) - // Handle updated house state - val (updatedHouseState, maybeFullHouseThreshold) = - house.zip(state.houseState) match { - case Some((thermalHouse, lastHouseState)) => - thermalHouse.determineState( - tick, - lastHouseState, - ambientTemperature, - qDotHouseLastState, + /* Both house and storage are updated. Determine what reaches the next threshold */ + val nextThreshold = determineMostRecentThreshold( + maybeHouseThreshold, + maybeStorageThreshold, ) - case None => - throw new InconsistentStateException("No house state available") - } - // Handle updated storage state - val (updatedStorageState, maybeStorageThreshold) = - storage.zip(state.storageState) match { - case Some((thermalStorage, lastStorageState)) => - thermalStorage.updateState( - tick, - qDotStorageLastState, - lastStorageState, + ( + state.copy( + houseState = updatedHouseState, + storageState = Some(updatedStorageState), + ), + nextThreshold, ) + case None => - throw new InconsistentStateException("No storage state available") + 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." + ) + } } + } + } - // Determine the most recent threshold - val nextThreshold = determineMostRecentThreshold( - maybeFullHouseThreshold, - maybeStorageThreshold, - ) - - ( - state.copy( - houseState = Some(updatedHouseState), - storageState = Some(updatedStorageState), - ), - nextThreshold, - ) - } else { - + /* Storage was not charged in the last state */ + else { house.zip(state.houseState) match { case Some((thermalHouse, lastHouseState)) => /* Set thermal power exchange with storage to zero */ From 77941f961f4c7dbf69510625d31a7be47aa59b6c Mon Sep 17 00:00:00 2001 From: danielfeismann Date: Fri, 19 Jul 2024 11:37:14 +0200 Subject: [PATCH 10/31] fix HpModelSpec to check for targetTemperature --- .../edu/ie3/simona/model/participant/HpModelSpec.scala | 6 +++--- 1 file changed, 3 insertions(+), 3 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 ef2e082265..8b10ab0760 100644 --- a/src/test/scala/edu/ie3/simona/model/participant/HpModelSpec.scala +++ b/src/test/scala/edu/ie3/simona/model/participant/HpModelSpec.scala @@ -153,13 +153,13 @@ class HpModelSpec Some(hpData.ambientTemperature), Kilowatts(95d), Kilowatts(80d), - thermalState(Celsius(20)), + thermalState(Celsius(19.9)), None, ), true, 95, - 18.0, - Some(HouseTemperatureUpperBoundaryReached(27771L)), + 17.92, + Some(HouseTemperatureUpperBoundaryReached(27946L)), ), ( HpState( From cde52e46b2cc0ec619136ca071d71f0708b9f485 Mon Sep 17 00:00:00 2001 From: danielfeismann Date: Fri, 19 Jul 2024 12:01:53 +0200 Subject: [PATCH 11/31] adapt HpModelSpec to check for targetTemperature --- .../model/participant/HpModelSpec.scala | 40 ++++++++++++++++--- 1 file changed, 35 insertions(+), 5 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 8b10ab0760..c4f21fe8e3 100644 --- a/src/test/scala/edu/ie3/simona/model/participant/HpModelSpec.scala +++ b/src/test/scala/edu/ie3/simona/model/participant/HpModelSpec.scala @@ -78,13 +78,28 @@ class HpModelSpec Some(hpData.ambientTemperature), Kilowatts(0d), Kilowatts(0d), - thermalState(Celsius(20)), + thermalState(Celsius(19.9)), None, ), true, 95, + 17.92, + Some(HouseTemperatureUpperBoundaryReached(27946L)), + ), + ( + HpState( + isRunning = false, + 0, + Some(hpData.ambientTemperature), + Kilowatts(0d), + Kilowatts(0d), + thermalState(Celsius(20)), + None, + ), + false, + 0, 18.0, - Some(HouseTemperatureUpperBoundaryReached(27771L)), + None, ), ( HpState( @@ -161,6 +176,21 @@ class HpModelSpec 17.92, Some(HouseTemperatureUpperBoundaryReached(27946L)), ), + ( + HpState( + isRunning = true, + 0, + Some(hpData.ambientTemperature), + Kilowatts(95d), + Kilowatts(80d), + thermalState(Celsius(20.0)), + None, + ), + false, + 0, + 18.0, + None, + ), ( HpState( isRunning = true, @@ -171,10 +201,10 @@ class HpModelSpec thermalState(Celsius(22)), None, ), - true, - 95, + false, + 0, 19.6, - Some(HouseTemperatureUpperBoundaryReached(23200L)), + Some(HouseTemperatureLowerBoundaryReached(13200L)), ), ( HpState( From 6244e79c804365e1d5f4c144e8a21464b28897eb Mon Sep 17 00:00:00 2001 From: danielfeismann Date: Fri, 19 Jul 2024 12:15:45 +0200 Subject: [PATCH 12/31] fmt --- 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 fe731a4f02..91964472d8 100644 --- a/src/main/scala/edu/ie3/simona/model/thermal/ThermalGrid.scala +++ b/src/main/scala/edu/ie3/simona/model/thermal/ThermalGrid.scala @@ -66,7 +66,7 @@ final case class ThermalGrid( lastHouseState.innerTemperature < thermalHouse.targetTemperature case None => throw new RuntimeException( - "Can't progress thermal house last state to determin energy demand." + "Can't progress thermal house last state to determine energy demand." ) } From 81ef30dccd2253520eb1220d6de66e91c7593977 Mon Sep 17 00:00:00 2001 From: danielfeismann Date: Fri, 19 Jul 2024 15:23:03 +0200 Subject: [PATCH 13/31] refactor energyDemand --- .../simona/model/thermal/ThermalGrid.scala | 40 +++++++++++-------- 1 file changed, 24 insertions(+), 16 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 91964472d8..53d785effc 100644 --- a/src/main/scala/edu/ie3/simona/model/thermal/ThermalGrid.scala +++ b/src/main/scala/edu/ie3/simona/model/thermal/ThermalGrid.scala @@ -60,30 +60,38 @@ final case class ThermalGrid( ): ThermalEnergyDemand = { /* First get the energy demand of the houses but only if inner temperature is below target temperature */ - val innerTemperatureBelowTarget: Boolean = + val houseDemand = house.zip(state.houseState).headOption match { case Some((thermalHouse, lastHouseState)) => - lastHouseState.innerTemperature < thermalHouse.targetTemperature + val updatedState = thermalHouse.determineState( + tick, + lastHouseState, + ambientTemperature, + zeroKW, + ) + if ( + updatedState._1.innerTemperature < thermalHouse.targetTemperature + ) { + house + .zip(state.houseState) + .map { case (house, state) => + house.energyDemand( + tick, + ambientTemperature, + state, + ) + } + .getOrElse(ThermalEnergyDemand.noDemand) + } else { + ThermalEnergyDemand.noDemand + } + case None => throw new RuntimeException( "Can't progress thermal house last state to determine energy demand." ) } - val houseDemand = - if (innerTemperatureBelowTarget) { - house - .zip(state.houseState) - .map { case (house, state) => - house.energyDemand( - tick, - ambientTemperature, - state, - ) - } - .getOrElse(ThermalEnergyDemand.noDemand) - } else { ThermalEnergyDemand.noDemand } - /* Then go over the storages, see what they can provide and what they might be able to charge */ val (storedEnergy, remainingCapacity) = { storage From ab0806c5bb3e90fa2f14fb02b3673151cdc2794c Mon Sep 17 00:00:00 2001 From: danielfeismann Date: Fri, 19 Jul 2024 15:25:51 +0200 Subject: [PATCH 14/31] refactor EmAgentIT --- .../edu/ie3/simona/agent/em/EmAgentIT.scala | 69 ++++++++++++------- .../test/common/input/EmInputTestData.scala | 4 +- 2 files changed, 46 insertions(+), 27 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 09f6acb019..55ffc799ef 100644 --- a/src/test/scala/edu/ie3/simona/agent/em/EmAgentIT.scala +++ b/src/test/scala/edu/ie3/simona/agent/em/EmAgentIT.scala @@ -560,9 +560,9 @@ class EmAgentIT /* TICK 0 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 apparent power): turned on - -> remaining -0.000566 MW + Heat pump: off, cannot be turned on + -> set point 0 kW + -> remaining -0.005416 MW */ emAgentActivation ! Activation(0) @@ -586,9 +586,11 @@ class EmAgentIT emResult.getInputModel shouldBe emInput.getUuid emResult.getTime shouldBe 0.toDateTime emResult.getP should equalWithTolerance( - (-0.000566087824).asMegaWatt + (-0.005416087824603603).asMegaWatt + ) + emResult.getQ should equalWithTolerance( + 0.0000882855367033582.asMegaVar ) - emResult.getQ should equalWithTolerance(0.001073120041.asMegaVar) } scheduler.expectMessage(Completion(emAgentActivation, Some(7200))) @@ -596,8 +598,8 @@ class EmAgentIT /* TICK 7200 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 apparent power): stays turned on with unchanged state + Heat pump: not running, can also be turned on + -> set point ~3.5 kW (bigger than 50 % rated apparent power): turned on -> remaining 0 MW */ @@ -630,8 +632,8 @@ class EmAgentIT /* TICK 14400 LOAD: 0.000269 MW (unchanged) PV: -0.000066 MW - Heat pump: Is still running, can still be turned off - -> flex signal is 0 MW: Heat pump is turned off + Heat pump: Is still running, can not be turned off + -> flex signal is 0.00505 MW: Heat pump still runs */ emAgentActivation ! Activation(14400) @@ -655,8 +657,8 @@ class EmAgentIT case ParticipantResultEvent(emResult: EmResult) => emResult.getInputModel shouldBe emInput.getUuid emResult.getTime shouldBe 14400L.toDateTime - emResult.getP should equalWithTolerance(0.000202956264.asMegaWatt) - emResult.getQ should equalWithTolerance(0.000088285537.asMegaVar) + emResult.getP should equalWithTolerance(0.005052956264.asMegaWatt) + emResult.getQ should equalWithTolerance(0.001073120041.asMegaVar) } scheduler.expectMessage(Completion(emAgentActivation, Some(21600))) @@ -664,8 +666,8 @@ class EmAgentIT /* TICK 21600 LOAD: 0.000269 MW (unchanged) PV: -0.000032 MW - Heat pump: Is not running, can run or stay off - -> flex signal is 0 MW: Heat pump is turned off + Heat pump: Is running, can not be turned off + -> flex signal is 0 MW: Heat pump still running */ emAgentActivation ! Activation(21600) @@ -688,30 +690,47 @@ class EmAgentIT case ParticipantResultEvent(emResult: EmResult) => emResult.getInputModel shouldBe emInput.getUuid emResult.getTime shouldBe 21600.toDateTime - emResult.getP should equalWithTolerance(0.0002367679996.asMegaWatt) - emResult.getQ should equalWithTolerance(0.000088285537.asMegaVar) + emResult.getP should equalWithTolerance( + 0.00508676799571654.asMegaWatt + ) + emResult.getQ should equalWithTolerance( + 0.00107312004778272.asMegaVar + ) } - scheduler.expectMessage(Completion(emAgentActivation, Some(28665))) - - /* TICK 28666 + scheduler.expectMessage(Completion(emAgentActivation, Some(28800))) + /* TICK 28800 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 + PV: -0.00 MW + Heat pump: Is still running cannot be turned off + -> flex signal is no control -> 0.00471 MW */ - emAgentActivation ! Activation(28665) + emAgentActivation ! Activation(28800) + + weatherDependentAgents.foreach { + _ ! ProvideWeatherMessage( + 28800, + weatherService.ref.toClassic, + WeatherData( + WattsPerSquareMeter(0d), + WattsPerSquareMeter(0d), + Celsius(0d), + MetersPerSecond(0d), + ), + Some(36000), + ) + } 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.getTime shouldBe 28800.toDateTime + emResult.getP should equalWithTolerance(0.004717329596.asMegaWatt) emResult.getQ should equalWithTolerance(0.001073120040.asMegaVar) } - scheduler.expectMessage(Completion(emAgentActivation, Some(28800))) + scheduler.expectMessage(Completion(emAgentActivation, Some(36000))) } } 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 28cc5fcf15..0c3ab04821 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 @@ -142,10 +142,10 @@ trait EmInputTestData "thermal house", thermalBusInput, Quantities.getQuantity(0.325, StandardUnits.THERMAL_TRANSMISSION), - Quantities.getQuantity(75, StandardUnits.HEAT_CAPACITY), + Quantities.getQuantity(7.5, StandardUnits.HEAT_CAPACITY), Quantities.getQuantity(20.3, StandardUnits.TEMPERATURE), Quantities.getQuantity(22.0, StandardUnits.TEMPERATURE), - Quantities.getQuantity(20.0, StandardUnits.TEMPERATURE), + Quantities.getQuantity(18.0, StandardUnits.TEMPERATURE), ) val adaptedThermalGrid = new ThermalGrid( thermalBusInput, From 8c6aed67ef3055049248586dced14f990dacfc59 Mon Sep 17 00:00:00 2001 From: danielfeismann Date: Mon, 22 Jul 2024 09:00:53 +0200 Subject: [PATCH 15/31] rollback HpModelSpec --- .../model/participant/HpModelSpec.scala | 44 +++---------------- 1 file changed, 7 insertions(+), 37 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 c4f21fe8e3..ef2e082265 100644 --- a/src/test/scala/edu/ie3/simona/model/participant/HpModelSpec.scala +++ b/src/test/scala/edu/ie3/simona/model/participant/HpModelSpec.scala @@ -78,28 +78,13 @@ class HpModelSpec Some(hpData.ambientTemperature), Kilowatts(0d), Kilowatts(0d), - thermalState(Celsius(19.9)), + thermalState(Celsius(20)), None, ), true, 95, - 17.92, - Some(HouseTemperatureUpperBoundaryReached(27946L)), - ), - ( - HpState( - isRunning = false, - 0, - Some(hpData.ambientTemperature), - Kilowatts(0d), - Kilowatts(0d), - thermalState(Celsius(20)), - None, - ), - false, - 0, 18.0, - None, + Some(HouseTemperatureUpperBoundaryReached(27771L)), ), ( HpState( @@ -168,28 +153,13 @@ class HpModelSpec Some(hpData.ambientTemperature), Kilowatts(95d), Kilowatts(80d), - thermalState(Celsius(19.9)), + thermalState(Celsius(20)), None, ), true, 95, - 17.92, - Some(HouseTemperatureUpperBoundaryReached(27946L)), - ), - ( - HpState( - isRunning = true, - 0, - Some(hpData.ambientTemperature), - Kilowatts(95d), - Kilowatts(80d), - thermalState(Celsius(20.0)), - None, - ), - false, - 0, 18.0, - None, + Some(HouseTemperatureUpperBoundaryReached(27771L)), ), ( HpState( @@ -201,10 +171,10 @@ class HpModelSpec thermalState(Celsius(22)), None, ), - false, - 0, + true, + 95, 19.6, - Some(HouseTemperatureLowerBoundaryReached(13200L)), + Some(HouseTemperatureUpperBoundaryReached(23200L)), ), ( HpState( From 44873d291fe5268aec8b8734694762b491bf53e5 Mon Sep 17 00:00:00 2001 From: danielfeismann Date: Mon, 22 Jul 2024 09:01:04 +0200 Subject: [PATCH 16/31] fix energyDemand --- src/main/scala/edu/ie3/simona/model/thermal/ThermalGrid.scala | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/main/scala/edu/ie3/simona/model/thermal/ThermalGrid.scala b/src/main/scala/edu/ie3/simona/model/thermal/ThermalGrid.scala index 53d785effc..8c937d42c7 100644 --- a/src/main/scala/edu/ie3/simona/model/thermal/ThermalGrid.scala +++ b/src/main/scala/edu/ie3/simona/model/thermal/ThermalGrid.scala @@ -87,9 +87,7 @@ final case class ThermalGrid( } case None => - throw new RuntimeException( - "Can't progress thermal house last state to determine energy demand." - ) + ThermalEnergyDemand.noDemand } /* Then go over the storages, see what they can provide and what they might be able to charge */ From ec7d3f75c4fd471822651e472b56beb43df90b1c Mon Sep 17 00:00:00 2001 From: danielfeismann Date: Mon, 22 Jul 2024 10:33:48 +0200 Subject: [PATCH 17/31] update EmAgentIT --- .../edu/ie3/simona/agent/em/EmAgentIT.scala | 95 ++++++++----------- .../test/common/input/EmInputTestData.scala | 4 +- 2 files changed, 41 insertions(+), 58 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 55ffc799ef..526ff9e0f1 100644 --- a/src/test/scala/edu/ie3/simona/agent/em/EmAgentIT.scala +++ b/src/test/scala/edu/ie3/simona/agent/em/EmAgentIT.scala @@ -560,9 +560,9 @@ class EmAgentIT /* TICK 0 LOAD: 0.000269 MW PV: -0.005685 MW - Heat pump: off, cannot be turned on - -> set point 0 kW - -> remaining -0.005416 MW + Heat pump: off, can be turned on or stay off + -> set point ~3.5 kW (bigger than 50 % rated apparent power): turned on + -> remaining -0.000566 MW */ emAgentActivation ! Activation(0) @@ -586,11 +586,9 @@ class EmAgentIT emResult.getInputModel shouldBe emInput.getUuid emResult.getTime shouldBe 0.toDateTime emResult.getP should equalWithTolerance( - (-0.005416087824603603).asMegaWatt - ) - emResult.getQ should equalWithTolerance( - 0.0000882855367033582.asMegaVar + (-0.000566087824).asMegaWatt ) + emResult.getQ should equalWithTolerance(0.001073120041.asMegaVar) } scheduler.expectMessage(Completion(emAgentActivation, Some(7200))) @@ -598,9 +596,8 @@ class EmAgentIT /* TICK 7200 LOAD: 0.000269 MW (unchanged) PV: -0.003797 MW - Heat pump: not running, can also be turned on - -> set point ~3.5 kW (bigger than 50 % rated apparent power): turned on - -> remaining 0 MW + Heat pump: running (turned on from last request), must be turned off + -> remaining -0.003528 MW */ emAgentActivation ! Activation(7200) @@ -623,8 +620,8 @@ class EmAgentIT case ParticipantResultEvent(emResult: EmResult) => emResult.getInputModel shouldBe emInput.getUuid emResult.getTime shouldBe 7200.toDateTime - emResult.getP should equalWithTolerance(0.00132184544484.asMegaWatt) - emResult.getQ should equalWithTolerance(0.001073120041.asMegaVar) + emResult.getP should equalWithTolerance(-0.003528154555.asMegaWatt) + emResult.getQ should equalWithTolerance(0.0000882855367.asMegaVar) } scheduler.expectMessage(Completion(emAgentActivation, Some(14400))) @@ -632,8 +629,8 @@ class EmAgentIT /* TICK 14400 LOAD: 0.000269 MW (unchanged) PV: -0.000066 MW - Heat pump: Is still running, can not be turned off - -> flex signal is 0.00505 MW: Heat pump still runs + Heat pump: Is not running, //FIXME: can still be turned off + -> flex signal is 0 MW: Heat pump stays off */ emAgentActivation ! Activation(14400) @@ -657,6 +654,25 @@ class EmAgentIT case ParticipantResultEvent(emResult: EmResult) => emResult.getInputModel shouldBe emInput.getUuid emResult.getTime shouldBe 14400L.toDateTime + emResult.getP should equalWithTolerance(0.000202956264.asMegaWatt) + emResult.getQ should equalWithTolerance(0.000088285537.asMegaVar) + } + + scheduler.expectMessage(Completion(emAgentActivation, Some(20498))) + + /* TICK 20498 + LOAD: 0.000269 MW (unchanged) + PV: -0.000032 MW (unchanged) + FIXME Heat pump: Is not running, can run or stay off + FIXME -> flex signal is 0 MW: Heat pump is turned off + */ + + emAgentActivation ! Activation(20498) + + resultListener.expectMessageType[ParticipantResultEvent] match { + case ParticipantResultEvent(emResult: EmResult) => + emResult.getInputModel shouldBe emInput.getUuid + emResult.getTime shouldBe 20498.toDateTime emResult.getP should equalWithTolerance(0.005052956264.asMegaWatt) emResult.getQ should equalWithTolerance(0.001073120041.asMegaVar) } @@ -664,11 +680,13 @@ class EmAgentIT scheduler.expectMessage(Completion(emAgentActivation, Some(21600))) /* TICK 21600 - LOAD: 0.000269 MW (unchanged) - PV: -0.000032 MW - Heat pump: Is running, can not be turned off - -> flex signal is 0 MW: Heat pump still running - */ + LOAD: 0.000269 MW (unchanged) + PV: -0.000032 MW (unchanged) + FIXME Heat pump: Is not running, can run or stay off + FIXME -> flex signal is 0 MW: Heat pump is turned off + Heat pump: Is turned on again and cannot be turned off + -> flex signal is no control -> 0.00485 MW + */ emAgentActivation ! Activation(21600) @@ -690,49 +708,14 @@ class EmAgentIT case ParticipantResultEvent(emResult: EmResult) => emResult.getInputModel shouldBe emInput.getUuid emResult.getTime shouldBe 21600.toDateTime - emResult.getP should equalWithTolerance( - 0.00508676799571654.asMegaWatt - ) - emResult.getQ should equalWithTolerance( - 0.00107312004778272.asMegaVar - ) + emResult.getP should equalWithTolerance(0.005086767999.asMegaWatt) + emResult.getQ should equalWithTolerance(0.001073120041.asMegaVar) } scheduler.expectMessage(Completion(emAgentActivation, Some(28800))) - /* TICK 28800 - LOAD: 0.000269 MW (unchanged) - PV: -0.00 MW - Heat pump: Is still running cannot be turned off - -> flex signal is no control -> 0.00471 MW - */ - emAgentActivation ! Activation(28800) - weatherDependentAgents.foreach { - _ ! ProvideWeatherMessage( - 28800, - weatherService.ref.toClassic, - WeatherData( - WattsPerSquareMeter(0d), - WattsPerSquareMeter(0d), - Celsius(0d), - MetersPerSecond(0d), - ), - Some(36000), - ) - } - - resultListener.expectMessageType[ParticipantResultEvent] match { - case ParticipantResultEvent(emResult: EmResult) => - emResult.getInputModel shouldBe emInput.getUuid - emResult.getTime shouldBe 28800.toDateTime - emResult.getP should equalWithTolerance(0.004717329596.asMegaWatt) - emResult.getQ should equalWithTolerance(0.001073120040.asMegaVar) - } - - scheduler.expectMessage(Completion(emAgentActivation, Some(36000))) } } - } } 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 0c3ab04821..28cc5fcf15 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 @@ -142,10 +142,10 @@ trait EmInputTestData "thermal house", thermalBusInput, Quantities.getQuantity(0.325, StandardUnits.THERMAL_TRANSMISSION), - Quantities.getQuantity(7.5, StandardUnits.HEAT_CAPACITY), + Quantities.getQuantity(75, StandardUnits.HEAT_CAPACITY), Quantities.getQuantity(20.3, StandardUnits.TEMPERATURE), Quantities.getQuantity(22.0, StandardUnits.TEMPERATURE), - Quantities.getQuantity(18.0, StandardUnits.TEMPERATURE), + Quantities.getQuantity(20.0, StandardUnits.TEMPERATURE), ) val adaptedThermalGrid = new ThermalGrid( thermalBusInput, From 5dcc2301371e0811b753f6da9bf543593a026ffc Mon Sep 17 00:00:00 2001 From: danielfeismann Date: Mon, 22 Jul 2024 11:38:50 +0200 Subject: [PATCH 18/31] adapt EmAgentIT --- .../scala/edu/ie3/simona/agent/em/EmAgentIT.scala | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 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 526ff9e0f1..b32e5151f4 100644 --- a/src/test/scala/edu/ie3/simona/agent/em/EmAgentIT.scala +++ b/src/test/scala/edu/ie3/simona/agent/em/EmAgentIT.scala @@ -63,6 +63,8 @@ class EmAgentIT with EmInputTestData with MockitoSugar { + // implicit val messageTimeout: Duration = Duration(50.0, SECONDS) + // start a bit later so the sun is up protected implicit val simulationStartDate: ZonedDateTime = TimeUtil.withDefaults.toZonedDateTime("2020-01-01T10:00:00Z") @@ -629,7 +631,7 @@ class EmAgentIT /* TICK 14400 LOAD: 0.000269 MW (unchanged) PV: -0.000066 MW - Heat pump: Is not running, //FIXME: can still be turned off + Heat pump: Is not running, can be turned on -> flex signal is 0 MW: Heat pump stays off */ @@ -663,8 +665,8 @@ class EmAgentIT /* TICK 20498 LOAD: 0.000269 MW (unchanged) PV: -0.000032 MW (unchanged) - FIXME Heat pump: Is not running, can run or stay off - FIXME -> flex signal is 0 MW: Heat pump is turned off + Heat pump: Is not running, since lower temp boundary is reached: Hp is turned on + -> flex signal is no control -> 0.00485 MW */ emAgentActivation ! Activation(20498) @@ -682,11 +684,9 @@ class EmAgentIT /* TICK 21600 LOAD: 0.000269 MW (unchanged) PV: -0.000032 MW (unchanged) - FIXME Heat pump: Is not running, can run or stay off + Heat pump: Is running and cannot be turned off FIXME -> flex signal is 0 MW: Heat pump is turned off - Heat pump: Is turned on again and cannot be turned off - -> flex signal is no control -> 0.00485 MW - */ + */ emAgentActivation ! Activation(21600) @@ -714,7 +714,6 @@ class EmAgentIT scheduler.expectMessage(Completion(emAgentActivation, Some(28800))) - } } } From a3959cb833d9c847c3a30e8f175328fafb06dbdc Mon Sep 17 00:00:00 2001 From: danielfeismann Date: Mon, 22 Jul 2024 15:18:10 +0200 Subject: [PATCH 19/31] rollback EmAgentIT changes --- .../edu/ie3/simona/agent/em/EmAgentIT.scala | 67 +++++++++---------- 1 file changed, 33 insertions(+), 34 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 b32e5151f4..09f6acb019 100644 --- a/src/test/scala/edu/ie3/simona/agent/em/EmAgentIT.scala +++ b/src/test/scala/edu/ie3/simona/agent/em/EmAgentIT.scala @@ -63,8 +63,6 @@ class EmAgentIT with EmInputTestData with MockitoSugar { - // implicit val messageTimeout: Duration = Duration(50.0, SECONDS) - // start a bit later so the sun is up protected implicit val simulationStartDate: ZonedDateTime = TimeUtil.withDefaults.toZonedDateTime("2020-01-01T10:00:00Z") @@ -598,8 +596,9 @@ class EmAgentIT /* TICK 7200 LOAD: 0.000269 MW (unchanged) PV: -0.003797 MW - Heat pump: running (turned on from last request), must be turned off - -> remaining -0.003528 MW + Heat pump: running (turned on from last request), can also be turned off + -> set point ~3.5 kW (bigger than 50 % rated apparent power): stays turned on with unchanged state + -> remaining 0 MW */ emAgentActivation ! Activation(7200) @@ -622,8 +621,8 @@ class EmAgentIT case ParticipantResultEvent(emResult: EmResult) => emResult.getInputModel shouldBe emInput.getUuid emResult.getTime shouldBe 7200.toDateTime - emResult.getP should equalWithTolerance(-0.003528154555.asMegaWatt) - emResult.getQ should equalWithTolerance(0.0000882855367.asMegaVar) + emResult.getP should equalWithTolerance(0.00132184544484.asMegaWatt) + emResult.getQ should equalWithTolerance(0.001073120041.asMegaVar) } scheduler.expectMessage(Completion(emAgentActivation, Some(14400))) @@ -631,8 +630,8 @@ class EmAgentIT /* TICK 14400 LOAD: 0.000269 MW (unchanged) PV: -0.000066 MW - Heat pump: Is not running, can be turned on - -> flex signal is 0 MW: Heat pump stays off + Heat pump: Is still running, can still be turned off + -> flex signal is 0 MW: Heat pump is turned off */ emAgentActivation ! Activation(14400) @@ -660,32 +659,13 @@ class EmAgentIT emResult.getQ should equalWithTolerance(0.000088285537.asMegaVar) } - scheduler.expectMessage(Completion(emAgentActivation, Some(20498))) - - /* TICK 20498 - LOAD: 0.000269 MW (unchanged) - PV: -0.000032 MW (unchanged) - Heat pump: Is not running, since lower temp boundary is reached: Hp is turned on - -> flex signal is no control -> 0.00485 MW - */ - - emAgentActivation ! Activation(20498) - - resultListener.expectMessageType[ParticipantResultEvent] match { - case ParticipantResultEvent(emResult: EmResult) => - emResult.getInputModel shouldBe emInput.getUuid - emResult.getTime shouldBe 20498.toDateTime - emResult.getP should equalWithTolerance(0.005052956264.asMegaWatt) - emResult.getQ should equalWithTolerance(0.001073120041.asMegaVar) - } - scheduler.expectMessage(Completion(emAgentActivation, Some(21600))) /* TICK 21600 - LOAD: 0.000269 MW (unchanged) - PV: -0.000032 MW (unchanged) - Heat pump: Is running and cannot be turned off - FIXME -> flex signal is 0 MW: Heat pump is turned off + LOAD: 0.000269 MW (unchanged) + PV: -0.000032 MW + Heat pump: Is not running, can run or stay off + -> flex signal is 0 MW: Heat pump is turned off */ emAgentActivation ! Activation(21600) @@ -708,13 +688,32 @@ class EmAgentIT case ParticipantResultEvent(emResult: EmResult) => emResult.getInputModel shouldBe emInput.getUuid emResult.getTime shouldBe 21600.toDateTime - emResult.getP should equalWithTolerance(0.005086767999.asMegaWatt) - emResult.getQ should equalWithTolerance(0.001073120041.asMegaVar) + emResult.getP should equalWithTolerance(0.0002367679996.asMegaWatt) + emResult.getQ should equalWithTolerance(0.000088285537.asMegaVar) } - scheduler.expectMessage(Completion(emAgentActivation, Some(28800))) + scheduler.expectMessage(Completion(emAgentActivation, Some(28665))) + + /* TICK 28666 + 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) + + 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) + } + + scheduler.expectMessage(Completion(emAgentActivation, Some(28800))) } } + } } From 07574aa877d7600045e721ed6816ff4a1aa64571 Mon Sep 17 00:00:00 2001 From: danielfeismann Date: Mon, 22 Jul 2024 15:20:23 +0200 Subject: [PATCH 20/31] Revert "rollback EmAgentIT changes" This reverts commit a3959cb833d9c847c3a30e8f175328fafb06dbdc. --- .../edu/ie3/simona/agent/em/EmAgentIT.scala | 67 ++++++++++--------- 1 file changed, 34 insertions(+), 33 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 09f6acb019..b32e5151f4 100644 --- a/src/test/scala/edu/ie3/simona/agent/em/EmAgentIT.scala +++ b/src/test/scala/edu/ie3/simona/agent/em/EmAgentIT.scala @@ -63,6 +63,8 @@ class EmAgentIT with EmInputTestData with MockitoSugar { + // implicit val messageTimeout: Duration = Duration(50.0, SECONDS) + // start a bit later so the sun is up protected implicit val simulationStartDate: ZonedDateTime = TimeUtil.withDefaults.toZonedDateTime("2020-01-01T10:00:00Z") @@ -596,9 +598,8 @@ class EmAgentIT /* TICK 7200 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 apparent power): stays turned on with unchanged state - -> remaining 0 MW + Heat pump: running (turned on from last request), must be turned off + -> remaining -0.003528 MW */ emAgentActivation ! Activation(7200) @@ -621,8 +622,8 @@ class EmAgentIT case ParticipantResultEvent(emResult: EmResult) => emResult.getInputModel shouldBe emInput.getUuid emResult.getTime shouldBe 7200.toDateTime - emResult.getP should equalWithTolerance(0.00132184544484.asMegaWatt) - emResult.getQ should equalWithTolerance(0.001073120041.asMegaVar) + emResult.getP should equalWithTolerance(-0.003528154555.asMegaWatt) + emResult.getQ should equalWithTolerance(0.0000882855367.asMegaVar) } scheduler.expectMessage(Completion(emAgentActivation, Some(14400))) @@ -630,8 +631,8 @@ class EmAgentIT /* TICK 14400 LOAD: 0.000269 MW (unchanged) PV: -0.000066 MW - Heat pump: Is still running, can still be turned off - -> flex signal is 0 MW: Heat pump is turned off + Heat pump: Is not running, can be turned on + -> flex signal is 0 MW: Heat pump stays off */ emAgentActivation ! Activation(14400) @@ -659,13 +660,32 @@ class EmAgentIT emResult.getQ should equalWithTolerance(0.000088285537.asMegaVar) } + scheduler.expectMessage(Completion(emAgentActivation, Some(20498))) + + /* TICK 20498 + LOAD: 0.000269 MW (unchanged) + PV: -0.000032 MW (unchanged) + Heat pump: Is not running, since lower temp boundary is reached: Hp is turned on + -> flex signal is no control -> 0.00485 MW + */ + + emAgentActivation ! Activation(20498) + + resultListener.expectMessageType[ParticipantResultEvent] match { + case ParticipantResultEvent(emResult: EmResult) => + emResult.getInputModel shouldBe emInput.getUuid + emResult.getTime shouldBe 20498.toDateTime + emResult.getP should equalWithTolerance(0.005052956264.asMegaWatt) + emResult.getQ should equalWithTolerance(0.001073120041.asMegaVar) + } + scheduler.expectMessage(Completion(emAgentActivation, Some(21600))) /* TICK 21600 - LOAD: 0.000269 MW (unchanged) - PV: -0.000032 MW - Heat pump: Is not running, can run or stay off - -> flex signal is 0 MW: Heat pump is turned off + LOAD: 0.000269 MW (unchanged) + PV: -0.000032 MW (unchanged) + Heat pump: Is running and cannot be turned off + FIXME -> flex signal is 0 MW: Heat pump is turned off */ emAgentActivation ! Activation(21600) @@ -688,32 +708,13 @@ class EmAgentIT case ParticipantResultEvent(emResult: EmResult) => emResult.getInputModel shouldBe emInput.getUuid emResult.getTime shouldBe 21600.toDateTime - emResult.getP should equalWithTolerance(0.0002367679996.asMegaWatt) - emResult.getQ should equalWithTolerance(0.000088285537.asMegaVar) - } - - scheduler.expectMessage(Completion(emAgentActivation, Some(28665))) - - /* TICK 28666 - 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) - - 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.getP should equalWithTolerance(0.005086767999.asMegaWatt) + emResult.getQ should equalWithTolerance(0.001073120041.asMegaVar) } scheduler.expectMessage(Completion(emAgentActivation, Some(28800))) + } } - } } From 1d7ae94fdcdd0c8a61f0367c19b3ad398a8cb561 Mon Sep 17 00:00:00 2001 From: danielfeismann Date: Mon, 22 Jul 2024 15:25:17 +0200 Subject: [PATCH 21/31] adapt EmAgentIT comments --- src/test/scala/edu/ie3/simona/agent/em/EmAgentIT.scala | 7 ++----- 1 file changed, 2 insertions(+), 5 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 b32e5151f4..4b2695e913 100644 --- a/src/test/scala/edu/ie3/simona/agent/em/EmAgentIT.scala +++ b/src/test/scala/edu/ie3/simona/agent/em/EmAgentIT.scala @@ -63,8 +63,6 @@ class EmAgentIT with EmInputTestData with MockitoSugar { - // implicit val messageTimeout: Duration = Duration(50.0, SECONDS) - // start a bit later so the sun is up protected implicit val simulationStartDate: ZonedDateTime = TimeUtil.withDefaults.toZonedDateTime("2020-01-01T10:00:00Z") @@ -684,8 +682,7 @@ class EmAgentIT /* TICK 21600 LOAD: 0.000269 MW (unchanged) PV: -0.000032 MW (unchanged) - Heat pump: Is running and cannot be turned off - FIXME -> flex signal is 0 MW: Heat pump is turned off + Heat pump: Is running and cannot be turned off */ emAgentActivation ! Activation(21600) @@ -713,8 +710,8 @@ class EmAgentIT } scheduler.expectMessage(Completion(emAgentActivation, Some(28800))) - } } + } } From fd1630e092b88115b38f32e8f7746089d829e775 Mon Sep 17 00:00:00 2001 From: danielfeismann Date: Mon, 22 Jul 2024 15:28:44 +0200 Subject: [PATCH 22/31] use actual state when determine energyDemand for next period --- .../scala/edu/ie3/simona/model/thermal/ThermalGrid.scala | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/main/scala/edu/ie3/simona/model/thermal/ThermalGrid.scala b/src/main/scala/edu/ie3/simona/model/thermal/ThermalGrid.scala index 8c937d42c7..29d2d895b1 100644 --- a/src/main/scala/edu/ie3/simona/model/thermal/ThermalGrid.scala +++ b/src/main/scala/edu/ie3/simona/model/thermal/ThermalGrid.scala @@ -24,7 +24,7 @@ import edu.ie3.simona.util.TickUtil.TickLong import edu.ie3.util.quantities.QuantityUtils.RichQuantityDouble import edu.ie3.util.scala.quantities.DefaultQuantities._ import squants.energy.Kilowatts -import squants.{Energy, Power, Temperature} +import squants.{Each, Energy, Power, Temperature} import java.time.ZonedDateTime import scala.jdk.CollectionConverters.SetHasAsScala @@ -67,7 +67,7 @@ final case class ThermalGrid( tick, lastHouseState, ambientTemperature, - zeroKW, + lastHouseState.qDot, ) if ( updatedState._1.innerTemperature < thermalHouse.targetTemperature @@ -95,7 +95,9 @@ final case class ThermalGrid( storage .zip(state.storageState) .map { case (storage, state) => - val usableEnergy = state.storedEnergy + val updatedStorageState = + storage.updateState(tick, state.qDot, state)._1 + val usableEnergy = updatedStorageState.storedEnergy val remaining = storage.getMaxEnergyThreshold - usableEnergy ( usableEnergy, From 9cd94e8fab22e1a36a6f485cf99495f79f9a9587 Mon Sep 17 00:00:00 2001 From: danielfeismann Date: Mon, 22 Jul 2024 15:29:33 +0200 Subject: [PATCH 23/31] only charge storage if it has still available capacity --- .../simona/model/thermal/ThermalGrid.scala | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 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 29d2d895b1..bcfa282483 100644 --- a/src/main/scala/edu/ie3/simona/model/thermal/ThermalGrid.scala +++ b/src/main/scala/edu/ie3/simona/model/thermal/ThermalGrid.scala @@ -172,7 +172,24 @@ final case class ThermalGrid( case (None, None) => (zeroKW, zeroKW) } - if (qDotStorageLastState > zeroKW) { + val actualThermalStorageSoc = + if (storage.nonEmpty && state.storageState.nonEmpty) { + val (thermalStorage, lastStorageState) = + storage.zip(state.storageState).head + val (updatedStorageState, maybeStorageThreshold) = + thermalStorage.updateState( + tick, + qDot, + lastStorageState, + ) + Each( + updatedStorageState.storedEnergy / thermalStorage.getMaxEnergyThreshold + ) + } else { + Each(1.0) + } + + if (qDotStorageLastState > zeroKW && actualThermalStorageSoc < Each(1.0)) { { storage.zip(state.storageState) match { case Some((thermalStorage, lastStorageState)) => From a001dc02b4d53408123b98469bdb2f8739b18759 Mon Sep 17 00:00:00 2001 From: danielfeismann Date: Mon, 22 Jul 2024 15:30:05 +0200 Subject: [PATCH 24/31] use the actual result tick for thermalhouse and thermal storage results --- .../simona/model/thermal/ThermalGrid.scala | 38 +++++++++++++++++++ 1 file changed, 38 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 bcfa282483..7994a11911 100644 --- a/src/main/scala/edu/ie3/simona/model/thermal/ThermalGrid.scala +++ b/src/main/scala/edu/ie3/simona/model/thermal/ThermalGrid.scala @@ -500,6 +500,44 @@ final case class ThermalGrid( def results( state: ThermalGridState )(implicit startDateTime: ZonedDateTime): Seq[ResultEntity] = { + /* FIXME: We only want to write results when there is a change within the participant. + At the moment we write an storage result when the house result gets updated and vice versa. + * */ + + val houseResultTick: Option[Long] = house + .zip(state.houseState) + .headOption + .flatMap { + case ( + thermalHouse, + ThermalHouseState(tick, _, _), + ) => + Some(tick) + case _ => None + } + + val storageResultTick: Option[Long] = storage + .zip(state.storageState) + .headOption + .flatMap { + case ( + thermalStorage, + ThermalStorageState(tick, _, _), + ) => + Some(tick) + case _ => None + } + + val actualResultTick: Long = (houseResultTick, storageResultTick) match { + case (Some(hTick), Some(sTick)) => math.max(hTick, sTick) + case (Some(hTick), None) => hTick + case (None, Some(sTick)) => sTick + case (None, None) => + throw new RuntimeException( + "ThermalGrid result should be carried out but it was not possible to get the tick for the result" + ) + } + val houseResults = house .zip(state.houseState) .map { From ae8185ee811633ec3e7abbab359d7ae430de561c Mon Sep 17 00:00:00 2001 From: danielfeismann Date: Mon, 22 Jul 2024 15:30:19 +0200 Subject: [PATCH 25/31] use the actual result tick for thermalhouse and thermal storage results part 2 --- src/main/scala/edu/ie3/simona/model/thermal/ThermalGrid.scala | 4 ++-- 1 file changed, 2 insertions(+), 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 7994a11911..2dba440bdc 100644 --- a/src/main/scala/edu/ie3/simona/model/thermal/ThermalGrid.scala +++ b/src/main/scala/edu/ie3/simona/model/thermal/ThermalGrid.scala @@ -546,7 +546,7 @@ final case class ThermalGrid( ThermalHouseState(tick, innerTemperature, thermalInfeed), ) => Seq.empty[ResultEntity] :+ new ThermalHouseResult( - tick.toDateTime, + actualResultTick.toDateTime, thermalHouse.uuid, thermalInfeed.toMegawatts.asMegaWatt, innerTemperature.toKelvinScale.asKelvin, @@ -562,7 +562,7 @@ final case class ThermalGrid( ThermalStorageState(tick, storedEnergy, qDot), ) => houseResults :+ new CylindricalStorageResult( - tick.toDateTime, + actualResultTick.toDateTime, storage.uuid, storedEnergy.toMegawattHours.asMegaWattHour, qDot.toMegawatts.asMegaWatt, From 43d114b403fea489a7baf83fdb3b6b3739272a69 Mon Sep 17 00:00:00 2001 From: danielfeismann Date: Mon, 22 Jul 2024 15:57:51 +0200 Subject: [PATCH 26/31] fmt --- src/test/scala/edu/ie3/simona/agent/em/EmAgentIT.scala | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) 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 4b2695e913..46fe549cce 100644 --- a/src/test/scala/edu/ie3/simona/agent/em/EmAgentIT.scala +++ b/src/test/scala/edu/ie3/simona/agent/em/EmAgentIT.scala @@ -620,7 +620,9 @@ class EmAgentIT case ParticipantResultEvent(emResult: EmResult) => emResult.getInputModel shouldBe emInput.getUuid emResult.getTime shouldBe 7200.toDateTime - emResult.getP should equalWithTolerance(-0.003528154555.asMegaWatt) + emResult.getP should equalWithTolerance( + (-0.003528154555).asMegaWatt + ) emResult.getQ should equalWithTolerance(0.0000882855367.asMegaVar) } From 402b858f9ce1856b52f0f4c616ee7499988e74f1 Mon Sep 17 00:00:00 2001 From: danielfeismann Date: Tue, 23 Jul 2024 14:48:22 +0200 Subject: [PATCH 27/31] refactor determining the energyDemand to recharge storage --- .../simona/model/participant/HpModel.scala | 8 ++++- .../simona/model/thermal/ThermalGrid.scala | 32 +++++++++---------- .../model/participant/HpModelSpec.scala | 2 +- .../ThermalGridWithHouseAndStorageSpec.scala | 29 +++++------------ .../ThermalGridWithStorageOnlySpec.scala | 4 +-- 5 files changed, 34 insertions(+), 41 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 7af9279611..90f01a36c7 100644 --- a/src/main/scala/edu/ie3/simona/model/participant/HpModel.scala +++ b/src/main/scala/edu/ie3/simona/model/participant/HpModel.scala @@ -153,7 +153,13 @@ final case class HpModel( relevantData.ambientTemperature, state.thermalGridState, ) - demand.hasRequiredDemand || (state.isRunning && demand.hasAdditionalDemand) + + val storedEnergy = state.thermalGridState.storageState + .map(storageState => storageState.storedEnergy) + .getOrElse(zeroKWH) + + (demand.hasRequiredDemand && (demand.required > storedEnergy)) || (state.isRunning && demand.hasAdditionalDemand) + } /** Calculate state depending on whether heat pump is needed or not. Also diff --git a/src/main/scala/edu/ie3/simona/model/thermal/ThermalGrid.scala b/src/main/scala/edu/ie3/simona/model/thermal/ThermalGrid.scala index 2dba440bdc..f0cb9ed6fc 100644 --- a/src/main/scala/edu/ie3/simona/model/thermal/ThermalGrid.scala +++ b/src/main/scala/edu/ie3/simona/model/thermal/ThermalGrid.scala @@ -91,34 +91,34 @@ final case class ThermalGrid( } /* Then go over the storages, see what they can provide and what they might be able to charge */ - val (storedEnergy, remainingCapacity) = { + val storageDemand = { + storage .zip(state.storageState) .map { case (storage, state) => val updatedStorageState = storage.updateState(tick, state.qDot, state)._1 - val usableEnergy = updatedStorageState.storedEnergy - val remaining = storage.getMaxEnergyThreshold - usableEnergy - ( - usableEnergy, - remaining, + val storedEnergy = updatedStorageState.storedEnergy + val soc = storedEnergy / storage.getMaxEnergyThreshold + val storageRequired = + if (soc < 0.5) { + storage.getMaxEnergyThreshold * 0.5 - storedEnergy + } else { zeroMWH } + + val storagePossible = storage.getMaxEnergyThreshold - storedEnergy + ThermalEnergyDemand( + storageRequired, + storagePossible, ) } .getOrElse( - (zeroMWH, zeroMWH) + ThermalEnergyDemand(zeroMWH, zeroMWH) ) } - val usedEnergy = - if (storedEnergy >= houseDemand.required) - houseDemand.required - else - storedEnergy - val finallyRemaining = remainingCapacity + usedEnergy - ThermalEnergyDemand( - houseDemand.required - usedEnergy, - houseDemand.possible + finallyRemaining, + houseDemand.required + storageDemand.required, + houseDemand.possible + storageDemand.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 ef2e082265..6dec2fc25c 100644 --- a/src/test/scala/edu/ie3/simona/model/participant/HpModelSpec.scala +++ b/src/test/scala/edu/ie3/simona/model/participant/HpModelSpec.scala @@ -251,7 +251,7 @@ class HpModelSpec Some( ThermalStorageState( 0L, - KilowattHours(20), + KilowattHours(250), Kilowatts(0), ) ), 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 6955b3c705..8b1557dab2 100644 --- a/src/test/scala/edu/ie3/simona/model/thermal/ThermalGridWithHouseAndStorageSpec.scala +++ b/src/test/scala/edu/ie3/simona/model/thermal/ThermalGridWithHouseAndStorageSpec.scala @@ -102,13 +102,13 @@ class ThermalGridWithHouseAndStorageSpec ThermalGrid.startingState(thermalGrid), ) - gridDemand.required should approximate(KilowattHours(0d)) + gridDemand.required should approximate(KilowattHours(0d + 345d)) gridDemand.possible should approximate( - KilowattHours(31.05009722 + 920) + KilowattHours(31.05009722 + 920d) ) } - "consider stored energy to reduce house demand" in { + "deliver the correct house and storage demand" in { val tick = 10800 // after three hours val startingState = ThermalGrid.startingState(thermalGrid) @@ -122,25 +122,12 @@ class ThermalGridWithHouseAndStorageSpec ), ) - gridDemand.required should approximate(KilowattHours(0d)) - gridDemand.possible should approximate(KilowattHours(1041.200111111)) - } - - "consider stored energy to reduce house demand if stored energy is not enough" in { - val tick = 10800 // after three hours - - val startingState = ThermalGrid.startingState(thermalGrid) - val gridDemand = thermalGrid.energyDemand( - tick, - testGridambientTemperature, - startingState.copy(houseState = - startingState.houseState.map( - _.copy(innerTemperature = Celsius(3d)) - ) - ), + gridDemand.required should approximate( + KilowattHours(45.6000555 + 345.0) + ) + gridDemand.possible should approximate( + KilowattHours(75.600055555 + 920.0) ) - gridDemand.required should approximate(KilowattHours(8.64987499999)) - gridDemand.possible should approximate(KilowattHours(1418.64987499999)) } } 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 fe4276909f..3f9725f86f 100644 --- a/src/test/scala/edu/ie3/simona/model/thermal/ThermalGridWithStorageOnlySpec.scala +++ b/src/test/scala/edu/ie3/simona/model/thermal/ThermalGridWithStorageOnlySpec.scala @@ -86,8 +86,8 @@ class ThermalGridWithStorageOnlySpec ThermalGrid.startingState(thermalGrid), ) - gridDemand.required should approximate(MegawattHours(0d)) - gridDemand.possible should approximate(MegawattHours(0.92d)) + gridDemand.required should approximate(KilowattHours(0d + 345d)) + gridDemand.possible should approximate(KilowattHours(0d + 920d)) } } From 901f44425a36af01cb2f10f0feca52e524981f00 Mon Sep 17 00:00:00 2001 From: danielfeismann Date: Tue, 23 Jul 2024 16:28:05 +0200 Subject: [PATCH 28/31] split energyDemand of ThermalGrid into demand of house and of storage --- .../simona/model/participant/HpModel.scala | 64 +++++++++++++---- .../simona/model/thermal/ThermalGrid.scala | 70 +++++++++---------- .../ThermalGridWithHouseAndStorageSpec.scala | 26 +++---- .../ThermalGridWithHouseOnlySpec.scala | 20 ++++-- .../ThermalGridWithStorageOnlySpec.scala | 16 ++++- 5 files changed, 127 insertions(+), 69 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 90f01a36c7..b513b602f8 100644 --- a/src/main/scala/edu/ie3/simona/model/participant/HpModel.scala +++ b/src/main/scala/edu/ie3/simona/model/participant/HpModel.scala @@ -127,8 +127,9 @@ final case class HpModel( state: HpState, relevantData: HpRelevantData, ): HpState = { - val turnOn = operatesInNextState(state, relevantData) - calcState(state, relevantData, turnOn) + val (turnOn, houseDemand, storageDemand) = + operatesInNextState(state, relevantData) + calcState(state, relevantData, turnOn, houseDemand, storageDemand) } /** Depending on the input, this function decides whether the heat pump will @@ -142,13 +143,14 @@ final case class HpModel( * @param relevantData * Relevant (external) data * @return - * boolean defining if heat pump runs in next time step + * boolean defining if heat pump runs in next time step and if house and + * storage have some demand */ private def operatesInNextState( state: HpState, relevantData: HpRelevantData, - ): Boolean = { - val demand = thermalGrid.energyDemand( + ): (Boolean, Boolean, Boolean) = { + val (demandHouse, demandStorage) = thermalGrid.energyDemand( relevantData.currentTick, relevantData.ambientTemperature, state.thermalGridState, @@ -158,8 +160,18 @@ final case class HpModel( .map(storageState => storageState.storedEnergy) .getOrElse(zeroKWH) - (demand.hasRequiredDemand && (demand.required > storedEnergy)) || (state.isRunning && demand.hasAdditionalDemand) + val turnHpOn = + (demandHouse.hasRequiredDemand && demandHouse.required > storedEnergy) || demandStorage.hasRequiredDemand || (state.isRunning && demandHouse.hasAdditionalDemand) || (state.isRunning && demandStorage.hasAdditionalDemand) + val houseHasDemand = + demandHouse.hasRequiredDemand || demandHouse.hasAdditionalDemand + val storageHasDemand = + demandStorage.hasRequiredDemand || demandStorage.hasAdditionalDemand + ( + turnHpOn, + houseHasDemand, + storageHasDemand, + ) } /** Calculate state depending on whether heat pump is needed or not. Also @@ -172,6 +184,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]] */ @@ -179,6 +195,8 @@ final case class HpModel( state: HpState, relevantData: HpRelevantData, isRunning: Boolean, + houseDemand: Boolean, + storageDemand: Boolean, ): HpState = { val (newActivePower, newThermalPower) = if (isRunning) @@ -192,6 +210,8 @@ final case class HpModel( state.thermalGridState, state.ambientTemperature.getOrElse(relevantData.ambientTemperature), newThermalPower, + houseDemand, + storageDemand, ) HpState( @@ -213,14 +233,17 @@ final case class HpModel( val updatedState = determineState(lastState, data) /* Determine the options we have */ - val thermalEnergyDemand = thermalGrid.energyDemand( - data.currentTick, - data.ambientTemperature, - lastState.thermalGridState, - ) + val (thermalEnergyDemandHouse, thermalEnergyDemandStorge) = + thermalGrid.energyDemand( + data.currentTick, + data.ambientTemperature, + lastState.thermalGridState, + ) val canOperate = - thermalEnergyDemand.hasRequiredDemand || thermalEnergyDemand.hasAdditionalDemand - val canBeOutOfOperation = !thermalEnergyDemand.hasRequiredDemand + thermalEnergyDemandHouse.hasRequiredDemand || thermalEnergyDemandHouse.hasAdditionalDemand || + thermalEnergyDemandStorge.hasRequiredDemand || thermalEnergyDemandStorge.hasAdditionalDemand + val canBeOutOfOperation = + !thermalEnergyDemandHouse.hasRequiredDemand && !thermalEnergyDemandStorge.hasRequiredDemand val lowerBoundary = if (canBeOutOfOperation) @@ -265,7 +288,20 @@ final case class HpModel( ): (HpState, FlexChangeIndicator) = { /* If the setpoint value is above 50 % of the electrical power, turn on the heat pump otherwise turn it off */ val turnOn = setPower > (sRated * cosPhiRated * 0.5) - val updatedState = calcState(lastState, data, turnOn) + + val (thermalEnergyDemandHouse, thermalEnergyDemandStorge) = + thermalGrid.energyDemand( + data.currentTick, + data.ambientTemperature, + lastState.thermalGridState, + ) + val houseHasDemand = + thermalEnergyDemandHouse.hasRequiredDemand || thermalEnergyDemandHouse.hasAdditionalDemand + val storageHasDemand = + thermalEnergyDemandStorge.hasRequiredDemand || thermalEnergyDemandStorge.hasAdditionalDemand + + val updatedState = + calcState(lastState, data, turnOn, houseHasDemand, storageHasDemand) ( updatedState, 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 f0cb9ed6fc..9de756b74d 100644 --- a/src/main/scala/edu/ie3/simona/model/thermal/ThermalGrid.scala +++ b/src/main/scala/edu/ie3/simona/model/thermal/ThermalGrid.scala @@ -24,7 +24,7 @@ import edu.ie3.simona.util.TickUtil.TickLong import edu.ie3.util.quantities.QuantityUtils.RichQuantityDouble import edu.ie3.util.scala.quantities.DefaultQuantities._ import squants.energy.Kilowatts -import squants.{Each, Energy, Power, Temperature} +import squants.{Energy, Power, Temperature} import java.time.ZonedDateTime import scala.jdk.CollectionConverters.SetHasAsScala @@ -51,13 +51,13 @@ final case class ThermalGrid( * @param state * Currently applicable state of the thermal grid * @return - * The total energy demand of the grid + * The total energy demand of the house and the storage */ def energyDemand( tick: Long, ambientTemperature: Temperature, state: ThermalGridState, - ): ThermalEnergyDemand = { + ): (ThermalEnergyDemand, ThermalEnergyDemand) = { /* First get the energy demand of the houses but only if inner temperature is below target temperature */ val houseDemand = @@ -116,13 +116,20 @@ final case class ThermalGrid( ) } - ThermalEnergyDemand( - houseDemand.required + storageDemand.required, - houseDemand.possible + storageDemand.possible, + ( + ThermalEnergyDemand( + houseDemand.required, + houseDemand.possible, + ), + ThermalEnergyDemand( + storageDemand.required, + storageDemand.possible, + ), ) } /** Update the current state of the grid + * * @param tick * Instance in time * @param state @@ -131,6 +138,10 @@ final case class ThermalGrid( * Ambient temperature * @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 */ @@ -139,8 +150,17 @@ final case class ThermalGrid( state: ThermalGridState, ambientTemperature: Temperature, qDot: Power, + houseDemand: Boolean, + storageDemand: Boolean, ): (ThermalGridState, Option[ThermalThreshold]) = if (qDot > zeroKW) - handleInfeed(tick, ambientTemperature, state, qDot) + handleInfeed( + tick, + ambientTemperature, + state, + qDot, + houseDemand, + storageDemand, + ) else handleConsumption(tick, ambientTemperature, state, qDot) @@ -154,6 +174,10 @@ final case class ThermalGrid( * Current state of the houses * @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 */ @@ -162,34 +186,10 @@ final case class ThermalGrid( ambientTemperature: Temperature, state: ThermalGridState, qDot: Power, + houseDemand: Boolean, + storageDemand: Boolean, ): (ThermalGridState, Option[ThermalThreshold]) = { - - val (qDotHouseLastState, qDotStorageLastState) = - (state.houseState, state.storageState) match { - case (Some(house), Some(storage)) => (house.qDot, storage.qDot) - case (Some(house), None) => (house.qDot, zeroKW) - case (None, Some(storage)) => (zeroKW, storage.qDot) - case (None, None) => (zeroKW, zeroKW) - } - - val actualThermalStorageSoc = - if (storage.nonEmpty && state.storageState.nonEmpty) { - val (thermalStorage, lastStorageState) = - storage.zip(state.storageState).head - val (updatedStorageState, maybeStorageThreshold) = - thermalStorage.updateState( - tick, - qDot, - lastStorageState, - ) - Each( - updatedStorageState.storedEnergy / thermalStorage.getMaxEnergyThreshold - ) - } else { - Each(1.0) - } - - if (qDotStorageLastState > zeroKW && actualThermalStorageSoc < Each(1.0)) { + if (storageDemand) { { storage.zip(state.storageState) match { case Some((thermalStorage, lastStorageState)) => @@ -209,7 +209,7 @@ final case class ThermalGrid( ) (Some(nextHouseState._1), nextHouseState._2) - case _ => state.houseState + case None => (None, state.houseState) } val (updatedStorageState, maybeStorageThreshold) = 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 8b1557dab2..8920c5dc7f 100644 --- a/src/test/scala/edu/ie3/simona/model/thermal/ThermalGridWithHouseAndStorageSpec.scala +++ b/src/test/scala/edu/ie3/simona/model/thermal/ThermalGridWithHouseAndStorageSpec.scala @@ -96,23 +96,23 @@ class ThermalGridWithHouseAndStorageSpec "deliver the house demand (no demand) with added flexibility by storage" in { val tick = 10800 // after three hours - val gridDemand = thermalGrid.energyDemand( + val (houseDemand, storageDemand) = thermalGrid.energyDemand( tick, testGridambientTemperature, ThermalGrid.startingState(thermalGrid), ) - gridDemand.required should approximate(KilowattHours(0d + 345d)) - gridDemand.possible should approximate( - KilowattHours(31.05009722 + 920d) - ) + houseDemand.required should approximate(KilowattHours(0d)) + houseDemand.possible should approximate(KilowattHours(31.05009722d)) + storageDemand.required should approximate(KilowattHours(345d)) + storageDemand.possible should approximate(KilowattHours(920d)) } "deliver the correct house and storage demand" in { val tick = 10800 // after three hours val startingState = ThermalGrid.startingState(thermalGrid) - val gridDemand = thermalGrid.energyDemand( + val (houseDemand, storageDemand) = thermalGrid.energyDemand( tick, testGridambientTemperature, startingState.copy(houseState = @@ -122,12 +122,10 @@ class ThermalGridWithHouseAndStorageSpec ), ) - gridDemand.required should approximate( - KilowattHours(45.6000555 + 345.0) - ) - gridDemand.possible should approximate( - KilowattHours(75.600055555 + 920.0) - ) + houseDemand.required should approximate(KilowattHours(45.6000555)) + houseDemand.possible should approximate(KilowattHours(75.600055555)) + storageDemand.required should approximate(KilowattHours(345d)) + storageDemand.possible should approximate(KilowattHours(920d)) } } @@ -476,6 +474,8 @@ class ThermalGridWithHouseAndStorageSpec testGridambientTemperature, initialGridState, externalQDot, + true, + false, ) updatedGridState match { @@ -521,6 +521,8 @@ class ThermalGridWithHouseAndStorageSpec testGridambientTemperature, gridState, externalQDot, + false, + true, ) 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 ac96c4c541..2c5960f1b5 100644 --- a/src/test/scala/edu/ie3/simona/model/thermal/ThermalGridWithHouseOnlySpec.scala +++ b/src/test/scala/edu/ie3/simona/model/thermal/ThermalGridWithHouseOnlySpec.scala @@ -14,7 +14,7 @@ import edu.ie3.simona.model.thermal.ThermalHouse.ThermalHouseThreshold.{ HouseTemperatureUpperBoundaryReached, } import edu.ie3.simona.test.common.UnitSpec -import squants.energy.{Kilowatts, Megawatts, WattHours, Watts} +import squants.energy.{KilowattHours, Kilowatts, Megawatts, WattHours, Watts} import squants.thermal.Celsius import squants.{Energy, Power, Temperature} @@ -74,20 +74,22 @@ class ThermalGridWithHouseOnlySpec extends UnitSpec with ThermalHouseTestData { "determining the energy demand" should { "exactly be the demand of the house" in { val tick = 10800 // after three house - val houseDemand = thermalHouse.energyDemand( + val expectedHouseDemand = thermalHouse.energyDemand( tick, testGridambientTemperature, expectedHouseStartingState, ) - val gridDemand = thermalGrid.energyDemand( + val (houseDemand, storageDemand) = thermalGrid.energyDemand( tick, testGridambientTemperature, ThermalGrid.startingState(thermalGrid), ) - gridDemand.required should approximate(houseDemand.required) - gridDemand.possible should approximate(houseDemand.possible) + houseDemand.required should approximate(expectedHouseDemand.required) + houseDemand.possible should approximate(expectedHouseDemand.possible) + storageDemand.required should approximate(KilowattHours(0d)) + storageDemand.possible should approximate(KilowattHours(0d)) } } @@ -169,6 +171,8 @@ class ThermalGridWithHouseOnlySpec extends UnitSpec with ThermalHouseTestData { testGridambientTemperature, gridState, testGridQDotInfeed, + true, + false, ) updatedGridState match { @@ -194,6 +198,8 @@ class ThermalGridWithHouseOnlySpec extends UnitSpec with ThermalHouseTestData { ThermalGrid.startingState(thermalGrid), testGridambientTemperature, testGridQDotInfeed, + true, + false, ) match { case ( ThermalGridState( @@ -216,6 +222,8 @@ class ThermalGridWithHouseOnlySpec extends UnitSpec with ThermalHouseTestData { ThermalGrid.startingState(thermalGrid), testGridambientTemperature, testGridQDotConsumption, + true, + false, ) match { case ( ThermalGridState( @@ -238,6 +246,8 @@ class ThermalGridWithHouseOnlySpec extends UnitSpec with ThermalHouseTestData { ThermalGrid.startingState(thermalGrid), testGridambientTemperature, Megawatts(0d), + true, + false, ) 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 3f9725f86f..e49b218cca 100644 --- a/src/test/scala/edu/ie3/simona/model/thermal/ThermalGridWithStorageOnlySpec.scala +++ b/src/test/scala/edu/ie3/simona/model/thermal/ThermalGridWithStorageOnlySpec.scala @@ -80,14 +80,16 @@ class ThermalGridWithStorageOnlySpec "deliver the capabilities of the storage" in { val tick = 10800 // after three hours - val gridDemand = thermalGrid.energyDemand( + val (houseDemand, storageDemand) = thermalGrid.energyDemand( tick, testGridambientTemperature, ThermalGrid.startingState(thermalGrid), ) - gridDemand.required should approximate(KilowattHours(0d + 345d)) - gridDemand.possible should approximate(KilowattHours(0d + 920d)) + houseDemand.required should approximate(KilowattHours(0d)) + houseDemand.possible should approximate(KilowattHours(0d)) + storageDemand.required should approximate(KilowattHours(345d)) + storageDemand.possible should approximate(KilowattHours(920d)) } } @@ -149,6 +151,8 @@ class ThermalGridWithStorageOnlySpec testGridambientTemperature, gridState, testGridQDotInfeed, + false, + true, ) updatedGridState match { @@ -172,6 +176,8 @@ class ThermalGridWithStorageOnlySpec ThermalGrid.startingState(thermalGrid), testGridambientTemperature, testGridQDotInfeed, + true, + false, ) nextThreshold shouldBe Some(StorageFull(220800L)) @@ -204,6 +210,8 @@ class ThermalGridWithStorageOnlySpec ), testGridambientTemperature, testGridQDotConsumptionHigh, + true, + false, ) match { case ( ThermalGridState( @@ -226,6 +234,8 @@ class ThermalGridWithStorageOnlySpec ThermalGrid.startingState(thermalGrid), testGridambientTemperature, Kilowatts(0d), + false, + false, ) updatedState match { case ( From ba75a19596069cb0c6f7543a387552cc4c0d9597 Mon Sep 17 00:00:00 2001 From: danielfeismann Date: Tue, 23 Jul 2024 16:31:00 +0200 Subject: [PATCH 29/31] changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 784562582f..82e45c953b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -79,6 +79,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Finally fixing `RuntimeEventListenerSpec` [#849](https://github.com/ie3-institute/simona/issues/849) - Fixed result output for thermal houses and cylindrical storages [#844](https://github.com/ie3-institute/simona/issues/844) - 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) ## [3.0.0] - 2023-08-07 From a65f25a06d904199718ab1fa9dfb37efcc8c7d73 Mon Sep 17 00:00:00 2001 From: danielfeismann Date: Tue, 23 Jul 2024 16:48:41 +0200 Subject: [PATCH 30/31] fix condition for handleInfeed in case qdot should be used first for storage --- 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 9de756b74d..2cf19bf317 100644 --- a/src/main/scala/edu/ie3/simona/model/thermal/ThermalGrid.scala +++ b/src/main/scala/edu/ie3/simona/model/thermal/ThermalGrid.scala @@ -189,7 +189,7 @@ final case class ThermalGrid( houseDemand: Boolean, storageDemand: Boolean, ): (ThermalGridState, Option[ThermalThreshold]) = { - if (storageDemand) { + if (storageDemand && !houseDemand) { { storage.zip(state.storageState) match { case Some((thermalStorage, lastStorageState)) => From dc8ce19ed3fa69c65c93269b82a7795edccb5066 Mon Sep 17 00:00:00 2001 From: danielfeismann Date: Fri, 26 Jul 2024 14:27:34 +0200 Subject: [PATCH 31/31] Refactor ThermalGrid, distinguishes between house supply and storage --- .../simona/model/participant/HpModel.scala | 33 +- .../simona/model/thermal/ThermalGrid.scala | 313 +++++++++--------- .../model/thermal/ThermalGridTestData.scala | 7 +- .../ThermalGridWithHouseAndStorageSpec.scala | 10 +- .../ThermalGridWithHouseOnlySpec.scala | 16 +- .../ThermalGridWithStorageOnlySpec.scala | 16 +- 6 files changed, 209 insertions(+), 186 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 b513b602f8..bd51151454 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.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 @@ -149,7 +152,7 @@ final case class HpModel( private def operatesInNextState( state: HpState, relevantData: HpRelevantData, - ): (Boolean, Boolean, Boolean) = { + ): (Boolean, ThermalEnergyDemand, ThermalEnergyDemand) = { val (demandHouse, demandStorage) = thermalGrid.energyDemand( relevantData.currentTick, relevantData.ambientTemperature, @@ -162,15 +165,11 @@ final case class HpModel( val turnHpOn = (demandHouse.hasRequiredDemand && demandHouse.required > storedEnergy) || demandStorage.hasRequiredDemand || (state.isRunning && demandHouse.hasAdditionalDemand) || (state.isRunning && demandStorage.hasAdditionalDemand) - val houseHasDemand = - demandHouse.hasRequiredDemand || demandHouse.hasAdditionalDemand - val storageHasDemand = - demandStorage.hasRequiredDemand || demandStorage.hasAdditionalDemand ( turnHpOn, - houseHasDemand, - storageHasDemand, + demandHouse, + demandStorage, ) } @@ -195,8 +194,8 @@ final case class HpModel( state: HpState, relevantData: HpRelevantData, isRunning: Boolean, - houseDemand: Boolean, - storageDemand: Boolean, + houseDemand: ThermalEnergyDemand, + storageDemand: ThermalEnergyDemand, ): HpState = { val (newActivePower, newThermalPower) = if (isRunning) @@ -289,19 +288,21 @@ final case class HpModel( /* If the setpoint value is above 50 % of the electrical power, turn on the heat pump otherwise turn it off */ val turnOn = setPower > (sRated * cosPhiRated * 0.5) - val (thermalEnergyDemandHouse, thermalEnergyDemandStorge) = + val (thermalEnergyDemandHouse, thermalEnergyDemandStorage) = thermalGrid.energyDemand( data.currentTick, data.ambientTemperature, lastState.thermalGridState, ) - val houseHasDemand = - thermalEnergyDemandHouse.hasRequiredDemand || thermalEnergyDemandHouse.hasAdditionalDemand - val storageHasDemand = - thermalEnergyDemandStorge.hasRequiredDemand || thermalEnergyDemandStorge.hasAdditionalDemand val updatedState = - calcState(lastState, data, turnOn, houseHasDemand, storageHasDemand) + calcState( + lastState, + data, + turnOn, + thermalEnergyDemandHouse, + thermalEnergyDemandStorage, + ) ( updatedState, 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 2cf19bf317..249699c7f5 100644 --- a/src/main/scala/edu/ie3/simona/model/thermal/ThermalGrid.scala +++ b/src/main/scala/edu/ie3/simona/model/thermal/ThermalGrid.scala @@ -44,6 +44,7 @@ final case class ThermalGrid( /** Determine the energy demand of the total grid at the given instance in * time + * * @param tick * Questioned instance in time * @param ambientTemperature @@ -103,7 +104,9 @@ final case class ThermalGrid( val storageRequired = if (soc < 0.5) { storage.getMaxEnergyThreshold * 0.5 - storedEnergy - } else { zeroMWH } + } else { + zeroMWH + } val storagePossible = storage.getMaxEnergyThreshold - storedEnergy ThermalEnergyDemand( @@ -150,8 +153,8 @@ final case class ThermalGrid( state: ThermalGridState, ambientTemperature: Temperature, qDot: Power, - houseDemand: Boolean, - storageDemand: Boolean, + houseDemand: ThermalEnergyDemand, + storageDemand: ThermalEnergyDemand, ): (ThermalGridState, Option[ThermalThreshold]) = if (qDot > zeroKW) handleInfeed( tick, @@ -164,8 +167,9 @@ final case class ThermalGrid( else handleConsumption(tick, ambientTemperature, state, 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 ambientTemperature @@ -186,160 +190,173 @@ final case class ThermalGrid( ambientTemperature: Temperature, state: ThermalGridState, qDot: Power, - houseDemand: Boolean, - storageDemand: Boolean, + houseDemand: ThermalEnergyDemand, + storageDemand: ThermalEnergyDemand, ): (ThermalGridState, Option[ThermalThreshold]) = { - if (storageDemand && !houseDemand) { - { - storage.zip(state.storageState) match { - case Some((thermalStorage, lastStorageState)) => - val ( - updatedHouseState: Option[ThermalHouseState], - maybeHouseThreshold: Option[ThermalThreshold], - ) = - /* Set thermal power exchange with house to zero */ - house.zip(state.houseState) match { - case Some((thermalHouse, houseState)) => - val nextHouseState = - thermalHouse.determineState( - tick, - houseState, - ambientTemperature, - zeroKW, - ) - (Some(nextHouseState._1), nextHouseState._2) - - case None => (None, state.houseState) - } - - val (updatedStorageState, maybeStorageThreshold) = - thermalStorage.updateState( - tick, - qDot, - lastStorageState, - ) - - /* Both house and storage are updated. Determine what reaches the next threshold */ - val nextThreshold = determineMostRecentThreshold( - maybeHouseThreshold, - maybeStorageThreshold, - ) - - ( - state.copy( - houseState = updatedHouseState, - storageState = Some(updatedStorageState), - ), - nextThreshold, - ) + // 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." + ) + } - 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." - ) - } + if (qDotHouseLastState > zeroKW | qDotStorageLastState > zeroKW) { + val (updatedHouseState, thermalHouseThreshold, remainingQDotHouse) = + handleInfeedHouse(tick, ambientTemperature, state, qDotHouseLastState) + val (updatedStorageState, thermalStorageThreshold) = + if (remainingQDotHouse > qDotStorageLastState) { + handleInfeedStorage( + tick, + ambientTemperature, + state, + remainingQDotHouse, + ) + } else { + handleInfeedStorage( + tick, + ambientTemperature, + state, + qDotStorageLastState, + ) } + + val nextThreshold = determineMostRecentThreshold( + thermalHouseThreshold, + thermalStorageThreshold, + ) + ( + state.copy( + houseState = updatedHouseState, + storageState = updatedStorageState, + ), + nextThreshold, + ) + } else { + if (houseDemand.hasAdditionalDemand) { + val (updatedHouseState, thermalHouseThreshold, remainingQDotHouse) = + handleInfeedHouse(tick, ambientTemperature, state, qDot) + val (updatedStorageState, thermalStorageThreshold) = + handleInfeedStorage(tick, ambientTemperature, state, zeroKW) + val nextThreshold = determineMostRecentThreshold( + thermalHouseThreshold, + thermalStorageThreshold, + ) + ( + state.copy( + houseState = updatedHouseState, + storageState = updatedStorageState, + ), + nextThreshold, + ) + } else if (storageDemand.hasAdditionalDemand) { + val (updatedHouseState, thermalHouseThreshold, remainingQDotHouse) = + handleInfeedHouse(tick, ambientTemperature, state, zeroKW) + val (updatedStorageState, thermalStorageThreshold) = + handleInfeedStorage(tick, ambientTemperature, state, qDot) + val nextThreshold = determineMostRecentThreshold( + thermalHouseThreshold, + thermalStorageThreshold, + ) + ( + state.copy( + houseState = updatedHouseState, + storageState = updatedStorageState, + ), + nextThreshold, + ) + } else { + throw new RuntimeException( + "Heat pump is running but neither house nor storage has additional demand. This should not happen." + ) } } + } - /* Storage was not charged in the last state */ - else { - 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 - } - - val (updatedHouseState, maybeHouseThreshold) = + /** Handles the case, when the house has heat demand and will be heated up + * here. + * + * @param tick + * Current tick + * @param ambientTemperature + * 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, + 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, + ambientTemperature, + qDot, + ) + /* Check if house can handle the thermal feed in */ + if ( + thermalHouse.isInnerTemperatureTooHigh( + newState.innerTemperature + ) + ) { + val (fullHouseState, maybeFullHouseThreshold) = thermalHouse.determineState( tick, lastHouseState, ambientTemperature, - qDot, - ) - - if ( - thermalHouse.isInnerTemperatureTooHigh( - updatedHouseState.innerTemperature + zeroKW, ) - ) { - /* The house is already heated up fully, set back the infeed and put it into storage, if available */ - val (fullHouseState, maybeFullHouseThreshold) = - thermalHouse.determineState( - tick, - lastHouseState, - 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, - ) - } - } else { - /* The house can handle the infeed */ - ( - state.copy(houseState = Some(updatedHouseState)), - maybeHouseThreshold, - ) - } + (Some(fullHouseState), maybeFullHouseThreshold, qDot) + } else { + (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 case, when the storage has heat demand and will be filled up + * here. + * @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, + ambientTemperature: Temperature, + 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) } } @@ -501,7 +518,7 @@ final case class ThermalGrid( state: ThermalGridState )(implicit startDateTime: ZonedDateTime): Seq[ResultEntity] = { /* FIXME: We only want to write results when there is a change within the participant. - At the moment we write an storage result when the house result gets updated and vice versa. + * At the moment we write an storage result when the house result gets updated and vice versa. * */ val houseResultTick: Option[Long] = house 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 a7af090fb6..ba82893e6f 100644 --- a/src/test/scala/edu/ie3/simona/model/thermal/ThermalGridTestData.scala +++ b/src/test/scala/edu/ie3/simona/model/thermal/ThermalGridTestData.scala @@ -9,7 +9,8 @@ package edu.ie3.simona.model.thermal import edu.ie3.datamodel.models.OperationTime import edu.ie3.datamodel.models.input.OperatorInput import edu.ie3.datamodel.models.input.thermal.ThermalBusInput -import squants.energy.{Kilowatts, Power} +import edu.ie3.simona.model.thermal.ThermalGrid.ThermalEnergyDemand +import squants.energy.{KilowattHours, Kilowatts, Power} import squants.thermal.{Celsius, Temperature} import java.util.UUID @@ -25,4 +26,8 @@ trait ThermalGridTestData { protected val testGridQDotInfeed: Power = Kilowatts(15d) protected val testGridQDotConsumption: Power = Kilowatts(-42d) protected val testGridQDotConsumptionHigh: Power = Kilowatts(-200d) + protected val noThermalDemand: ThermalEnergyDemand = + ThermalEnergyDemand(KilowattHours(0d), KilowattHours(0d)) + protected val thermalDemand: ThermalEnergyDemand = + ThermalEnergyDemand(KilowattHours(1d), KilowattHours(2d)) } 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 8920c5dc7f..34cc9f5b0e 100644 --- a/src/test/scala/edu/ie3/simona/model/thermal/ThermalGridWithHouseAndStorageSpec.scala +++ b/src/test/scala/edu/ie3/simona/model/thermal/ThermalGridWithHouseAndStorageSpec.scala @@ -474,8 +474,8 @@ class ThermalGridWithHouseAndStorageSpec testGridambientTemperature, initialGridState, externalQDot, - true, - false, + thermalDemand, + noThermalDemand, ) updatedGridState match { @@ -489,7 +489,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) @@ -521,8 +521,8 @@ class ThermalGridWithHouseAndStorageSpec testGridambientTemperature, gridState, externalQDot, - false, - true, + 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 2c5960f1b5..0dbe5f9625 100644 --- a/src/test/scala/edu/ie3/simona/model/thermal/ThermalGridWithHouseOnlySpec.scala +++ b/src/test/scala/edu/ie3/simona/model/thermal/ThermalGridWithHouseOnlySpec.scala @@ -171,8 +171,8 @@ class ThermalGridWithHouseOnlySpec extends UnitSpec with ThermalHouseTestData { testGridambientTemperature, gridState, testGridQDotInfeed, - true, - false, + thermalDemand, + noThermalDemand, ) updatedGridState match { @@ -198,8 +198,8 @@ class ThermalGridWithHouseOnlySpec extends UnitSpec with ThermalHouseTestData { ThermalGrid.startingState(thermalGrid), testGridambientTemperature, testGridQDotInfeed, - true, - false, + thermalDemand, + noThermalDemand, ) match { case ( ThermalGridState( @@ -222,8 +222,8 @@ class ThermalGridWithHouseOnlySpec extends UnitSpec with ThermalHouseTestData { ThermalGrid.startingState(thermalGrid), testGridambientTemperature, testGridQDotConsumption, - true, - false, + thermalDemand, + noThermalDemand, ) match { case ( ThermalGridState( @@ -246,8 +246,8 @@ class ThermalGridWithHouseOnlySpec extends UnitSpec with ThermalHouseTestData { ThermalGrid.startingState(thermalGrid), testGridambientTemperature, Megawatts(0d), - true, - false, + 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 e49b218cca..ada5195e33 100644 --- a/src/test/scala/edu/ie3/simona/model/thermal/ThermalGridWithStorageOnlySpec.scala +++ b/src/test/scala/edu/ie3/simona/model/thermal/ThermalGridWithStorageOnlySpec.scala @@ -151,8 +151,8 @@ class ThermalGridWithStorageOnlySpec testGridambientTemperature, gridState, testGridQDotInfeed, - false, - true, + noThermalDemand, + thermalDemand, ) updatedGridState match { @@ -176,8 +176,8 @@ class ThermalGridWithStorageOnlySpec ThermalGrid.startingState(thermalGrid), testGridambientTemperature, testGridQDotInfeed, - true, - false, + noThermalDemand, + thermalDemand, ) nextThreshold shouldBe Some(StorageFull(220800L)) @@ -210,8 +210,8 @@ class ThermalGridWithStorageOnlySpec ), testGridambientTemperature, testGridQDotConsumptionHigh, - true, - false, + thermalDemand, + noThermalDemand, ) match { case ( ThermalGridState( @@ -234,8 +234,8 @@ class ThermalGridWithStorageOnlySpec ThermalGrid.startingState(thermalGrid), testGridambientTemperature, Kilowatts(0d), - false, - false, + noThermalDemand, + noThermalDemand, ) updatedState match { case (