Skip to content

Commit

Permalink
Merge remote-tracking branch 'refs/remotes/origin/df/#729-additional-…
Browse files Browse the repository at this point in the history
…tests-thermal-house' into df/#856-tap-water

# Conflicts:
#	CHANGELOG.md
#	src/test/scala/edu/ie3/simona/model/participant/HpModelSpec.scala
#	src/test/scala/edu/ie3/simona/model/thermal/ThermalGridWithHouseAndStorageSpec.scala
  • Loading branch information
danielfeismann committed Aug 21, 2024
2 parents be1b45a + e74c5dd commit feefb09
Show file tree
Hide file tree
Showing 5 changed files with 220 additions and 45 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- 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)
- Additional tests to check flexibility options of thermal house and storage [#729](https://github.com/ie3-institute/simona/issues/729)
- Integration test for thermal grids [#878](https://github.com/ie3-institute/simona/issues/878)

### Changed
Expand Down
1 change: 1 addition & 0 deletions docs/readthedocs/models.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ models/two_winding_transformer_model
models/three_winding_transformer_model
models/reference_system
models/thermal_grid_model
models/thermal_house_model
```

## System Participant Related Models
Expand Down
2 changes: 2 additions & 0 deletions docs/readthedocs/models/thermal_grid_model.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@

The Thermal Grid Model introduces a coupling point to thermal system, equivalent to an electrical node. It can be used to interconnect thermal units to a thermal heat network.

A Thermal Grid Model consists of a {ref}`thermal_house_model` and / or a {ref}`cts_model` which are supplied by a thermal source like a {ref}`hp_model`.

## Attributes, Units and Remarks

Please refer to {doc}`PowerSystemDataModel - Thermal Bus <psdm:models/input/thermal/thermalbus>` for Attributes and Units used in this Model.
13 changes: 13 additions & 0 deletions docs/readthedocs/models/thermal_house_model.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
(thermal_house_model)=

# Thermal House Model

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 shoe box with a thermal capacity and with transmission losses.

## Attributes, Units and Remarks

Please refer to {doc}`PowerSystemDataModel - Thermal House Model <psdm:models/input/thermal/thermalhouse>` for Attributes and Units used in this Model.
248 changes: 203 additions & 45 deletions src/test/scala/edu/ie3/simona/model/participant/HpModelSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import edu.ie3.simona.ontology.messages.flex.MinMaxFlexibilityMessage.ProvideMin
import edu.ie3.simona.test.common.UnitSpec
import edu.ie3.simona.test.common.input.HpInputTestData
import edu.ie3.util.scala.quantities.WattsPerKelvin
import org.scalatest.prop.TableDrivenPropertyChecks
import org.scalatest.prop.{TableDrivenPropertyChecks, TableFor3}
import squants.energy.{KilowattHours, Kilowatts, Watts}
import squants.thermal.Celsius
import squants.{Kelvin, Power, Temperature}
Expand Down Expand Up @@ -232,56 +232,214 @@ class HpModelSpec
}
}

"determining the flexibility options" when {
"the house is heated up and storage has space" should {
"deliver positive flexibility" in {
val house = thermalHouse(18, 22)
.copy(ethLosses = WattsPerKelvin(200))
val grid = thermalGrid(house, Some(thermalStorage), None)
val hp = hpModel(grid)
// Tick, at which the house is heated up
val relevantData = hpData.copy(currentTick = 2763L)
val thermalState = ThermalGridState(
Some(
ThermalHouseState(
0L,
Celsius(21),
Kilowatts(80),
)
"determining the flexibility options for different states" should {
"deliver correct flexibility options" in {
val testCases
: TableFor3[ThermalGridState, HpState, (Double, Double, Double)] =
Table(
("thermalState", "lastState", "expectedValues"),
// House is below lower temperature boundary
(
ThermalGridState(
Some(ThermalHouseState(0L, Celsius(15), Kilowatts(0))),
Some(ThermalStorageState(0L, KilowattHours(20), Kilowatts(0))),
),
HpState(
isRunning = false,
0,
Some(hpData.ambientTemperature),
Kilowatts(0.0),
Kilowatts(0.0),
ThermalGridState(
Some(ThermalHouseState(0L, Celsius(15), Kilowatts(0))),
Some(
ThermalStorageState(0L, KilowattHours(20), Kilowatts(0))
),
),
None,
),
(95.0, 95.0, 95.0),
),
Some(
ThermalStorageState(
0L,
KilowattHours(250),
Kilowatts(0),
)
// House is between target temperature and lower temperature boundary, Hp actually running
(
ThermalGridState(
Some(ThermalHouseState(0L, Celsius(19), Kilowatts(0))),
Some(ThermalStorageState(0L, KilowattHours(20), Kilowatts(0))),
),
HpState(
isRunning = true,
0,
Some(hpData.ambientTemperature),
Kilowatts(95.0),
Kilowatts(80.0),
ThermalGridState(
Some(ThermalHouseState(0L, Celsius(19), Kilowatts(80))),
Some(
ThermalStorageState(0L, KilowattHours(20), Kilowatts(0))
),
),
None,
),
(95.0, 0.0, 95.0),
),

// House is between target temperature and lower temperature boundary, Hp actually not running
(
ThermalGridState(
Some(ThermalHouseState(0L, Celsius(19), Kilowatts(0))),
Some(ThermalStorageState(0L, KilowattHours(20), Kilowatts(0))),
),
HpState(
isRunning = false,
0,
Some(hpData.ambientTemperature),
Kilowatts(0.0),
Kilowatts(0.0),
ThermalGridState(
Some(ThermalHouseState(0L, Celsius(19), Kilowatts(0))),
Some(
ThermalStorageState(0L, KilowattHours(20), Kilowatts(0))
),
),
None,
),
(0.0, 0.0, 95.0),
),
// Storage and house have remaining capacity
(
ThermalGridState(
Some(ThermalHouseState(0L, Celsius(21), Kilowatts(80))),
Some(ThermalStorageState(0L, KilowattHours(20), Kilowatts(0))),
),
HpState(
isRunning = true,
0,
Some(hpData.ambientTemperature),
Kilowatts(95.0),
Kilowatts(80.0),
ThermalGridState(
Some(ThermalHouseState(0L, Celsius(21), Kilowatts(80))),
Some(ThermalStorageState(0L, KilowattHours(20), Kilowatts(0))),
),
Some(HouseTemperatureUpperBoundaryReached(0L)),
),
(95.0, 0.0, 95.0),
),

// Storage is full, House has capacity till upper boundary
(
ThermalGridState(
Some(ThermalHouseState(0L, Celsius(21), Kilowatts(80))),
Some(ThermalStorageState(0L, KilowattHours(500), Kilowatts(0))),
),
HpState(
isRunning = false,
0,
Some(hpData.ambientTemperature),
Kilowatts(0.0),
Kilowatts(0.0),
ThermalGridState(
Some(ThermalHouseState(0L, Celsius(21), Kilowatts(80))),
Some(
ThermalStorageState(0L, KilowattHours(500), Kilowatts(0))
),
),
Some(HouseTemperatureUpperBoundaryReached(0L)),
),
(0.0, 0.0, 95.0),
),

// No capacity for flexibility at all because house is
// at upperTempBoundary and storage is at max capacity
(
ThermalGridState(
Some(ThermalHouseState(0L, Celsius(22), Kilowatts(80))),
Some(ThermalStorageState(0L, KilowattHours(500), Kilowatts(0))),
),
HpState(
isRunning = true,
0,
Some(hpData.ambientTemperature),
Kilowatts(95.0),
Kilowatts(80.0),
ThermalGridState(
Some(ThermalHouseState(0L, Celsius(22), Kilowatts(80))),
Some(
ThermalStorageState(0L, KilowattHours(500), Kilowatts(0))
),
),
Some(HouseTemperatureUpperBoundaryReached(0L)),
),
(0.0, 0.0, 0.0),
),

// No capacity for flexibility at all when storage is full and house has been (externally) heated up above upperTemperatureBoundary
(
ThermalGridState(
Some(ThermalHouseState(0L, Celsius(25), Kilowatts(0))),
Some(ThermalStorageState(0L, KilowattHours(500), Kilowatts(0))),
),
HpState(
isRunning = false,
0,
Some(hpData.ambientTemperature),
Kilowatts(95.0),
Kilowatts(80.0),
ThermalGridState(
Some(ThermalHouseState(0L, Celsius(25), Kilowatts(0))),
Some(
ThermalStorageState(0L, KilowattHours(500), Kilowatts(0))
),
),
None,
),
(0.0, 0.0, 0.0),
),
None,
)
val lastState = HpState(
isRunning = true,
0,
Some(hpData.ambientTemperature),
Kilowatts(95.0),
Kilowatts(80.0),
thermalState,
Some(HouseTemperatureUpperBoundaryReached(7995L)),
)

hp.determineFlexOptions(relevantData, lastState) match {
case ProvideMinMaxFlexOptions(
modelUuid,
referencePower,
minPower,
maxPower,
) =>
modelUuid shouldBe hp.uuid
referencePower should approximate(Kilowatts(95.0))
minPower should approximate(Kilowatts(0.0))
maxPower should approximate(Kilowatts(95.0))
}
// Run the test cases
forAll(testCases) {
(
thermalState: ThermalGridState,
lastState: HpState,
expectedValues: (Double, Double, Double),
) =>
val (expectedReferencePower, expectedMinPower, expectedMaxPower) =
expectedValues

// Initialize the house and grid models
val house =
thermalHouse(18, 22).copy(ethLosses = WattsPerKelvin(200))
val grid = thermalGrid(house, Some(thermalStorage))
val hp = hpModel(grid)

// Create relevant data for the current test
// As we are only testing flexOptions here, we can use tick 0
// which is also the tick of the lastState.
// This should not happen in the simulation!
// This can be simplified once the transitoryData is introduced
val relevantData = hpData.copy(currentTick =
thermalState.houseState.map(_.tick).getOrElse(0L)
)

// Invoke determineFlexOptions and match the results
hp.determineFlexOptions(relevantData, lastState) match {
case ProvideMinMaxFlexOptions(
modelUuid,
referencePower,
minPower,
maxPower,
) =>
modelUuid shouldBe hp.uuid
referencePower should approximate(
Kilowatts(expectedReferencePower)
)
minPower should approximate(Kilowatts(expectedMinPower))
maxPower should approximate(Kilowatts(expectedMaxPower))
}
}
}
}

}
}

0 comments on commit feefb09

Please sign in to comment.