From 267c08d2d9ddbdd6d0051e028e6c37ba2fed06a2 Mon Sep 17 00:00:00 2001 From: danielfeismann Date: Thu, 13 Feb 2025 18:31:34 +0100 Subject: [PATCH 1/3] Change thermal house behaviour to heat till targetTemperature --- CHANGELOG.md | 1 + .../readthedocs/models/thermal_house_model.md | 15 +- .../simona/model/participant/HpModel.scala | 14 +- .../simona/model/thermal/ThermalGrid.scala | 79 ++++++- .../simona/model/thermal/ThermalHouse.scala | 56 ++--- .../ie3/simona/agent/grid/ThermalGridIT.scala | 223 +++++++++++------- .../model/participant/HpModelSpec.scala | 28 +-- .../ThermalGridWithHouseAndStorageSpec.scala | 45 +++- .../ThermalGridWithHouseOnlySpec.scala | 33 ++- .../ThermalGridWithStorageOnlySpec.scala | 8 + .../model/thermal/ThermalHouseSpec.scala | 9 +- 11 files changed, 356 insertions(+), 155 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9070d22b39..6f9a961039 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -48,6 +48,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Checking the number of slack nodes [#1122](https://github.com/ie3-institute/simona/issues/1122) - Enhance exception message in case of InvalidGridException [#1124](https://github.com/ie3-institute/simona/issues/1124) - Integration test for thermal grids [#1145](https://github.com/ie3-institute/simona/issues/1145) +- Change thermal house behaviour to heat till targetTemperature [#1176](https://github.com/ie3-institute/simona/issues/1176) ### Changed - Adapted to changed data source in PSDM [#435](https://github.com/ie3-institute/simona/issues/435) diff --git a/docs/readthedocs/models/thermal_house_model.md b/docs/readthedocs/models/thermal_house_model.md index 84044085dc..a803c4996d 100644 --- a/docs/readthedocs/models/thermal_house_model.md +++ b/docs/readthedocs/models/thermal_house_model.md @@ -6,7 +6,20 @@ This page documents the functionality of the thermal house available in SIMONA. ## Behaviour -This house model represents the thermal behaviour of a building. This reflects a simple shoebox with a thermal capacity and with transmission losses. +This house model represents the thermal behaviour of a building. It represents a simple shoebox with thermal capacity and transmission losses. +The house can optionally be equipped with a {ref}`cts_model` as thermal storage. Both are connected by the {ref}`thermal_grid_model`. + +There are different operating modes, depending on whether the heat source is em-controlled or not. + +### Behaviour without EM control + +If the heat source of this building is not under {ref}`em` control, the internal temperature of the house should remain between the target temperature and lower temperature boundary. If the temperature falls below the lower temperature limit, the available heat from the storage is used first. If the storage +is empty, the heat pump will first heat the house up to the target temperature and then refill the storage. +As the storage is initialised as empty, the heat source will start charging the storage first. Whenever the heat source is in operation (e.g. to charge the storage), it will continue to operate until the house has reached the target temperature again. + +### Behaviour under EM control + +Currently not fully supported ## Attributes, Units and Remarks 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 e874e7c028..985c3bb3a2 100644 --- a/src/main/scala/edu/ie3/simona/model/participant/HpModel.scala +++ b/src/main/scala/edu/ie3/simona/model/participant/HpModel.scala @@ -136,6 +136,7 @@ final case class HpModel( thermalGrid.energyDemandAndUpdatedState( relevantData, lastHpState, + false, ) // Determining the operation point and limitations at this tick @@ -149,7 +150,7 @@ final case class HpModel( // Updating the HpState val updatedHpState = - calcState(lastHpState, relevantData, turnOn, thermalDemandWrapper) + calcState(lastHpState, relevantData, turnOn, false, thermalDemandWrapper) (canOperate, canBeOutOfOperation, updatedHpState) } @@ -212,6 +213,9 @@ final case class HpModel( * data of heat pump including state of the heat pump * @param isRunning * determines whether the heat pump is running or not + * @param useUpperTempBoundaryForFlexibility + * determines whether the upper temperature boundary of the house will be + * applied or not * @param demandWrapper * holds the thermal demands of the thermal units (house, storage) * @return @@ -221,6 +225,7 @@ final case class HpModel( lastState: HpState, relevantData: HpRelevantData, isRunning: Boolean, + useUpperTempBoundaryForFlexibility: Boolean, demandWrapper: ThermalDemandWrapper, ): HpState = { val lastStateStorageQDot = lastState.thermalGridState.storageState @@ -250,6 +255,7 @@ final case class HpModel( lastState.thermalGridState, lastState.ambientTemperature.getOrElse(relevantData.ambientTemperature), isRunning, + useUpperTempBoundaryForFlexibility, qDotIntoGrid, demandWrapper, ) @@ -317,6 +323,10 @@ final case class HpModel( /* If the set point value is above 50 % of the electrical power, turn on the heat pump otherwise turn it off */ val turnOn = setPower > (sRated.toActivePower(cosPhiRated) * 0.5) + val useUpperTempBoundaryForFlexibility = + (lastState.tick == relevantData.currentTick && + lastState.isRunning != turnOn) + val ( thermalDemandWrapper, _, @@ -324,12 +334,14 @@ final case class HpModel( thermalGrid.energyDemandAndUpdatedState( relevantData, lastState, + useUpperTempBoundaryForFlexibility, ) val updatedHpState = calcState( lastState, relevantData, turnOn, + useUpperTempBoundaryForFlexibility, thermalDemandWrapper, ) diff --git a/src/main/scala/edu/ie3/simona/model/thermal/ThermalGrid.scala b/src/main/scala/edu/ie3/simona/model/thermal/ThermalGrid.scala index c506e44cfd..8cee6f89fd 100644 --- a/src/main/scala/edu/ie3/simona/model/thermal/ThermalGrid.scala +++ b/src/main/scala/edu/ie3/simona/model/thermal/ThermalGrid.scala @@ -53,6 +53,9 @@ final case class ThermalGrid( * Last state of the heat pump * @param relevantData * data of heat pump including + * @param useUpperTempBoundaryForFlexibility + * determines whether the upper temperature boundary of the house will be + * applied or not * @return * The total energy demand of the house and the storage and an updated * [[ThermalGridState]] @@ -60,12 +63,18 @@ final case class ThermalGrid( def energyDemandAndUpdatedState( relevantData: HpRelevantData, lastHpState: HpState, + useUpperTempBoundaryForFlexibility: Boolean, ): (ThermalDemandWrapper, ThermalGridState) = { /* First get the energy demand of the houses but only if inner temperature is below target temperature */ val (houseDemand, updatedHouseState) = house.zip(lastHpState.thermalGridState.houseState) match { case Some((thermalHouse, lastHouseState)) => + val actualTargetTemperature = + if (useUpperTempBoundaryForFlexibility) + thermalHouse.upperBoundaryTemperature + else thermalHouse.targetTemperature + val (updatedHouseState, _) = thermalHouse.determineState( relevantData, @@ -74,15 +83,18 @@ final case class ThermalGrid( relevantData.ambientTemperature ), lastHouseState.qDot, + actualTargetTemperature, ) if ( updatedHouseState.innerTemperature < thermalHouse.targetTemperature | (lastHouseState.qDot > zeroKW && updatedHouseState.innerTemperature < thermalHouse.upperBoundaryTemperature) ) { + ( thermalHouse.energyDemand( relevantData, updatedHouseState, + actualTargetTemperature, ), Some(updatedHouseState), ) @@ -155,6 +167,9 @@ final case class ThermalGrid( * Ambient temperature valid up until (not including) the current tick * @param isRunning * determines whether the heat pump is running or not + * @param useUpperTempBoundaryForFlexibility + * determines whether the upper temperature boundary of the house will be + * applied or not * @param qDot * Infeed to the grid from thermal generation (e.g. heat pump) or thermal * storages @@ -168,6 +183,7 @@ final case class ThermalGrid( lastThermalGridState: ThermalGridState, lastAmbientTemperature: Temperature, isRunning: Boolean, + useUpperTempBoundaryForFlexibility: Boolean, qDot: Power, thermalDemands: ThermalDemandWrapper, ): (ThermalGridState, Option[ThermalThreshold]) = if (qDot > zeroKW) @@ -176,6 +192,7 @@ final case class ThermalGrid( lastAmbientTemperature, lastThermalGridState, isRunning, + useUpperTempBoundaryForFlexibility, qDot, thermalDemands, ) @@ -184,6 +201,7 @@ final case class ThermalGrid( relevantData, lastAmbientTemperature, lastThermalGridState, + useUpperTempBoundaryForFlexibility, qDot, ) @@ -202,6 +220,9 @@ final case class ThermalGrid( * state of the thermalGrid until this tick * @param isRunning * determines whether the heat pump is running or not + * @param useUpperTempBoundaryForFlexibility + * determines whether the upper temperature boundary of the house will be + * applied or not * @param qDot * Infeed to the grid from thermal generation (e.g. heat pump) or thermal * storages @@ -215,6 +236,7 @@ final case class ThermalGrid( lastAmbientTemperature: Temperature, lastThermalGridState: ThermalGridState, isRunning: Boolean, + useUpperTempBoundaryForFlexibility: Boolean, qDot: Power, thermalDemands: ThermalDemandWrapper, ): (ThermalGridState, Option[ThermalThreshold]) = { @@ -241,6 +263,7 @@ final case class ThermalGrid( lastAmbientTemperature, lastThermalGridState, qDotHouseLastState, + useUpperTempBoundaryForFlexibility, ) // ...and for the storage @@ -283,6 +306,7 @@ final case class ThermalGrid( lastThermalGridState, qDot, zeroKW, + useUpperTempBoundaryForFlexibility, ) } else { // ... or continue lastState's behaviour @@ -292,6 +316,7 @@ final case class ThermalGrid( lastThermalGridState, qDotHouseLastState, qDotStorageLastState, + useUpperTempBoundaryForFlexibility, ) } } @@ -303,6 +328,7 @@ final case class ThermalGrid( lastThermalGridState, qDot, -qDot, + useUpperTempBoundaryForFlexibility, ) } // or finally check for all other cases. @@ -313,6 +339,7 @@ final case class ThermalGrid( lastAmbientTemperature, lastThermalGridState, qDot, + useUpperTempBoundaryForFlexibility, ) } @@ -357,6 +384,9 @@ final case class ThermalGrid( * @param qDot * Infeed to the grid from thermal generation (e.g. heat pump) or thermal * storages + * @param useUpperTempBoundaryForFlexibility + * determines whether the upper temperature boundary of the house will be + * applied or not * @return * Updated thermal grid state and the thermalThreshold if there is one */ @@ -366,6 +396,7 @@ final case class ThermalGrid( lastAmbientTemperature: Temperature, gridState: ThermalGridState, qDot: Power, + useUpperTempBoundaryForFlexibility: Boolean, ): (ThermalGridState, Option[ThermalThreshold]) = { if (thermalDemands.houseDemand.hasRequiredDemand) @@ -375,6 +406,7 @@ final case class ThermalGrid( gridState, qDot, zeroKW, + useUpperTempBoundaryForFlexibility, ) else if ( thermalDemands.heatStorageDemand.hasRequiredDemand || thermalDemands.heatStorageDemand.hasAdditionalDemand @@ -385,6 +417,7 @@ final case class ThermalGrid( gridState, zeroKW, qDot, + useUpperTempBoundaryForFlexibility, ) else if (thermalDemands.houseDemand.hasAdditionalDemand) handleCases( @@ -393,6 +426,7 @@ final case class ThermalGrid( gridState, qDot, zeroKW, + useUpperTempBoundaryForFlexibility, ) else handleCases( @@ -401,6 +435,7 @@ final case class ThermalGrid( gridState, zeroKW, zeroKW, + useUpperTempBoundaryForFlexibility, ) } @@ -418,7 +453,9 @@ final case class ThermalGrid( * Infeed to the house * @param qDotHeatStorage * Infeed to the heat storage (positive: Storage is charging, negative: - * Storage is discharging) + * Storage is discharging) * @param useUpperTempBoundaryForFlexibility * + * determines whether the upper temperature boundary of the house will be + * applied or not * @return * Updated thermal grid state and the next threshold if there is one */ @@ -428,6 +465,7 @@ final case class ThermalGrid( state: ThermalGridState, qDotHouse: Power, qDotHeatStorage: Power, + useUpperTempBoundaryForFlexibility: Boolean, ): (ThermalGridState, Option[ThermalThreshold]) = { val (updatedHouseState, thermalHouseThreshold, _) = handleInfeedHouse( @@ -435,6 +473,7 @@ final case class ThermalGrid( lastAmbientTemperature, state, qDotHouse, + useUpperTempBoundaryForFlexibility, ) val (updatedStorageState, thermalStorageThreshold) = @@ -465,6 +504,9 @@ final case class ThermalGrid( * Current state of the houses * @param qDotHouse * Infeed into the house + * @param useUpperTempBoundaryForFlexibility + * determines whether the upper temperature boundary of the house will be + * applied or not * @return * Updated thermal house state, a ThermalThreshold and the remaining qDot */ @@ -473,19 +515,27 @@ final case class ThermalGrid( lastAmbientTemperature: Temperature, state: ThermalGridState, qDotHouse: Power, + useUpperTempBoundaryForFlexibility: Boolean, ): (Option[ThermalHouseState], Option[ThermalThreshold], Power) = { (house, state.houseState) match { case (Some(thermalHouse), Some(lastHouseState)) => + val actualTargetTemperature = + if (useUpperTempBoundaryForFlexibility) + thermalHouse.upperBoundaryTemperature + else thermalHouse.targetTemperature + val (newState, threshold) = thermalHouse.determineState( relevantData, lastHouseState, lastAmbientTemperature, qDotHouse, + actualTargetTemperature, ) /* Check if house can handle the thermal feed in */ if ( thermalHouse.isInnerTemperatureTooHigh( - newState.innerTemperature + newState.innerTemperature, + thermalHouse.targetTemperature, ) ) { val (fullHouseState, maybeFullHouseThreshold) = @@ -494,6 +544,7 @@ final case class ThermalGrid( lastHouseState, lastAmbientTemperature, zeroKW, + actualTargetTemperature, ) (Some(fullHouseState), maybeFullHouseThreshold, qDotHouse) } else { @@ -565,6 +616,9 @@ final case class ThermalGrid( * Ambient temperature valid up until (not including) the current tick * @param lastThermalGridState * state of the thermalGrid until this tick + * @param useUpperTempBoundaryForFlexibility + * determines whether the upper temperature boundary of the house will be + * applied or not * @param qDot * Infeed to the grid from thermal generation (e.g. heat pump) or thermal * storages @@ -575,17 +629,25 @@ final case class ThermalGrid( relevantData: HpRelevantData, lastAmbientTemperature: Temperature, lastThermalGridState: ThermalGridState, + useUpperTempBoundaryForFlexibility: Boolean, qDot: Power, ): (ThermalGridState, Option[ThermalThreshold]) = { /* House will be left with no influx in all cases. Determine if and when a threshold is reached */ + val maybeUpdatedHouseState = house.zip(lastThermalGridState.houseState).map { - case (house, houseState) => - house.determineState( + case (thermalHouse, houseState) => + val actualTargetTemperature = + if (useUpperTempBoundaryForFlexibility) + thermalHouse.upperBoundaryTemperature + else thermalHouse.targetTemperature + + thermalHouse.determineState( relevantData, houseState, lastAmbientTemperature, zeroMW, + actualTargetTemperature, ) } @@ -605,6 +667,7 @@ final case class ThermalGrid( lastThermalGridState.storageState, lastAmbientTemperature, qDot, + useUpperTempBoundaryForFlexibility, ) val nextThreshold = determineMostRecentThreshold( @@ -654,6 +717,7 @@ final case class ThermalGrid( formerStorageState: Option[ThermalStorageState], lastAmbientTemperature: Temperature, qDot: Power, + useUpperTempBoundaryForFlexibility: Boolean, ): ( Option[(ThermalHouseState, Option[ThermalThreshold])], Option[(ThermalStorageState, Option[ThermalThreshold])], @@ -678,6 +742,12 @@ final case class ThermalGrid( ) ), ) + + val actualTargetTemperature = + if (useUpperTempBoundaryForFlexibility) + thermalHouse.upperBoundaryTemperature + else thermalHouse.targetTemperature + val revisedHouseState = thermalHouse.determineState( relevantData, formerHouseState.getOrElse( @@ -687,6 +757,7 @@ final case class ThermalGrid( ), lastAmbientTemperature, thermalStorage.getChargingPower, + actualTargetTemperature, ) (Some(revisedHouseState), Some(revisedStorageState)) case _ => (maybeHouseState, maybeStorageState) diff --git a/src/main/scala/edu/ie3/simona/model/thermal/ThermalHouse.scala b/src/main/scala/edu/ie3/simona/model/thermal/ThermalHouse.scala index a16840a081..0a20bfc415 100644 --- a/src/main/scala/edu/ie3/simona/model/thermal/ThermalHouse.scala +++ b/src/main/scala/edu/ie3/simona/model/thermal/ThermalHouse.scala @@ -16,7 +16,7 @@ import edu.ie3.simona.model.participant.HpModel.HpRelevantData import edu.ie3.simona.model.thermal.ThermalGrid.ThermalEnergyDemand import edu.ie3.simona.model.thermal.ThermalHouse.ThermalHouseThreshold.{ HouseTemperatureLowerBoundaryReached, - HouseTemperatureUpperBoundaryReached, + HouseTemperatureTargetOrUpperBoundaryReached, } import edu.ie3.simona.model.thermal.ThermalHouse.{ ThermalHouseState, @@ -87,12 +87,15 @@ final case class ThermalHouse( * data of heat pump including state of the heat pump * @param state * most recent state, that is valid for this model + * @param actualTargetTemperature + * the applied target temperature for this model * @return * the needed energy in the questioned tick */ def energyDemand( relevantData: HpRelevantData, state: ThermalHouseState, + actualTargetTemperature: Temperature, ): ThermalEnergyDemand = { /* Calculate the inner temperature of the house, at the questioned instance in time */ val duration = Seconds(relevantData.currentTick - state.tick) @@ -103,33 +106,18 @@ final case class ThermalHouse( relevantData.ambientTemperature, ) - /* Determine, which temperature boundary triggers a needed energy to reach the temperature constraints */ - val temperatureToTriggerRequiredEnergy = - if ( - currentInnerTemp <= state.innerTemperature && - state.qDot <= zeroKW - ) { - // temperature has been decreasing and heat source has been turned off - // => we have reached target temp before and are now targeting lower temp - lowerBoundaryTemperature - } else targetTemperature val requiredEnergy = if ( - isInnerTemperatureTooLow( - currentInnerTemp, - temperatureToTriggerRequiredEnergy, - ) - ) energy(targetTemperature, currentInnerTemp) + isInnerTemperatureTooLow(currentInnerTemp, lowerBoundaryTemperature) + ) { energy(targetTemperature, currentInnerTemp) } else - zeroMWh + zeroKWh val possibleEnergy = - if (!isInnerTemperatureTooHigh(currentInnerTemp)) { - // if upper boundary has not been reached, - // there is an amount of optional energy that could be stored - energy(upperBoundaryTemperature, currentInnerTemp) - } else - zeroMWh + if (!isInnerTemperatureTooHigh(currentInnerTemp, targetTemperature)) { + energy(actualTargetTemperature, currentInnerTemp) + } else zeroKWh + ThermalEnergyDemand(requiredEnergy, possibleEnergy) } @@ -159,13 +147,17 @@ final case class ThermalHouse( } /** Check if inner temperature is higher than preferred maximum temperature + * @param innerTemperature + * the inner temperature of the house + * @param boundaryTemperature + * the applied boundary temperature to check against * * @return * true, if inner temperature is too high */ def isInnerTemperatureTooHigh( innerTemperature: Temperature, - boundaryTemperature: Temperature = upperBoundaryTemperature, + boundaryTemperature: Temperature, ): Boolean = innerTemperature > ( boundaryTemperature - temperatureTolerance @@ -231,6 +223,8 @@ final case class ThermalHouse( * Ambient temperature valid up until (not including) the current tick * @param qDot * New thermal influx + * @param actualTargetTemperature + * the applied target temperature for this model * @return * Updated state and the tick in which the next threshold is reached */ @@ -239,6 +233,7 @@ final case class ThermalHouse( state: ThermalHouseState, lastAmbientTemperature: Temperature, qDot: Power, + actualTargetTemperature: Temperature, ): (ThermalHouseState, Option[ThermalThreshold]) = { val duration = Seconds(relevantData.currentTick - state.tick) val updatedInnerTemperature = newInnerTemperature( @@ -255,6 +250,7 @@ final case class ThermalHouse( qDot, updatedInnerTemperature, relevantData.ambientTemperature, + actualTargetTemperature, ) ( @@ -276,6 +272,8 @@ final case class ThermalHouse( * The inner temperature * @param ambientTemperature * The ambient temperature + * @param actualTargetTemperature + * the applied target temperature for this model * @return * The next threshold, that will be reached */ @@ -284,6 +282,7 @@ final case class ThermalHouse( qDotExternal: Power, innerTemperature: Temperature, ambientTemperature: Temperature, + actualTargetTemperature: Temperature, ): Option[ThermalThreshold] = { val artificialDuration = Hours(1d) val loss = ethLosses.calcThermalEnergyChange( @@ -306,16 +305,17 @@ final case class ThermalHouse( ).map(HouseTemperatureLowerBoundaryReached) } else if ( resultingQDot > zeroMW && !isInnerTemperatureTooHigh( - innerTemperature + innerTemperature, + actualTargetTemperature, ) ) { /* House has more gain than losses */ nextActivation( tick, - upperBoundaryTemperature, + actualTargetTemperature, innerTemperature, resultingQDot, - ).map(HouseTemperatureUpperBoundaryReached) + ).map(HouseTemperatureTargetOrUpperBoundaryReached) } else { /* House is in perfect balance */ None @@ -399,7 +399,7 @@ object ThermalHouse { final case class HouseTemperatureLowerBoundaryReached( override val tick: Long ) extends ThermalThreshold - final case class HouseTemperatureUpperBoundaryReached( + final case class HouseTemperatureTargetOrUpperBoundaryReached( override val tick: Long ) extends ThermalThreshold } diff --git a/src/test/scala/edu/ie3/simona/agent/grid/ThermalGridIT.scala b/src/test/scala/edu/ie3/simona/agent/grid/ThermalGridIT.scala index fe03206b34..246a85da77 100644 --- a/src/test/scala/edu/ie3/simona/agent/grid/ThermalGridIT.scala +++ b/src/test/scala/edu/ie3/simona/agent/grid/ThermalGridIT.scala @@ -149,7 +149,7 @@ class ThermalGridIT /* TICK 0 Start of Simulation - House demand heating : requiredDemand = 0.0 kWh, possibleDemand ~ 15 kWh + House demand heating : requiredDemand = 0.0 kWh, possibleDemand = 0.0 kWh ThermalStorage : requiredDemand = 10.44 kWh, possibleDemand = 10.44 kWh Heat pump: turned on - to serve the storage demand */ @@ -166,7 +166,7 @@ class ThermalGridIT Celsius(-5d), MetersPerSecond(0d), ), - Some(7200), + Some(3600), ) } @@ -217,7 +217,7 @@ class ThermalGridIT /* TICK 3417 Storage is fully heated up - House demand heating : requiredDemand = 0.0 kWh, possibleDemand = 17.37 kWh + House demand heating : requiredDemand = 0.0 kWh, possibleDemand = 2.37 kWh ThermalStorage : requiredDemand = 0.0 kWh, possibleDemand = 0.0 kWh Heat pump: stays on since it was on and the house has possible demand */ @@ -270,20 +270,20 @@ class ThermalGridIT } } - scheduler.expectMessage(Completion(heatPumpAgent, Some(7200))) + scheduler.expectMessage(Completion(heatPumpAgent, Some(3600))) - /* TICK 7200 + /* TICK 3600 New weather data (unchanged) incoming - House demand heating : requiredDemand = 0.0 kWh, possibleDemand = 8.41 kWh + House demand heating : requiredDemand = 0.0 kWh, possibleDemand = 1.94 kWh ThermalStorage : requiredDemand = 0.0 kWh, possibleDemand = 0.0 kWh Heat pump: stays on, we got triggered by incoming weather data. So we continue with same behaviour as before */ - heatPumpAgent ! Activation(7200) + heatPumpAgent ! Activation(3600) weatherDependentAgents.foreach { _ ! ProvideWeatherMessage( - 7200, + 3600, weatherService.ref.toClassic, WeatherData( WattsPerSquareMeter(1d), @@ -291,14 +291,14 @@ class ThermalGridIT Celsius(-5d), MetersPerSecond(0d), ), - Some(28800), + Some(21600), ) } resultListener.expectMessageType[ParticipantResultEvent] match { case ParticipantResultEvent(hpResult) => hpResult.getInputModel shouldBe typicalHpInputModel.getUuid - hpResult.getTime shouldBe 7200.toDateTime + hpResult.getTime shouldBe 3600.toDateTime hpResult.getP should equalWithTolerance(pRunningHp) hpResult.getQ should equalWithTolerance( qRunningHp @@ -318,10 +318,10 @@ class ThermalGridIT indoorTemperature, ) => inputModel shouldBe typicalThermalHouse.getUuid - time shouldBe 7200.toDateTime + time shouldBe 3600.toDateTime qDot should equalWithTolerance(0.011.asMegaWatt) indoorTemperature should equalWithTolerance( - 20.8788983755569.asDegreeCelsius + 19.7413453047613.asDegreeCelsius ) case CylindricalThermalStorageResult( time, @@ -330,7 +330,7 @@ class ThermalGridIT energy, ) => inputModel shouldBe typicalThermalStorage.getUuid - time shouldBe 7200.toDateTime + time shouldBe 3600.toDateTime qDot should equalWithTolerance(0.asMegaWatt) energy should equalWithTolerance(0.01044.asMegaWattHour) case _ => @@ -340,21 +340,21 @@ class ThermalGridIT } } - scheduler.expectMessage(Completion(heatPumpAgent, Some(10798))) + scheduler.expectMessage(Completion(heatPumpAgent, Some(4419))) - /* TICK 10798 + /* TICK 4419 House reaches upper temperature boundary House demand heating : requiredDemand = 0.0 kWh, possibleDemand = 0.0 kWh ThermalStorage : requiredDemand = 0.0 kWh, possibleDemand = 0.0 kWh Heat pump: turned off */ - heatPumpAgent ! Activation(10798) + heatPumpAgent ! Activation(4419) resultListener.expectMessageType[ParticipantResultEvent] match { case ParticipantResultEvent(hpResult) => hpResult.getInputModel shouldBe typicalHpInputModel.getUuid - hpResult.getTime shouldBe 10798.toDateTime + hpResult.getTime shouldBe 4419.toDateTime hpResult.getP should equalWithTolerance(0.asMegaWatt) hpResult.getQ should equalWithTolerance(0.asMegaVar) } @@ -372,10 +372,10 @@ class ThermalGridIT indoorTemperature, ) => inputModel shouldBe typicalThermalHouse.getUuid - time shouldBe 10798.toDateTime + time shouldBe 4419.toDateTime qDot should equalWithTolerance(0.asMegaWatt) indoorTemperature should equalWithTolerance( - 21.9998899446115.asDegreeCelsius + 19.9999632240035.asDegreeCelsius ) case CylindricalThermalStorageResult( time, @@ -384,7 +384,7 @@ class ThermalGridIT energy, ) => inputModel shouldBe typicalThermalStorage.getUuid - time shouldBe 10798.toDateTime + time shouldBe 4419.toDateTime qDot should equalWithTolerance(0.asMegaWatt) energy should equalWithTolerance(0.01044.asMegaWattHour) case _ => @@ -394,21 +394,21 @@ class ThermalGridIT } } - scheduler.expectMessage(Completion(heatPumpAgent, Some(28800))) + scheduler.expectMessage(Completion(heatPumpAgent, Some(21600))) - /* TICK 28800 + /* TICK 21600 House would reach lowerTempBoundary at tick 50797 but now it's getting colder which should decrease inner temp of house faster - House demand heating : requiredDemand = 0.0 kWh, possibleDemand = 0.0 kWh + House demand heating : requiredDemand = 0.0 kWh, possibleDemand = 11.9 kWh ThermalStorage : requiredDemand = 0.0 kWh, possibleDemand = 0.0 kWh Heat pump: stays off */ - heatPumpAgent ! Activation(28800) + heatPumpAgent ! Activation(21600) weatherDependentAgents.foreach { _ ! ProvideWeatherMessage( - 28800, + 21600, weatherService.ref.toClassic, WeatherData( WattsPerSquareMeter(2d), @@ -416,14 +416,14 @@ class ThermalGridIT Celsius(-25d), MetersPerSecond(0d), ), - Some(45000), + Some(27000), ) } resultListener.expectMessageType[ParticipantResultEvent] match { case ParticipantResultEvent(hpResult) => hpResult.getInputModel shouldBe typicalHpInputModel.getUuid - hpResult.getTime shouldBe 28800.toDateTime + hpResult.getTime shouldBe 21600.toDateTime hpResult.getP should equalWithTolerance(0.0.asMegaWatt) hpResult.getQ should equalWithTolerance(0.0.asMegaVar) } @@ -441,10 +441,10 @@ class ThermalGridIT indoorTemperature, ) => inputModel shouldBe typicalThermalHouse.getUuid - time shouldBe 28800.toDateTime + time shouldBe 21600.toDateTime qDot should equalWithTolerance(0.0.asMegaWatt) indoorTemperature should equalWithTolerance( - 20.19969728245267.asDegreeCelsius + 18.4091322308494.asDegreeCelsius ) case CylindricalThermalStorageResult( @@ -454,7 +454,7 @@ class ThermalGridIT energy, ) => inputModel shouldBe typicalThermalStorage.getUuid - time shouldBe 28800.toDateTime + time shouldBe 21600.toDateTime qDot should equalWithTolerance(0.0.asMegaWatt) energy should equalWithTolerance(0.01044.asMegaWattHour) case _ => @@ -464,21 +464,21 @@ class ThermalGridIT } } - scheduler.expectMessage(Completion(heatPumpAgent, Some(41940))) + scheduler.expectMessage(Completion(heatPumpAgent, Some(24145))) - /* TICK 41940 + /* TICK 24145 House reach lowerTemperatureBoundary - House demand heating : requiredDemand = 15.0 kWh, possibleDemand = 30.00 kWh + House demand heating : requiredDemand = 15.0 kWh, possibleDemand = 15.00 kWh ThermalStorage : requiredDemand = 0.0 kWh, possibleDemand = 0.0 kWh Heat pump: stays off, demand should be covered by storage */ - heatPumpAgent ! Activation(41940) + heatPumpAgent ! Activation(24145) resultListener.expectMessageType[ParticipantResultEvent] match { case ParticipantResultEvent(hpResult) => hpResult.getInputModel shouldBe typicalHpInputModel.getUuid - hpResult.getTime shouldBe 41940.toDateTime + hpResult.getTime shouldBe 24145.toDateTime hpResult.getP should equalWithTolerance(0.0.asMegaWatt) hpResult.getQ should equalWithTolerance(0.0.asMegaVar) } @@ -496,10 +496,10 @@ class ThermalGridIT indoorTemperature, ) => inputModel shouldBe typicalThermalHouse.getUuid - time shouldBe 41940.toDateTime + time shouldBe 24145.toDateTime qDot should equalWithTolerance(0.01044.asMegaWatt) indoorTemperature should equalWithTolerance( - 17.9999786813733.asDegreeCelsius + 17.9999609659327.asDegreeCelsius ) case CylindricalThermalStorageResult( time, @@ -508,7 +508,7 @@ class ThermalGridIT energy, ) => inputModel shouldBe typicalThermalStorage.getUuid - time shouldBe 41940.toDateTime + time shouldBe 24145.toDateTime qDot should equalWithTolerance(-0.01044.asMegaWatt) energy should equalWithTolerance(0.01044.asMegaWattHour) case _ => @@ -518,21 +518,21 @@ class ThermalGridIT } } - scheduler.expectMessage(Completion(heatPumpAgent, Some(45000))) + scheduler.expectMessage(Completion(heatPumpAgent, Some(27000))) - /* TICK 45000 - Storage will be empty at tick 45540 + /* TICK 27000 + Storage will be empty at tick 27745 Additional trigger caused by (unchanged) weather data should not change this - House demand heating : requiredDemand = 9.78 kWh, possibleDemand = 24.78 kWh - ThermalStorage : requiredDemand = 0.0 kWh, possibleDemand = 8.87 kWh + House demand heating : requiredDemand = 0.0 kWh, possibleDemand = 10.13 kWh + ThermalStorage : requiredDemand = 0.0 kWh, possibleDemand = 8.28 kWh Heat pump: stays off */ - heatPumpAgent ! Activation(45000) + heatPumpAgent ! Activation(27000) weatherDependentAgents.foreach { _ ! ProvideWeatherMessage( - 45000, + 27000, weatherService.ref.toClassic, WeatherData( WattsPerSquareMeter(3d), @@ -540,14 +540,14 @@ class ThermalGridIT Celsius(-25d), MetersPerSecond(0d), ), - Some(57600), + Some(28800), ) } resultListener.expectMessageType[ParticipantResultEvent] match { case ParticipantResultEvent(hpResult) => hpResult.getInputModel shouldBe typicalHpInputModel.getUuid - hpResult.getTime shouldBe 45000.toDateTime + hpResult.getTime shouldBe 27000.toDateTime hpResult.getP should equalWithTolerance(0.0.asMegaWatt) hpResult.getQ should equalWithTolerance(0.0.asMegaVar) } @@ -565,10 +565,10 @@ class ThermalGridIT indoorTemperature, ) => inputModel shouldBe typicalThermalHouse.getUuid - time shouldBe 45000.toDateTime + time shouldBe 27000.toDateTime qDot should equalWithTolerance(0.01044.asMegaWatt) indoorTemperature should equalWithTolerance( - 18.69584558965105.asDegreeCelsius + 18.64920952682994.asDegreeCelsius ) case CylindricalThermalStorageResult( @@ -578,10 +578,10 @@ class ThermalGridIT energy, ) => inputModel shouldBe typicalThermalStorage.getUuid - time shouldBe 45000.toDateTime + time shouldBe 27000.toDateTime qDot should equalWithTolerance(-0.01044.asMegaWatt) energy should equalWithTolerance( - 0.00156599999999999.asMegaWattHour + 0.0021604999999999992.asMegaWattHour ) case _ => fail( @@ -590,22 +590,22 @@ class ThermalGridIT } } - scheduler.expectMessage(Completion(heatPumpAgent, Some(45540))) + scheduler.expectMessage(Completion(heatPumpAgent, Some(27745))) - /* TICK 45540 + /* TICK 27745 Storage will be empty - House demand heating : requiredDemand = 8.87kWh, possibleDemand = 23.87 kWh + House demand heating : requiredDemand = 0.0kWh, possibleDemand = 8.87 kWh ThermalStorage : requiredDemand = 10.44 kWh, possibleDemand = 10.44 kWh DomesticWaterStorage : tba Heat pump: will be turned on - to serve the remaining heat demand of house (and refill storage later) */ - heatPumpAgent ! Activation(45540) + heatPumpAgent ! Activation(27745) resultListener.expectMessageType[ParticipantResultEvent] match { case ParticipantResultEvent(hpResult) => hpResult.getInputModel shouldBe typicalHpInputModel.getUuid - hpResult.getTime shouldBe 45540.toDateTime + hpResult.getTime shouldBe 27745.toDateTime hpResult.getP should equalWithTolerance(pRunningHp) hpResult.getQ should equalWithTolerance(qRunningHp) } @@ -623,10 +623,10 @@ class ThermalGridIT indoorTemperature, ) => inputModel shouldBe typicalThermalHouse.getUuid - time shouldBe 45540.toDateTime + time shouldBe 27745.toDateTime qDot should equalWithTolerance(0.011.asMegaWatt) indoorTemperature should equalWithTolerance( - 18.81725389847177.asDegreeCelsius + 18.81683670795034.asDegreeCelsius ) case CylindricalThermalStorageResult( @@ -636,7 +636,7 @@ class ThermalGridIT energy, ) => inputModel shouldBe typicalThermalStorage.getUuid - time shouldBe 45540.toDateTime + time shouldBe 27745.toDateTime qDot should equalWithTolerance(0.asMegaWatt) energy should equalWithTolerance(0.asMegaWattHour) case _ => @@ -646,20 +646,20 @@ class ThermalGridIT } } - scheduler.expectMessage(Completion(heatPumpAgent, Some(57600))) + scheduler.expectMessage(Completion(heatPumpAgent, Some(28800))) - /* TICK 57600 + /* TICK 28800 New weather data: it's getting warmer again - House demand heating : requiredDemand = 0.00 kWh, possibleDemand = 1.70 kWh + House demand heating : requiredDemand = 0.00 kWh, possibleDemand = 6.93 kWh ThermalStorage : requiredDemand = 10.44 kWh, possibleDemand = 10.44 kWh Heat pump: stays on */ - heatPumpAgent ! Activation(57600) + heatPumpAgent ! Activation(28800) weatherDependentAgents.foreach { _ ! ProvideWeatherMessage( - 57600, + 28800, weatherService.ref.toClassic, WeatherData( WattsPerSquareMeter(4d), @@ -674,7 +674,7 @@ class ThermalGridIT resultListener.expectMessageType[ParticipantResultEvent] match { case ParticipantResultEvent(hpResult) => hpResult.getInputModel shouldBe typicalHpInputModel.getUuid - hpResult.getTime shouldBe 57600.toDateTime + hpResult.getTime shouldBe 28800.toDateTime hpResult.getP should equalWithTolerance(pRunningHp) hpResult.getQ should equalWithTolerance(qRunningHp) } @@ -692,10 +692,10 @@ class ThermalGridIT indoorTemperature, ) => inputModel shouldBe typicalThermalHouse.getUuid - time shouldBe 57600.toDateTime + time shouldBe 28800.toDateTime qDot should equalWithTolerance(0.011.asMegaWatt) indoorTemperature should equalWithTolerance( - 21.77341655767336.asDegreeCelsius + 19.07544129044337.asDegreeCelsius ) case CylindricalThermalStorageResult( @@ -705,27 +705,27 @@ class ThermalGridIT energy, ) => inputModel shouldBe typicalThermalStorage.getUuid - time shouldBe 57600.toDateTime + time shouldBe 28800.toDateTime qDot should equalWithTolerance(0.asMegaWatt) energy should equalWithTolerance(0.asMegaWattHour) } } - scheduler.expectMessage(Completion(heatPumpAgent, Some(58256))) + scheduler.expectMessage(Completion(heatPumpAgent, Some(31402))) - /* TICK 58256 + /* TICK 31402 House will reach the upperTemperatureBoundary House demand heating : requiredDemand = 0.00 kWh, possibleDemand = 0.00 kWh ThermalStorage : requiredDemand = 10.44 kWh, possibleDemand = 10.44 kWh Heat pump: stays on to refill the storage now */ - heatPumpAgent ! Activation(58256) + heatPumpAgent ! Activation(31402) resultListener.expectMessageType[ParticipantResultEvent] match { case ParticipantResultEvent(hpResult) => hpResult.getInputModel shouldBe typicalHpInputModel.getUuid - hpResult.getTime shouldBe 58256.toDateTime + hpResult.getTime shouldBe 31402.toDateTime hpResult.getP should equalWithTolerance(pRunningHp) hpResult.getQ should equalWithTolerance(qRunningHp) } @@ -743,10 +743,10 @@ class ThermalGridIT indoorTemperature, ) => inputModel shouldBe typicalThermalHouse.getUuid - time shouldBe 58256.toDateTime + time shouldBe 31402.toDateTime qDot should equalWithTolerance(0.asMegaWatt) indoorTemperature should equalWithTolerance( - 21.999922627074.asDegreeCelsius + 19.9998698154888.asDegreeCelsius ) case CylindricalThermalStorageResult( @@ -756,7 +756,7 @@ class ThermalGridIT energy, ) => inputModel shouldBe typicalThermalStorage.getUuid - time shouldBe 58256.toDateTime + time shouldBe 31402.toDateTime qDot should equalWithTolerance(0.011.asMegaWatt) energy should equalWithTolerance( 0.asMegaWattHour @@ -764,21 +764,74 @@ class ThermalGridIT } } - scheduler.expectMessage(Completion(heatPumpAgent, Some(61673))) + scheduler.expectMessage(Completion(heatPumpAgent, Some(34819))) + + /* TICK 34819 + Storage will be fully charged, but meanwhile the house cooled a bit + House demand heating : requiredDemand = 0.00 kWh, possibleDemand = 1.42 kWh + ThermalStorage : requiredDemand = 0.00 kWh, possibleDemand = 0.00 kWh + Heat pump: stays on + */ + + heatPumpAgent ! Activation(34819) + + resultListener.expectMessageType[ParticipantResultEvent] match { + case ParticipantResultEvent(hpResult) => + hpResult.getInputModel shouldBe typicalHpInputModel.getUuid + hpResult.getTime shouldBe 34819.toDateTime + hpResult.getP should equalWithTolerance(pRunningHp) + hpResult.getQ should equalWithTolerance(qRunningHp) + } + + Range(0, 2) + .map { _ => + resultListener.expectMessageType[ResultEvent] + } + .foreach { case ResultEvent.ThermalResultEvent(thermalUnitResult) => + thermalUnitResult match { + case ThermalHouseResult( + time, + inputModel, + qDot, + indoorTemperature, + ) => + inputModel shouldBe typicalThermalHouse.getUuid + time shouldBe 34819.toDateTime + qDot should equalWithTolerance(0.011.asMegaWatt) + indoorTemperature should equalWithTolerance( + 19.81003812971276.asDegreeCelsius + ) + + case CylindricalThermalStorageResult( + time, + inputModel, + qDot, + energy, + ) => + inputModel shouldBe typicalThermalStorage.getUuid + time shouldBe 34819.toDateTime + qDot should equalWithTolerance(0.asMegaWatt) + energy should equalWithTolerance( + 0.01044.asMegaWattHour + ) + } + } + + scheduler.expectMessage(Completion(heatPumpAgent, Some(35358))) - /* TICK 61673 - Storage will be fully charged + /* TICK 35358 + Neither house nor storage have any demand House demand heating : requiredDemand = 0.00 kWh, possibleDemand = 0.00 kWh ThermalStorage : requiredDemand = 0.00 kWh, possibleDemand = 0.00 kWh Heat pump: turned off */ - heatPumpAgent ! Activation(61673) + heatPumpAgent ! Activation(35358) resultListener.expectMessageType[ParticipantResultEvent] match { case ParticipantResultEvent(hpResult) => hpResult.getInputModel shouldBe typicalHpInputModel.getUuid - hpResult.getTime shouldBe 61673.toDateTime + hpResult.getTime shouldBe 35358.toDateTime hpResult.getP should equalWithTolerance(0.asMegaWatt) hpResult.getQ should equalWithTolerance(0.asMegaVar) } @@ -796,10 +849,10 @@ class ThermalGridIT indoorTemperature, ) => inputModel shouldBe typicalThermalHouse.getUuid - time shouldBe 61673.toDateTime - qDot should equalWithTolerance(0.asMegaWatt) + time shouldBe 35358.toDateTime + qDot should equalWithTolerance(0.0.asMegaWatt) indoorTemperature should equalWithTolerance( - 21.7847791618269.asDegreeCelsius + 20.000065498039.asDegreeCelsius ) case CylindricalThermalStorageResult( @@ -809,7 +862,7 @@ class ThermalGridIT energy, ) => inputModel shouldBe typicalThermalStorage.getUuid - time shouldBe 61673.toDateTime + time shouldBe 35358.toDateTime qDot should equalWithTolerance(0.asMegaWatt) energy should equalWithTolerance( 0.01044.asMegaWattHour @@ -817,7 +870,7 @@ class ThermalGridIT } } - scheduler.expectMessage(Completion(heatPumpAgent, Some(122555))) + scheduler.expectMessage(Completion(heatPumpAgent, Some(71359))) } } 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 9964f7af3f..a8b51265bb 100644 --- a/src/test/scala/edu/ie3/simona/model/participant/HpModelSpec.scala +++ b/src/test/scala/edu/ie3/simona/model/participant/HpModelSpec.scala @@ -11,7 +11,7 @@ import edu.ie3.simona.model.thermal.ThermalGrid.ThermalGridState import edu.ie3.simona.model.thermal.ThermalHouse.ThermalHouseState import edu.ie3.simona.model.thermal.ThermalHouse.ThermalHouseThreshold.{ HouseTemperatureLowerBoundaryReached, - HouseTemperatureUpperBoundaryReached, + HouseTemperatureTargetOrUpperBoundaryReached, } import edu.ie3.simona.model.thermal.ThermalStorage.ThermalStorageState import edu.ie3.simona.ontology.messages.flex.MinMaxFlexibilityMessage.ProvideMinMaxFlexOptions @@ -55,7 +55,7 @@ class HpModelSpec true, 95, 15.6, - Some(HouseTemperatureUpperBoundaryReached(31711)), + Some(HouseTemperatureTargetOrUpperBoundaryReached(24051)), ), ( HpState( @@ -70,7 +70,7 @@ class HpModelSpec true, 95, 16.4, - Some(HouseTemperatureUpperBoundaryReached(30642)), + Some(HouseTemperatureTargetOrUpperBoundaryReached(22270)), ), ( HpState( @@ -85,7 +85,7 @@ class HpModelSpec true, 95, 18.0, - Some(HouseTemperatureUpperBoundaryReached(27771)), + Some(HouseTemperatureTargetOrUpperBoundaryReached(17486)), ), ( HpState( @@ -450,7 +450,7 @@ class HpModelSpec Some(ThermalHouseState(0L, Celsius(21), Kilowatts(0))), Some(ThermalStorageState(0L, KilowattHours(0), Kilowatts(0))), ), - Some(HouseTemperatureUpperBoundaryReached(0L)), + Some(HouseTemperatureTargetOrUpperBoundaryReached(0L)), ), (95.0, 0.0, 95.0), ), @@ -471,7 +471,7 @@ class HpModelSpec Some(ThermalHouseState(0L, Celsius(21), Kilowatts(0))), Some(ThermalStorageState(0L, KilowattHours(20), Kilowatts(0))), ), - Some(HouseTemperatureUpperBoundaryReached(0L)), + Some(HouseTemperatureTargetOrUpperBoundaryReached(0L)), ), (95.0, 0.0, 95.0), ), @@ -494,7 +494,7 @@ class HpModelSpec Some(ThermalHouseState(0L, Celsius(21), Kilowatts(0))), Some(ThermalStorageState(0L, KilowattHours(0), Kilowatts(0))), ), - Some(HouseTemperatureUpperBoundaryReached(0L)), + Some(HouseTemperatureTargetOrUpperBoundaryReached(0L)), ), (95.0, 0.0, 95.0), ), @@ -515,7 +515,7 @@ class HpModelSpec Some(ThermalHouseState(0L, Celsius(21), Kilowatts(0))), Some(ThermalStorageState(0L, KilowattHours(20), Kilowatts(0))), ), - Some(HouseTemperatureUpperBoundaryReached(0L)), + Some(HouseTemperatureTargetOrUpperBoundaryReached(0L)), ), (0.0, 0.0, 95.0), ), @@ -539,7 +539,7 @@ class HpModelSpec Some(ThermalHouseState(0L, Celsius(21), Kilowatts(0))), Some(ThermalStorageState(0L, KilowattHours(0), Kilowatts(0))), ), - Some(HouseTemperatureUpperBoundaryReached(0L)), + Some(HouseTemperatureTargetOrUpperBoundaryReached(0L)), ), (95.0, 0.0, 95.0), ), @@ -560,7 +560,7 @@ class HpModelSpec Some(ThermalHouseState(0L, Celsius(21), Kilowatts(0))), Some(ThermalStorageState(0L, KilowattHours(20), Kilowatts(0))), ), - Some(HouseTemperatureUpperBoundaryReached(0L)), + Some(HouseTemperatureTargetOrUpperBoundaryReached(0L)), ), (95.0, 0.0, 95.0), ), @@ -583,7 +583,7 @@ class HpModelSpec Some(ThermalHouseState(0L, Celsius(21), Kilowatts(0))), Some(ThermalStorageState(0L, KilowattHours(0), Kilowatts(0))), ), - Some(HouseTemperatureUpperBoundaryReached(0L)), + Some(HouseTemperatureTargetOrUpperBoundaryReached(0L)), ), (95.0, 0.0, 95.0), ), @@ -605,7 +605,7 @@ class HpModelSpec Some(ThermalHouseState(0L, Celsius(21), Kilowatts(0))), Some(ThermalStorageState(0L, KilowattHours(20), Kilowatts(0))), ), - Some(HouseTemperatureUpperBoundaryReached(0L)), + Some(HouseTemperatureTargetOrUpperBoundaryReached(0L)), ), (0.0, 0.0, 95.0), ), @@ -628,7 +628,7 @@ class HpModelSpec ThermalStorageState(0L, KilowattHours(500), Kilowatts(0)) ), ), - Some(HouseTemperatureUpperBoundaryReached(0L)), + Some(HouseTemperatureTargetOrUpperBoundaryReached(0L)), ), (0.0, 0.0, 95.0), ), @@ -651,7 +651,7 @@ class HpModelSpec ThermalStorageState(0L, KilowattHours(500), Kilowatts(0)) ), ), - Some(HouseTemperatureUpperBoundaryReached(0L)), + Some(HouseTemperatureTargetOrUpperBoundaryReached(0L)), ), (0.0, 0.0, 0.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 302834f9c4..1d5a52886b 100644 --- a/src/test/scala/edu/ie3/simona/model/thermal/ThermalGridWithHouseAndStorageSpec.scala +++ b/src/test/scala/edu/ie3/simona/model/thermal/ThermalGridWithHouseAndStorageSpec.scala @@ -12,7 +12,7 @@ import edu.ie3.simona.model.thermal.ThermalGrid.ThermalGridState import edu.ie3.simona.model.thermal.ThermalHouse.ThermalHouseState import edu.ie3.simona.model.thermal.ThermalHouse.ThermalHouseThreshold.{ HouseTemperatureLowerBoundaryReached, - HouseTemperatureUpperBoundaryReached, + HouseTemperatureTargetOrUpperBoundaryReached, } import edu.ie3.simona.model.thermal.ThermalStorage.ThermalStorageState import edu.ie3.simona.model.thermal.ThermalStorage.ThermalStorageThreshold.{ @@ -113,12 +113,13 @@ class ThermalGridWithHouseAndStorageSpec thermalGrid.energyDemandAndUpdatedState( relevantData, lastHpState, + false, ) val houseDemand = thermalDemands.houseDemand val storageDemand = thermalDemands.heatStorageDemand houseDemand.required should approximate(zeroKWh) - houseDemand.possible should approximate(KilowattHours(31.05009722d)) + houseDemand.possible should approximate(KilowattHours(1.05009722d)) storageDemand.required should approximate(KilowattHours(1150d)) storageDemand.possible should approximate(KilowattHours(1150d)) updatedThermalGridState.houseState shouldBe Some( @@ -153,12 +154,13 @@ class ThermalGridWithHouseAndStorageSpec thermalGrid.energyDemandAndUpdatedState( relevantData, lastHpState, + false, ) val houseDemand = thermalDemands.houseDemand val storageDemand = thermalDemands.heatStorageDemand houseDemand.required should approximate(KilowattHours(45.6000555)) - houseDemand.possible should approximate(KilowattHours(75.600055555)) + houseDemand.possible should approximate(KilowattHours(45.600055555)) storageDemand.required should approximate(KilowattHours(1150d)) storageDemand.possible should approximate(KilowattHours(1150d)) updatedThermalGridState.houseState shouldBe Some( @@ -195,6 +197,7 @@ class ThermalGridWithHouseAndStorageSpec relevantData, testGridAmbientTemperature, gridState, + false, externalQDot, ) @@ -234,6 +237,7 @@ class ThermalGridWithHouseAndStorageSpec relevantData, testGridAmbientTemperature, gridState, + false, externalQDot, ) @@ -292,6 +296,7 @@ class ThermalGridWithHouseAndStorageSpec None, testGridAmbientTemperature, testGridQDotConsumption, + false, ) match { case (maybeRevisedHouseState, maybeRevisedStorageState) => maybeRevisedHouseState shouldBe maybeHouseState @@ -334,6 +339,7 @@ class ThermalGridWithHouseAndStorageSpec maybeStorageState.map(_._1), ambientTemperature, zeroInflux, + false, ) match { case (maybeRevisedHouseState, maybeRevisedStorageState) => maybeRevisedHouseState shouldBe maybeHouseState @@ -354,7 +360,7 @@ class ThermalGridWithHouseAndStorageSpec ), testGridQDotInfeed, ), - Some(HouseTemperatureUpperBoundaryReached(3600L)), + Some(HouseTemperatureTargetOrUpperBoundaryReached(3600L)), ) ) val maybeStorageState = Some( @@ -376,6 +382,7 @@ class ThermalGridWithHouseAndStorageSpec maybeStorageState.map(_._1), ambientTemperature, testGridQDotInfeed, + false, ) match { case (maybeRevisedHouseState, maybeRevisedStorageState) => maybeRevisedHouseState shouldBe maybeHouseState @@ -420,6 +427,7 @@ class ThermalGridWithHouseAndStorageSpec maybeStorageState.map(_._1), ambientTemperature, zeroInflux, + false, ) match { case (maybeRevisedHouseState, maybeRevisedStorageState) => maybeRevisedHouseState shouldBe maybeHouseState @@ -483,12 +491,17 @@ class ThermalGridWithHouseAndStorageSpec formerStorageState, ambientTemperature, zeroInflux, + false, ) match { case ( Some( ( ThermalHouseState(houseTick, _, revisedQDotHouse), - Some(HouseTemperatureUpperBoundaryReached(houseWarmTick)), + Some( + HouseTemperatureTargetOrUpperBoundaryReached( + houseWarmTick + ) + ), ) ), Some( @@ -506,7 +519,7 @@ class ThermalGridWithHouseAndStorageSpec thermalStorage.chargingPower * (-1) ) - houseWarmTick shouldBe 3695L + houseWarmTick shouldBe 3601 storageEmptyTick shouldBe 3663L case _ => fail("Revision of states failed") } @@ -519,12 +532,15 @@ class ThermalGridWithHouseAndStorageSpec Symbol("handleInfeed") ) - "heat the house, if the upper temperature in the house is not reached" in { + "heat the house, if the target temperature in the house is not reached" in { val relevantData = HpRelevantData( 0L, testGridAmbientTemperature, ) - val initialGridState = ThermalGrid.startingState(thermalGrid) + val initialGridState = ThermalGridState( + Some(ThermalHouseState(-1, Celsius(17), zeroKW)), + Some(expectedStorageStartingState), + ) val externalQDot = testGridQDotInfeed val (updatedGridState, reachedThreshold) = @@ -533,6 +549,7 @@ class ThermalGridWithHouseAndStorageSpec testGridAmbientTemperature, initialGridState, isRunning, + false, externalQDot, onlyThermalDemandOfHouse, ) @@ -547,7 +564,7 @@ class ThermalGridWithHouseAndStorageSpec ), ) => houseTick shouldBe 0L - innerTemperature should approximate(Celsius(18.9999d)) + innerTemperature should approximate(Celsius(16.9999d)) qDotHouse should approximate(externalQDot) storageTick shouldBe 0L @@ -562,16 +579,19 @@ class ThermalGridWithHouseAndStorageSpec case _ => fail("Thermal grid state has been calculated wrong.") } reachedThreshold shouldBe Some( - HouseTemperatureUpperBoundaryReached(7372L) + HouseTemperatureTargetOrUpperBoundaryReached(7322L) ) } - "load the storage, if the upper temperature in the house is reached" in { + "load the storage, if the target temperature in the house is reached" in { val relevantData = HpRelevantData( 0L, testGridAmbientTemperature, ) - val initialGridState = ThermalGrid.startingState(thermalGrid) + val initialGridState = ThermalGridState( + Some(ThermalHouseState(-1, Celsius(17), zeroKW)), + Some(expectedStorageStartingState), + ) val gridState = initialGridState.copy(houseState = initialGridState.houseState.map( _.copy(innerTemperature = thermalHouse.upperBoundaryTemperature) @@ -585,6 +605,7 @@ class ThermalGridWithHouseAndStorageSpec testGridAmbientTemperature, gridState, isRunning, + false, externalQDot, onlyThermalDemandOfHeatStorage, ) 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 4d1186869b..ccec99ff56 100644 --- a/src/test/scala/edu/ie3/simona/model/thermal/ThermalGridWithHouseOnlySpec.scala +++ b/src/test/scala/edu/ie3/simona/model/thermal/ThermalGridWithHouseOnlySpec.scala @@ -12,7 +12,7 @@ import edu.ie3.simona.model.thermal.ThermalGrid.ThermalGridState import edu.ie3.simona.model.thermal.ThermalHouse.ThermalHouseState import edu.ie3.simona.model.thermal.ThermalHouse.ThermalHouseThreshold.{ HouseTemperatureLowerBoundaryReached, - HouseTemperatureUpperBoundaryReached, + HouseTemperatureTargetOrUpperBoundaryReached, } import edu.ie3.simona.test.common.UnitSpec import edu.ie3.util.scala.quantities.DefaultQuantities.{zeroKW, zeroKWh} @@ -92,12 +92,14 @@ class ThermalGridWithHouseOnlySpec extends UnitSpec with ThermalHouseTestData { val expectedHouseDemand = thermalHouse.energyDemand( relevantData, expectedHouseStartingState, + thermalHouse.targetTemperature, ) val (thermalDemands, updatedThermalGridState) = thermalGrid.energyDemandAndUpdatedState( relevantData, lastHpState, + false, ) val houseDemand = thermalDemands.houseDemand @@ -133,6 +135,7 @@ class ThermalGridWithHouseOnlySpec extends UnitSpec with ThermalHouseTestData { relevantData, testGridAmbientTemperature, gridState, + false, externalQDot, ) @@ -163,6 +166,7 @@ class ThermalGridWithHouseOnlySpec extends UnitSpec with ThermalHouseTestData { relevantData, testGridAmbientTemperature, gridState, + false, testGridQDotConsumption, ) @@ -193,7 +197,10 @@ class ThermalGridWithHouseOnlySpec extends UnitSpec with ThermalHouseTestData { 0L, testGridAmbientTemperature, ) - val gridState = ThermalGrid.startingState(thermalGrid) + val gridState = ThermalGridState( + Some(ThermalHouseState(-1, Celsius(17), zeroKW)), + None, + ) val (updatedGridState, reachedThreshold) = thermalGrid invokePrivate handleInfeed( @@ -201,6 +208,7 @@ class ThermalGridWithHouseOnlySpec extends UnitSpec with ThermalHouseTestData { testGridAmbientTemperature, gridState, isNotRunning, + false, testGridQDotInfeed, onlyThermalDemandOfHouse, ) @@ -211,12 +219,12 @@ class ThermalGridWithHouseOnlySpec extends UnitSpec with ThermalHouseTestData { None, ) => tick shouldBe 0L - innerTemperature should approximate(Celsius(18.9999d)) + innerTemperature should approximate(Celsius(16.9999d)) qDot should approximate(testGridQDotInfeed) case _ => fail("Thermal grid state has been calculated wrong.") } reachedThreshold shouldBe Some( - HouseTemperatureUpperBoundaryReached(7372L) + HouseTemperatureTargetOrUpperBoundaryReached(7322L) ) } } @@ -224,12 +232,17 @@ class ThermalGridWithHouseOnlySpec extends UnitSpec with ThermalHouseTestData { "updating the grid state dependent on the given thermal infeed" should { val relevantData = HpRelevantData(0, testGridAmbientTemperature) "deliver proper result, if energy is fed into the grid" in { + val gridState = ThermalGridState( + Some(ThermalHouseState(-1, Celsius(17), zeroKW)), + None, + ) thermalGrid.updateState( relevantData, - ThermalGrid.startingState(thermalGrid), + gridState, testGridAmbientTemperature, isRunning, + false, testGridQDotInfeed, onlyThermalDemandOfHouse, ) match { @@ -238,12 +251,14 @@ class ThermalGridWithHouseOnlySpec extends UnitSpec with ThermalHouseTestData { Some(ThermalHouseState(tick, innerTemperature, qDot)), None, ), - Some(HouseTemperatureUpperBoundaryReached(thresholdTick)), + Some( + HouseTemperatureTargetOrUpperBoundaryReached(thresholdTick) + ), ) => tick shouldBe 0L - innerTemperature should approximate(Celsius(18.9999d)) + innerTemperature should approximate(Celsius(16.9999d)) qDot should approximate(testGridQDotInfeed) - thresholdTick shouldBe 7372L + thresholdTick shouldBe 7322L case _ => fail("Thermal grid state updated failed") } } @@ -254,6 +269,7 @@ class ThermalGridWithHouseOnlySpec extends UnitSpec with ThermalHouseTestData { ThermalGrid.startingState(thermalGrid), testGridAmbientTemperature, isNotRunning, + false, testGridQDotConsumption, onlyThermalDemandOfHouse, ) match { @@ -278,6 +294,7 @@ class ThermalGridWithHouseOnlySpec extends UnitSpec with ThermalHouseTestData { ThermalGrid.startingState(thermalGrid), testGridAmbientTemperature, isNotRunning, + false, zeroKW, onlyThermalDemandOfHouse, ) match { 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 2c0b6b0771..c222dac62d 100644 --- a/src/test/scala/edu/ie3/simona/model/thermal/ThermalGridWithStorageOnlySpec.scala +++ b/src/test/scala/edu/ie3/simona/model/thermal/ThermalGridWithStorageOnlySpec.scala @@ -98,6 +98,7 @@ class ThermalGridWithStorageOnlySpec thermalGrid.energyDemandAndUpdatedState( relevantData, lastHpState, + false, ) val houseDemand = thermalDemands.houseDemand val storageDemand = thermalDemands.heatStorageDemand @@ -134,6 +135,7 @@ class ThermalGridWithStorageOnlySpec thermalGrid.energyDemandAndUpdatedState( relevantData, lastHpState, + false, ) val houseDemand = thermalDemands.houseDemand val storageDemand = thermalDemands.heatStorageDemand @@ -177,6 +179,7 @@ class ThermalGridWithStorageOnlySpec relevantData, testGridAmbientTemperature, gridState, + false, testGridQDotConsumptionHigh, ) @@ -213,6 +216,7 @@ class ThermalGridWithStorageOnlySpec testGridAmbientTemperature, gridState, isRunning, + false, testGridQDotInfeed, onlyThermalDemandOfHeatStorage, ) @@ -250,6 +254,7 @@ class ThermalGridWithStorageOnlySpec testGridAmbientTemperature, gridState, isNotRunning, + false, testGridQDotInfeed, onlyThermalDemandOfHeatStorage, ) @@ -277,6 +282,7 @@ class ThermalGridWithStorageOnlySpec ThermalGrid.startingState(thermalGrid), testGridAmbientTemperature, isRunning, + false, testGridQDotInfeed, onlyThermalDemandOfHeatStorage, ) @@ -311,6 +317,7 @@ class ThermalGridWithStorageOnlySpec ), testGridAmbientTemperature, isRunning, + false, testGridQDotConsumptionHigh, onlyThermalDemandOfHouse, ) match { @@ -335,6 +342,7 @@ class ThermalGridWithStorageOnlySpec ThermalGrid.startingState(thermalGrid), testGridAmbientTemperature, isRunning, + false, zeroKW, noThermalDemand, ) diff --git a/src/test/scala/edu/ie3/simona/model/thermal/ThermalHouseSpec.scala b/src/test/scala/edu/ie3/simona/model/thermal/ThermalHouseSpec.scala index 4438036f06..20b290835a 100644 --- a/src/test/scala/edu/ie3/simona/model/thermal/ThermalHouseSpec.scala +++ b/src/test/scala/edu/ie3/simona/model/thermal/ThermalHouseSpec.scala @@ -37,7 +37,8 @@ class ThermalHouseSpec extends UnitSpec with HpInputTestData { (17d, false, true), (17.98d, false, true), (18d, false, true), - (20d, false, false), + (19.98d, false, false), + (20d, true, false), (22d, true, false), (22.02d, true, false), (23d, true, false), @@ -46,7 +47,10 @@ class ThermalHouseSpec extends UnitSpec with HpInputTestData { forAll(testCases) { (innerTemperature: Double, isTooHigh: Boolean, isTooLow: Boolean) => val innerTemp = Temperature(innerTemperature, Celsius) - val isHigher = thermalHouseTest.isInnerTemperatureTooHigh(innerTemp) + val isHigher = thermalHouseTest.isInnerTemperatureTooHigh( + innerTemp, + thermalHouseTest.targetTemperature, + ) val isLower = thermalHouseTest.isInnerTemperatureTooLow(innerTemp) isHigher shouldBe isTooHigh @@ -83,6 +87,7 @@ class ThermalHouseSpec extends UnitSpec with HpInputTestData { initialHousestate, lastAmbientTemperature, zeroKW, + house.targetTemperature, ) thermalHouseState match { From be2830f02fd12c26594a4d7c2388fb5e00396a89 Mon Sep 17 00:00:00 2001 From: danielfeismann Date: Thu, 13 Feb 2025 18:35:03 +0100 Subject: [PATCH 2/3] use correct hp when applying for weather service within EmAgentIT --- src/test/scala/edu/ie3/simona/agent/em/EmAgentIT.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 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 599fe4aa4b..35ea20ad99 100644 --- a/src/test/scala/edu/ie3/simona/agent/em/EmAgentIT.scala +++ b/src/test/scala/edu/ie3/simona/agent/em/EmAgentIT.scala @@ -543,8 +543,8 @@ class EmAgentIT weatherService.expectMessage( RegisterForWeatherMessage( - hpInputModel.getNode.getGeoPosition.getY, - hpInputModel.getNode.getGeoPosition.getX, + adaptedHpInputModel.getNode.getGeoPosition.getY, + adaptedHpInputModel.getNode.getGeoPosition.getX, ) ) From 59e7bdf79a5fb7b2c6a795ba1314d566f5f462df Mon Sep 17 00:00:00 2001 From: danielfeismann Date: Fri, 14 Feb 2025 10:35:04 +0100 Subject: [PATCH 3/3] fix to use correct temperature when checking if innerTemperature is too high --- src/main/scala/edu/ie3/simona/model/thermal/ThermalGrid.scala | 2 +- src/main/scala/edu/ie3/simona/model/thermal/ThermalHouse.scala | 2 +- 2 files 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 8cee6f89fd..a6394e17b7 100644 --- a/src/main/scala/edu/ie3/simona/model/thermal/ThermalGrid.scala +++ b/src/main/scala/edu/ie3/simona/model/thermal/ThermalGrid.scala @@ -535,7 +535,7 @@ final case class ThermalGrid( if ( thermalHouse.isInnerTemperatureTooHigh( newState.innerTemperature, - thermalHouse.targetTemperature, + actualTargetTemperature ) ) { val (fullHouseState, maybeFullHouseThreshold) = diff --git a/src/main/scala/edu/ie3/simona/model/thermal/ThermalHouse.scala b/src/main/scala/edu/ie3/simona/model/thermal/ThermalHouse.scala index 0a20bfc415..8f7dd667eb 100644 --- a/src/main/scala/edu/ie3/simona/model/thermal/ThermalHouse.scala +++ b/src/main/scala/edu/ie3/simona/model/thermal/ThermalHouse.scala @@ -114,7 +114,7 @@ final case class ThermalHouse( zeroKWh val possibleEnergy = - if (!isInnerTemperatureTooHigh(currentInnerTemp, targetTemperature)) { + if (!isInnerTemperatureTooHigh(currentInnerTemp, actualTargetTemperature)) { energy(actualTargetTemperature, currentInnerTemp) } else zeroKWh