Skip to content

Commit

Permalink
Merge branch 'refs/heads/df/#827_fix_hp_storage_refill' into df/#856-…
Browse files Browse the repository at this point in the history
…tap-water

# Conflicts:
#	CHANGELOG.md
#	src/main/scala/edu/ie3/simona/model/thermal/ThermalGrid.scala
#	src/test/scala/edu/ie3/simona/model/thermal/ThermalGridWithHouseOnlySpec.scala
  • Loading branch information
danielfeismann committed Aug 8, 2024
2 parents 33a2be9 + 68075ac commit f50e027
Show file tree
Hide file tree
Showing 12 changed files with 300 additions and 131 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Implementation of StorageAgent [#309](https://github.com/ie3-institute/simona/issues/309)
- Enhanced Newton-Raphson-PowerFlow failures with more information [#815](https://github.com/ie3-institute/simona/issues/815)
- Update RTD references and bibliography [#868](https://github.com/ie3-institute/simona/issues/868)
- Add gradle application plugin for command line execution with gradle run [#890](https://github.com/ie3-institute/simona/issues/890)

### Changed
- Adapted to changed data source in PSDM [#435](https://github.com/ie3-institute/simona/issues/435)
Expand Down Expand Up @@ -65,6 +66,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Rewrote SystemComponentTest from groovy to scala [#646](https://github.com/ie3-institute/simona/issues/646)
- Converting remaining rst files to markdown [#838](https://github.com/ie3-institute/simona/issues/838)
- Merging both `FixedFeedInModelSpec` tests [#870](https://github.com/ie3-institute/simona/issues/870)
- Prepare ThermalStorageTestData for Storage without storageVolumeLvlMin [#894](https://github.com/ie3-institute/simona/issues/894)

### Fixed
- Removed a repeated line in the documentation of vn_simona config [#658](https://github.com/ie3-institute/simona/issues/658)
Expand All @@ -88,6 +90,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Fixed result output for thermal houses and cylindrical storages [#844](https://github.com/ie3-institute/simona/issues/844)
- Fixed FixedFeedModelSpec [#861](https://github.com/ie3-institute/simona/issues/861)
- Fixing duration calculation in result events [#801](https://github.com/ie3-institute/simona/issues/801)
- Handle MobSim requests for current prices [#892](https://github.com/ie3-institute/simona/issues/892)
- Fixed 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)

Expand Down
6 changes: 6 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ plugins {
id "com.github.maiflai.scalatest" version "0.32" // run scalatest without specific spec task
id 'org.hidetake.ssh' version '2.11.2'
id 'net.thauvin.erik.gradle.semver' version '1.0.4' // semantic versioning
id "application"
}

ext {
Expand Down Expand Up @@ -166,6 +167,11 @@ jar {
}
}

// Run with ./gradlew run --args='--config /path/to/simona.conf'
application {
mainClassName = jar.manifest.attributes.get('Main-Class')
}

//////////////////////////////////////////////////////////////////////
// Build pekko'able fat jar using the gradle shadow plugin
// see http://www.sureshpw.com/2015/10/building-akka-bundle-with-all.html
Expand Down
2 changes: 1 addition & 1 deletion docs/readthedocs/requirements.txt
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
Sphinx==7.3.7
sphinx-rtd-theme==2.0.0
sphinxcontrib-plantuml==0.30
myst-parser==3.0.1
myst-parser==4.0.0
markdown-it-py==3.0.0
sphinx-hoverxref==1.4.0
sphinxcontrib-bibtex==2.6.2
66 changes: 42 additions & 24 deletions src/main/scala/edu/ie3/simona/model/participant/HpModel.scala
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import edu.ie3.util.quantities.PowerSystemUnits
import edu.ie3.util.scala.OperationInterval
import edu.ie3.util.scala.quantities.DefaultQuantities
import edu.ie3.util.scala.quantities.DefaultQuantities._
import squants.energy.Kilowatts
import squants.energy.{Energy, KilowattHours, Kilowatts}
import squants.{Power, Temperature}

import java.time.ZonedDateTime
Expand Down Expand Up @@ -153,18 +153,22 @@ final case class HpModel(
state: HpState,
relevantData: HpRelevantData,
): (Boolean, ThermalEnergyDemand, ThermalEnergyDemand) = {
val (demandHouse, demandStorage) = thermalGrid.energyDemand(
relevantData.currentTick,
relevantData.ambientTemperature,
state.thermalGridState,
)
val (demandHouse, demandStorage, updatedState) =
thermalGrid.energyDemandAndUpdatedState(
relevantData.currentTick,
relevantData.ambientTemperature,
state.thermalGridState,
)
implicit val tolerance: Energy = KilowattHours(1e-3)
val noStorageOrStorageIsEmpty: Boolean =
updatedState.storageState.isEmpty || updatedState.storageState.exists(
_.storedEnergy =~ zeroKWH
)

val storedEnergy = state.thermalGridState.storageState
.map(storageState => storageState.storedEnergy)
.getOrElse(zeroKWH)
val turnHpOn: Boolean = {
(demandHouse.hasRequiredDemand && noStorageOrStorageIsEmpty) || demandStorage.hasRequiredDemand || (state.isRunning && demandHouse.hasAdditionalDemand) || (state.isRunning && demandStorage.hasAdditionalDemand)

val turnHpOn =
(demandHouse.hasRequiredDemand && demandHouse.required > storedEnergy) || demandStorage.hasRequiredDemand || (state.isRunning && demandHouse.hasAdditionalDemand) || (state.isRunning && demandStorage.hasAdditionalDemand)
}

(
turnHpOn,
Expand Down Expand Up @@ -197,10 +201,16 @@ final case class HpModel(
houseDemand: ThermalEnergyDemand,
storageDemand: ThermalEnergyDemand,
): HpState = {
val lastStateStorageqDot = state.thermalGridState.storageState
.map(_.qDot)
.getOrElse(zeroKW)

val (newActivePower, newThermalPower) =
if (isRunning)
(pRated, pThermal)
else (DefaultQuantities.zeroKW, DefaultQuantities.zeroKW)
else if (lastStateStorageqDot < zeroKW)
(zeroKW, lastStateStorageqDot * (-1))
else (zeroKW, zeroKW)

/* Push thermal energy to the thermal grid and get its updated state in return */
val (thermalGridState, maybeThreshold) =
Expand Down Expand Up @@ -229,26 +239,30 @@ final case class HpModel(
lastState: HpState,
): ProvideFlexOptions = {
/* Determine the operating state in the given tick */
val updatedState = determineState(lastState, data)
val updatedHpState: HpState = determineState(lastState, data)

/* Determine the options we have */
val (thermalEnergyDemandHouse, thermalEnergyDemandStorge) =
thermalGrid.energyDemand(
val (
thermalEnergyDemandHouse,
thermalEnergyDemandStorage,
updatedThermalGridState,
) =
thermalGrid.energyDemandAndUpdatedState(
data.currentTick,
data.ambientTemperature,
lastState.thermalGridState,
)
val canOperate =
thermalEnergyDemandHouse.hasRequiredDemand || thermalEnergyDemandHouse.hasAdditionalDemand ||
thermalEnergyDemandStorge.hasRequiredDemand || thermalEnergyDemandStorge.hasAdditionalDemand
thermalEnergyDemandStorage.hasRequiredDemand || thermalEnergyDemandStorage.hasAdditionalDemand
val canBeOutOfOperation =
!thermalEnergyDemandHouse.hasRequiredDemand && !thermalEnergyDemandStorge.hasRequiredDemand
!thermalEnergyDemandHouse.hasRequiredDemand && !thermalEnergyDemandStorage.hasRequiredDemand

val lowerBoundary =
if (canBeOutOfOperation)
zeroKW
else
updatedState.activePower
updatedHpState.activePower
val upperBoundary =
if (canOperate)
sRated * cosPhiRated
Expand All @@ -257,7 +271,7 @@ final case class HpModel(

ProvideMinMaxFlexOptions(
uuid,
updatedState.activePower,
updatedHpState.activePower,
lowerBoundary,
upperBoundary,
)
Expand Down Expand Up @@ -288,14 +302,18 @@ 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, thermalEnergyDemandStorage) =
thermalGrid.energyDemand(
val (
thermalEnergyDemandHouse,
thermalEnergyDemandStorage,
updatedThermalGridState,
) =
thermalGrid.energyDemandAndUpdatedState(
data.currentTick,
data.ambientTemperature,
lastState.thermalGridState,
)

val updatedState =
val updatedHpState: HpState =
calcState(
lastState,
data,
Expand All @@ -305,10 +323,10 @@ final case class HpModel(
)

(
updatedState,
updatedHpState,
FlexChangeIndicator(
changesAtNextActivation = true,
updatedState.maybeThermalThreshold.map(_.tick),
updatedHpState.maybeThermalThreshold.map(_.tick),
),
)
}
Expand Down
86 changes: 50 additions & 36 deletions src/main/scala/edu/ie3/simona/model/thermal/ThermalGrid.scala
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ final case class ThermalGrid(
) extends LazyLogging {

/** Determine the energy demand of the total grid at the given instance in
* time
* time and returns it including the updatedState
*
* @param tick
* Questioned instance in time
Expand All @@ -53,47 +53,48 @@ final case class ThermalGrid(
* @param state
* Currently applicable state of the thermal grid
* @return
* The total energy demand of the house and the storage
* The total energy demand of the house and the storage and an updated
* [[ThermalGridState]]
*/
def energyDemand(
def energyDemandAndUpdatedState(
tick: Long,
ambientTemperature: Temperature,
state: ThermalGridState,
): (ThermalEnergyDemand, ThermalEnergyDemand) = {
): (ThermalEnergyDemand, ThermalEnergyDemand, ThermalGridState) = {
/* First get the energy demand of the houses but only if inner temperature is below target temperature */

val houseDemand =
val (houseDemand, updatedHouseState) =
house.zip(state.houseState).headOption match {
case Some((thermalHouse, lastHouseState)) =>
val updatedState = thermalHouse.determineState(
tick,
lastHouseState,
ambientTemperature,
lastHouseState.qDot,
)
val (updatedHouseState, updatedStorageState) =
thermalHouse.determineState(
tick,
lastHouseState,
ambientTemperature,
lastHouseState.qDot,
)
if (
updatedState._1.innerTemperature < thermalHouse.targetTemperature
updatedHouseState.innerTemperature < thermalHouse.targetTemperature
) {
house
.zip(state.houseState)
.map { case (house, state) =>
house.energyDemandHeating(
tick,
ambientTemperature,
state,
)
}
.getOrElse(ThermalEnergyDemand.noDemand)
(
thermalHouse.energyDemandHeating(
tick,
ambientTemperature,
updatedHouseState,
),
Some(updatedHouseState),
)

} else {
ThermalEnergyDemand.noDemand
(ThermalEnergyDemand.noDemand, Some(updatedHouseState))
}

case None =>
ThermalEnergyDemand.noDemand
(ThermalEnergyDemand.noDemand, None)
}

/* Then go over the storages, see what they can provide and what they might be able to charge */
val storageDemand = {
val (storageDemand, updatedStorageState) = {

storage
.zip(state.storageState)
Expand All @@ -102,21 +103,28 @@ final case class ThermalGrid(
storage.updateState(tick, state.qDot, state)._1
val storedEnergy = updatedStorageState.storedEnergy
val soc = storedEnergy / storage.getMaxEnergyThreshold
val storageRequired =
if (soc < 0.5) {
storage.getMaxEnergyThreshold * 0.5 - storedEnergy
val storageRequired = {
if (soc == 0d) {
storage.getMaxEnergyThreshold - storedEnergy

} else {
zeroMWH
}
}

val storagePossible = storage.getMaxEnergyThreshold - storedEnergy
ThermalEnergyDemand(
storageRequired,
storagePossible,
(
ThermalEnergyDemand(
storageRequired,
storagePossible,
),
Some(updatedStorageState),
)

}
.getOrElse(
ThermalEnergyDemand(zeroMWH, zeroMWH)
ThermalEnergyDemand(zeroMWH, zeroMWH),
None,
)
}

Expand All @@ -129,6 +137,7 @@ final case class ThermalGrid(
storageDemand.required,
storageDemand.possible,
),
ThermalGridState(updatedHouseState, updatedStorageState),
)
}

Expand Down Expand Up @@ -270,11 +279,15 @@ final case class ThermalGrid(
)
}

if (qDotHouseLastState > zeroKW | qDotStorageLastState > zeroKW) {
if (
(qDotHouseLastState > zeroKW && qDotHouseLastState == qDot) | qDotStorageLastState > zeroKW
) {
val (updatedHouseState, thermalHouseThreshold, remainingQDotHouse) =
handleInfeedHouse(tick, ambientTemperature, state, qDotHouseLastState)
val (updatedStorageState, thermalStorageThreshold) =
if (remainingQDotHouse > qDotStorageLastState) {
if (
qDotStorageLastState >= zeroKW && remainingQDotHouse > qDotStorageLastState
) {
handleInfeedStorage(
tick,
ambientTemperature,
Expand Down Expand Up @@ -391,8 +404,9 @@ final case class ThermalGrid(
}
}

/** Handles the case, when the storage has heat demand and will be filled up
* here.
/** Handles the cases, when the storage has heat demand and will be filled up
* here (positive qDot) or will be return its stored energy into the thermal
* grid (negative qDot).
* @param tick
* Current tick
* @param ambientTemperature
Expand Down
24 changes: 23 additions & 1 deletion src/main/scala/edu/ie3/simona/service/ev/ExtEvDataService.scala
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,8 @@ class ExtEvDataService(override val scheduler: ActorRef)
"ExtEvDataService was triggered without ExtEvMessage available"
)
) match {
case _: RequestCurrentPrices =>
requestCurrentPrices()
case _: RequestEvcsFreeLots =>
requestFreeLots(tick)
case departingEvsRequest: RequestDepartingEvs =>
Expand All @@ -192,6 +194,26 @@ class ExtEvDataService(override val scheduler: ActorRef)
}
}

private def requestCurrentPrices()(implicit
serviceStateData: ExtEvStateData
): (ExtEvStateData, Option[Long]) = {
// currently not supported, return dummy
val dummyPrice = double2Double(0d)
val prices = serviceStateData.uuidToActorRef.map { case (evcs, _) =>
evcs -> dummyPrice
}
serviceStateData.extEvData.queueExtResponseMsg(
new ProvideCurrentPrices(prices.asJava)
)

(
serviceStateData.copy(
extEvMessage = None
),
None,
)
}

private def requestFreeLots(tick: Long)(implicit
serviceStateData: ExtEvStateData
): (ExtEvStateData, Option[Long]) = {
Expand Down Expand Up @@ -351,7 +373,7 @@ class ExtEvDataService(override val scheduler: ActorRef)
freeLotsCount > 0
}
.map { case (evcs, freeLotsCount) =>
evcs -> Integer.valueOf(freeLotsCount)
evcs -> int2Integer(freeLotsCount)
}

serviceStateData.extEvData.queueExtResponseMsg(
Expand Down
Loading

0 comments on commit f50e027

Please sign in to comment.