Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Unify coordinate class [#1516](https://github.com/ie3-institute/simona/issues/1516)
- Remove type parameters from data and message classes [#1524](https://github.com/ie3-institute/simona/issues/1524)
- Adapt ThermalHouse and HP flexibility behaviour [#1391](https://github.com/ie3-institute/simona/issues/1391)
- Refactored method to handle feed in within `ThermalGrid` [#1554](https://github.com/ie3-institute/simona/issues/1554)

### Fixed
- Fixes in Documentation, ScalaDocs, Code Style and more [#1397](https://github.com/ie3-institute/simona/issues/1397)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/*
* © 2025. TU Dortmund University,
* Institute of Energy Systems, Energy Efficiency and Energy Economics,
* Research group Distribution grid planning and operation
*/

package edu.ie3.simona.model.thermal

import edu.ie3.simona.model.participant.hp.HpModel.HpState
import edu.ie3.util.scala.quantities.DefaultQuantities.zeroKW

private case class ThermalDemandConditions(
shouldContinueHouseHeating: Boolean,
houseDemand: Boolean,
heatStorageDemand: Boolean,
housePossible: Boolean,
heatStoragePossible: Boolean,
houseHeatedLastState: Boolean,
)
Comment on lines +12 to +19
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe you could add some scaladoc here.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, it's private case class and I guess this should be quite clear as it is. Isn't it?


private object ThermalDemandConditions {

/** Handles the case, when a grid has feed in. Depending on which entity has
* some heat demand the house or the storage will be heated up / filled up.
* First the actions from last operating point will be considered and checked
* if the behaviour should be continued. This might be the case, if we got
* activated by updated weather data. If this is not the case, all other
* cases will be handled.
*/
def from(state: HpState): ThermalDemandConditions = {
val lastOperatingPoint = state.lastHpOperatingPoint.thermalOps
val houseDemand = state.thermalDemands.houseDemand
val heatStorageDemand = state.thermalDemands.heatStorageDemand

val isHouseHeatedLastState =
lastOperatingPoint.qDotHouse > zeroKW && lastOperatingPoint.qDotHp > zeroKW

ThermalDemandConditions(
/* Consider the action in the last state
* We can continue using the qDots from last operating point to keep continuity.
* If the house was heated in lastState and has still some demand.
*/
shouldContinueHouseHeating =
lastOperatingPoint.qDotHouse > zeroKW && houseDemand.hasPossibleDemand,
houseDemand = houseDemand.hasRequiredDemand,
heatStorageDemand =
heatStorageDemand.hasRequiredDemand || heatStorageDemand.hasPossibleDemand,
housePossible = houseDemand.hasPossibleDemand,
heatStoragePossible = heatStorageDemand.hasPossibleDemand,
houseHeatedLastState = isHouseHeatedLastState,
)
}
}
49 changes: 23 additions & 26 deletions src/main/scala/edu/ie3/simona/model/thermal/ThermalGrid.scala
Original file line number Diff line number Diff line change
Expand Up @@ -170,14 +170,12 @@ final case class ThermalGrid(
qDot: Power,
): (ThermalGridOperatingPoint, Option[ThermalThreshold]) = {
// TODO: We would need to issue a storage result model here...
val conditions = ThermalDemandConditions.from(state)
val strategy = selectFeedInStrategy(conditions)
val (qDotHouse, qDotHeatStorage) = strategy(qDot)

handleCase(state, qDotHouse, qDotHeatStorage)

/* Consider the action in the last state
We can continue using the qDots from last operating point to keep continuity.
If the house was heated in lastState and has still some demand. */
if state.lastHpOperatingPoint.thermalOps.qDotHouse > zeroKW && state.thermalDemands.houseDemand.hasPossibleDemand
then handleCase(state, qDot, zeroKW)
// or finally check for all other cases.
else handleFinalFeedInCases(state, qDot)
}

/** Handles the last cases of [[ThermalGrid.handleFeedIn]], where the thermal
Expand Down Expand Up @@ -210,27 +208,26 @@ final case class ThermalGrid(
* | 3 | else if house.addD | house |
* | 4 | else | no output |
*
* @param state
* State of the heat pump.
* @param qDot
* Feed in to the grid from thermal generation (e.g. heat pump) or thermal
* storages.
* @param conditions
* The ThermalDemandConditions, describing the current status of heat
* demand of the grid elements.
* @return
* The operating point of the thermal grid and the thermalThreshold if
* there is one.
* The FeedInStrategy how to distribute the qDot from the heat source.
*/
private def handleFinalFeedInCases(
state: HpState,
qDot: Power,
): (ThermalGridOperatingPoint, Option[ThermalThreshold]) = {

if state.thermalDemands.houseDemand.hasRequiredDemand then
handleCase(state, qDot, zeroKW)
else if state.thermalDemands.heatStorageDemand.hasRequiredDemand || state.thermalDemands.heatStorageDemand.hasPossibleDemand
then handleCase(state, zeroKW, qDot)
else if state.thermalDemands.houseDemand.hasPossibleDemand then
handleCase(state, qDot, zeroKW)
else handleCase(state, zeroKW, zeroKW)
private def selectFeedInStrategy(
conditions: ThermalDemandConditions
): FeedInStrategy = {
if conditions.shouldContinueHouseHeating then {
HouseOnlyStrategy
} else if conditions.houseDemand then {
HouseOnlyStrategy
} else if conditions.heatStorageDemand then {
HeatStorageOnlyStrategy
} else if conditions.housePossible then {
HouseOnlyStrategy
} else {
NoOperationStrategy
}
}

/** Handles the different thermal flows from and into the thermal grid.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
* © 2025. TU Dortmund University,
* Institute of Energy Systems, Energy Efficiency and Energy Economics,
* Research group Distribution grid planning and operation
*/

package edu.ie3.simona.model.thermal

import edu.ie3.util.scala.quantities.DefaultQuantities.zeroKW
import squants.Power

/** Trait to provide a feed-in strategy for handling thermal infeed (qDot).
*/
private sealed trait FeedInStrategy {
def apply(
qDot: Power
): (Power, Power) // (house, heatStorage)
}

private object HouseOnlyStrategy extends FeedInStrategy {
override def apply(qDot: Power): (Power, Power) =
(qDot, zeroKW)
}

private object HeatStorageOnlyStrategy extends FeedInStrategy {
override def apply(qDot: Power): (Power, Power) =
(zeroKW, qDot)
}

private object NoOperationStrategy extends FeedInStrategy {
override def apply(qDot: Power): (Power, Power) =
(zeroKW, zeroKW)
}