Skip to content

Introduce thermal power for thermal storages #1252

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Feb 21, 2025
Merged
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 @@ -15,6 +15,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Enhance `TimeSeriesSource` with method to retrieve the previous value before a given key [#1182](https://github.com/ie3-institute/PowerSystemDataModel/issues/1182)
- Added `BdewLoadProfileTimeSeries` [#1230](https://github.com/ie3-institute/PowerSystemDataModel/issues/1230)
- Added `RandomLoadProfileTimeSeries` [#1232](https://github.com/ie3-institute/PowerSystemDataModel/issues/1232)
- Attribute `pThermalRated` for `ThermalStorage`s [#679](https://github.com/ie3-institute/PowerSystemDataModel/issues/679)

### Fixed
- Removing opened `SwitchInput` during connectivity check [#1221](https://github.com/ie3-institute/PowerSystemDataModel/issues/1221)
Expand Down
6 changes: 5 additions & 1 deletion docs/readthedocs/models/input/thermal/cylindricalstorage.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

# Cylindrical Thermal Storage

Model of a cylindrical thermal storage using a fluent to store thermal energy.
Model of a cylindrical thermal storage using a fluid to store thermal energy.

## Attributes, Units and Remarks

Expand Down Expand Up @@ -51,6 +51,10 @@ Model of a cylindrical thermal storage using a fluent to store thermal energy.
* - c
- kWh / (K :math:`\cdot` m³)
- Specific heat capacity of the storage medium

* - pThermalMax
- kW
- Maximum permissible thermal power of the storage

```

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

# Cylindrical Thermal Storage

Result of a cylindrical thermal storage using a fluent to store thermal energy.
Result of a cylindrical thermal storage using a fluid to store thermal energy.

## Attributes, Units and Remarks

Expand Down
1 change: 1 addition & 0 deletions docs/uml/main/input/ThermalDatamodelConcept.puml
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ package models {
- inletTemp: ComparableQuantity<Temperature> [°C]
- returnTemp: ComparableQuantity<Temperature> [°C]
- c: ComparableQuantity<SpecificHeatCapacity> [kWh/(K*m³)]
- pThermalMax: ComparableQuantity<Power> [kW]
}
CylindricalStorageInput --|> ThermalStorageInput
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import edu.ie3.datamodel.models.input.thermal.ThermalBusInput;
import edu.ie3.util.quantities.interfaces.SpecificHeatCapacity;
import java.util.UUID;
import javax.measure.quantity.Power;
import javax.measure.quantity.Temperature;
import javax.measure.quantity.Volume;
import tech.units.indriya.ComparableQuantity;
Expand All @@ -22,14 +23,15 @@ public class CylindricalStorageInputFactory
private static final String INLET_TEMP = "inletTemp";
private static final String RETURN_TEMP = "returnTemp";
private static final String C = "c";
private static final String P_THERMAL_MAX = "pThermalMax";

public CylindricalStorageInputFactory() {
super(CylindricalStorageInput.class);
}

@Override
protected String[] getAdditionalFields() {
return new String[] {STORAGE_VOLUME_LVL, INLET_TEMP, RETURN_TEMP, C};
return new String[] {STORAGE_VOLUME_LVL, INLET_TEMP, RETURN_TEMP, C, P_THERMAL_MAX};
}

@Override
Expand All @@ -48,7 +50,19 @@ protected CylindricalStorageInput buildModel(
data.getQuantity(RETURN_TEMP, StandardUnits.TEMPERATURE);
final ComparableQuantity<SpecificHeatCapacity> c =
data.getQuantity(C, StandardUnits.SPECIFIC_HEAT_CAPACITY);
final ComparableQuantity<Power> pThermalMax =
data.getQuantity(P_THERMAL_MAX, StandardUnits.ACTIVE_POWER_IN);

return new CylindricalStorageInput(
uuid, id, operator, operationTime, bus, storageVolumeLvl, inletTemp, returnTemp, c);
uuid,
id,
operator,
operationTime,
bus,
storageVolumeLvl,
inletTemp,
returnTemp,
c,
pThermalMax);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import edu.ie3.util.quantities.interfaces.SpecificHeatCapacity;
import java.util.Objects;
import java.util.UUID;
import javax.measure.quantity.Power;
import javax.measure.quantity.Temperature;
import javax.measure.quantity.Volume;
import tech.units.indriya.ComparableQuantity;
Expand All @@ -25,6 +26,8 @@ public class CylindricalStorageInput extends ThermalStorageInput {
private final ComparableQuantity<Temperature> returnTemp;
/** Specific heat capacity of the storage medium (typically in kWh/K*m³) */
private final ComparableQuantity<SpecificHeatCapacity> c;
/** Maximum permissible thermal power (typically in kW) */
private final ComparableQuantity<Power> pThermalMax;

/**
* @param uuid Unique identifier of a cylindrical storage
Expand All @@ -36,6 +39,7 @@ public class CylindricalStorageInput extends ThermalStorageInput {
* @param inletTemp Temperature of the inlet
* @param returnTemp Temperature of the outlet
* @param c Specific heat capacity of the storage medium
* @param pThermalMax Maximum thermal power of the storage
*/
public CylindricalStorageInput(
UUID uuid,
Expand All @@ -46,12 +50,14 @@ public CylindricalStorageInput(
ComparableQuantity<Volume> storageVolumeLvl,
ComparableQuantity<Temperature> inletTemp,
ComparableQuantity<Temperature> returnTemp,
ComparableQuantity<SpecificHeatCapacity> c) {
ComparableQuantity<SpecificHeatCapacity> c,
ComparableQuantity<Power> pThermalMax) {
super(uuid, id, operator, operationTime, bus);
this.storageVolumeLvl = storageVolumeLvl.to(StandardUnits.VOLUME);
this.inletTemp = inletTemp.to(StandardUnits.TEMPERATURE);
this.returnTemp = returnTemp.to(StandardUnits.TEMPERATURE);
this.c = c.to(StandardUnits.SPECIFIC_HEAT_CAPACITY);
this.pThermalMax = pThermalMax.to(StandardUnits.ACTIVE_POWER_IN);
}

/**
Expand All @@ -62,6 +68,7 @@ public CylindricalStorageInput(
* @param inletTemp Temperature of the inlet
* @param returnTemp Temperature of the outlet
* @param c Specific heat capacity of the storage medium
* @param pThermalMax Maximum thermal power of the storage
*/
public CylindricalStorageInput(
UUID uuid,
Expand All @@ -70,12 +77,14 @@ public CylindricalStorageInput(
ComparableQuantity<Volume> storageVolumeLvl,
ComparableQuantity<Temperature> inletTemp,
ComparableQuantity<Temperature> returnTemp,
ComparableQuantity<SpecificHeatCapacity> c) {
ComparableQuantity<SpecificHeatCapacity> c,
ComparableQuantity<Power> pThermalMax) {
super(uuid, id, bus);
this.storageVolumeLvl = storageVolumeLvl.to(StandardUnits.VOLUME);
this.inletTemp = inletTemp.to(StandardUnits.TEMPERATURE);
this.returnTemp = returnTemp.to(StandardUnits.TEMPERATURE);
this.c = c.to(StandardUnits.SPECIFIC_HEAT_CAPACITY);
this.pThermalMax = pThermalMax.to(StandardUnits.ACTIVE_POWER_IN);
}

public ComparableQuantity<Volume> getStorageVolumeLvl() {
Expand All @@ -94,6 +103,10 @@ public ComparableQuantity<SpecificHeatCapacity> getC() {
return c;
}

public ComparableQuantity<Power> getpThermalMax() {
return pThermalMax;
}

@Override
public CylindricalStorageInputCopyBuilder copy() {
return new CylindricalStorageInputCopyBuilder(this);
Expand All @@ -107,12 +120,13 @@ public boolean equals(Object o) {
return storageVolumeLvl.equals(that.storageVolumeLvl)
&& inletTemp.equals(that.inletTemp)
&& returnTemp.equals(that.returnTemp)
&& c.equals(that.c);
&& c.equals(that.c)
&& pThermalMax.equals(that.getpThermalMax());
}

@Override
public int hashCode() {
return Objects.hash(super.hashCode(), storageVolumeLvl, inletTemp, returnTemp, c);
return Objects.hash(super.hashCode(), storageVolumeLvl, inletTemp, returnTemp, c, pThermalMax);
}

@Override
Expand All @@ -136,6 +150,8 @@ public String toString() {
+ returnTemp
+ ", c="
+ c
+ ", pThermalMax="
+ pThermalMax
+ '}';
}

Expand All @@ -151,13 +167,15 @@ public static class CylindricalStorageInputCopyBuilder
private ComparableQuantity<Temperature> inletTemp;
private ComparableQuantity<Temperature> returnTemp;
private ComparableQuantity<SpecificHeatCapacity> c;
private ComparableQuantity<Power> pThermalMax;

private CylindricalStorageInputCopyBuilder(CylindricalStorageInput entity) {
super(entity);
this.storageVolumeLvl = entity.getStorageVolumeLvl();
this.inletTemp = entity.getInletTemp();
this.returnTemp = entity.getReturnTemp();
this.c = entity.getC();
this.pThermalMax = entity.getpThermalMax();
}

public CylindricalStorageInputCopyBuilder storageVolumeLvl(
Expand All @@ -182,9 +200,15 @@ public CylindricalStorageInputCopyBuilder c(ComparableQuantity<SpecificHeatCapac
return this;
}

public CylindricalStorageInputCopyBuilder pThermalMax(ComparableQuantity<Power> pThermalMax) {
this.pThermalMax = pThermalMax;
return this;
}

@Override
public CylindricalStorageInputCopyBuilder scale(Double factor) {
storageVolumeLvl(storageVolumeLvl.multiply(factor));
pThermalMax(pThermalMax.multiply(factor));
return this;
}

Expand All @@ -199,7 +223,8 @@ public CylindricalStorageInput build() {
storageVolumeLvl,
inletTemp,
returnTemp,
c);
c,
pThermalMax);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,9 @@ private static List<Try<Void, InvalidEntityException>> checkCylindricalStorage(
() ->
detectZeroOrNegativeQuantities(
new Quantity<?>[] {
cylindricalStorageInput.getStorageVolumeLvl(), cylindricalStorageInput.getC()
cylindricalStorageInput.getStorageVolumeLvl(),
cylindricalStorageInput.getC(),
cylindricalStorageInput.getpThermalMax()
},
cylindricalStorageInput),
InvalidEntityException.class));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -134,10 +134,10 @@ class ExtractorTest extends Specification {
tutd.thermalBus.operator
]

tutd.cylindricStorageInput || [
tutd.cylindricStorageInput.operator,
tutd.cylindricStorageInput.thermalBus,
tutd.cylindricStorageInput.thermalBus.operator
tutd.cylindricalStorageInput || [
tutd.cylindricalStorageInput.operator,
tutd.cylindricalStorageInput.thermalBus,
tutd.cylindricalStorageInput.thermalBus.operator
]

tutd.thermalHouseInput || [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@ class CylindricalStorageInputFactoryTest extends Specification implements Facto
"storagevolumelvl" : "3",
"inlettemp" : "4",
"returntemp" : "5",
"c" : "6"
"c" : "6",
"pThermalMax" : "7"
]
def inputClass = CylindricalStorageInput
def thermalBusInput = Mock(ThermalBusInput)
Expand All @@ -55,6 +56,7 @@ class CylindricalStorageInputFactoryTest extends Specification implements Facto
assert inletTemp == getQuant(parameter["inlettemp"], StandardUnits.TEMPERATURE)
assert returnTemp == getQuant(parameter["returntemp"], StandardUnits.TEMPERATURE)
assert c == getQuant(parameter["c"], StandardUnits.SPECIFIC_HEAT_CAPACITY)
assert pThermalMax == getQuant(parameter["pThermalMax"], StandardUnits.ACTIVE_POWER_IN)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ class CsvFileSinkTest extends Specification implements TimeSeriesTestData {
GridTestData.transformerCtoG,
GridTestData.lineGraphicCtoD,
GridTestData.nodeGraphicC,
ThermalUnitInputTestData.cylindricStorageInput,
ThermalUnitInputTestData.cylindricalStorageInput,
ThermalUnitInputTestData.thermalHouseInput,
SystemParticipantTestData.evcsInput,
SystemParticipantTestData.loadInput,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ class SqlSinkTest extends Specification implements TestContainerHelper, TimeSeri
GridTestData.transformerCtoG,
GridTestData.lineGraphicCtoD,
GridTestData.nodeGraphicC,
ThermalUnitInputTestData.cylindricStorageInput,
ThermalUnitInputTestData.cylindricalStorageInput,
ThermalUnitInputTestData.thermalHouseInput,
SystemParticipantTestData.evcsInput,
SystemParticipantTestData.loadInput,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ class CsvThermalSourceTest extends Specification implements CsvTestDataMeta {
inletTemp == sptd.inletTemp
returnTemp == sptd.returnTemp
c == sptd.c
pThermalMax == sptd.pThermalMax
}

//test method when operators and thermal buses are provided as constructor parameters
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ class CylindricalStorageInputTest extends Specification {

def "A CylindricalStorageInput copy method should work as expected"() {
given:
def cylindricalStorageInput = ThermalUnitInputTestData.cylindricStorageInput
def cylindricalStorageInput = ThermalUnitInputTestData.cylindricalStorageInput

when:
def alteredUnit = cylindricalStorageInput.copy().storageVolumeLvl(ThermalUnitInputTestData.storageVolumeLvl)
Expand All @@ -38,7 +38,7 @@ class CylindricalStorageInputTest extends Specification {

def "Scaling a CylindricalStorageInput via builder should work as expected"() {
given:
def cylindricalStorageInput = ThermalUnitInputTestData.cylindricStorageInput
def cylindricalStorageInput = ThermalUnitInputTestData.cylindricalStorageInput

when:
def alteredUnit = cylindricalStorageInput.copy().scale(2d).build()
Expand All @@ -54,6 +54,7 @@ class CylindricalStorageInputTest extends Specification {
assert inletTemp == cylindricalStorageInput.inletTemp
assert returnTemp == cylindricalStorageInput.returnTemp
assert c == cylindricalStorageInput.c
assert pThermalMax == cylindricalStorageInput.pThermalMax * 2d
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,15 @@ import edu.ie3.datamodel.utils.Try
import edu.ie3.test.common.SystemParticipantTestData
import edu.ie3.test.common.ThermalUnitInputTestData
import edu.ie3.util.TimeUtil
import edu.ie3.util.quantities.PowerSystemUnits
import edu.ie3.util.quantities.interfaces.HeatCapacity
import edu.ie3.util.quantities.interfaces.SpecificHeatCapacity
import edu.ie3.util.quantities.interfaces.ThermalConductance
import spock.lang.Specification
import tech.units.indriya.ComparableQuantity
import tech.units.indriya.quantity.Quantities

import javax.measure.quantity.Power
import javax.measure.quantity.Temperature
import javax.measure.quantity.Volume

Expand All @@ -50,6 +52,7 @@ class ThermalValidationUtilsTest extends Specification {
private static final ComparableQuantity<Temperature> inletTemp = Quantities.getQuantity(100, StandardUnits.TEMPERATURE)
private static final ComparableQuantity<Temperature> returnTemp = Quantities.getQuantity(80, StandardUnits.TEMPERATURE)
private static final ComparableQuantity<SpecificHeatCapacity> c = Quantities.getQuantity(1.05, StandardUnits.SPECIFIC_HEAT_CAPACITY)
private static final ComparableQuantity<Power> pThermalMax = Quantities.getQuantity(10.2, StandardUnits.ACTIVE_POWER_IN)

// Thermal House

Expand Down Expand Up @@ -88,7 +91,7 @@ class ThermalValidationUtilsTest extends Specification {

def "Smoke Test: Correct thermal cylindrical storage throws no exception"() {
given:
def cylindricalStorageInput = ThermalUnitInputTestData.cylindricStorageInput
def cylindricalStorageInput = ThermalUnitInputTestData.cylindricalStorageInput

when:
ValidationUtils.check(cylindricalStorageInput)
Expand All @@ -108,17 +111,18 @@ class ThermalValidationUtilsTest extends Specification {
ex.message == expectedException.message

where:
invalidCylindricalStorage || expectedSize || expectedException
new CylindricalStorageInput(thermalUnitUuid, id, operator, operationTime, SystemParticipantTestData.thermalBus, storageVolumeLvl, Quantities.getQuantity(100, StandardUnits.TEMPERATURE), Quantities.getQuantity(200, StandardUnits.TEMPERATURE), c) || 1 || new InvalidEntityException("Inlet temperature of the cylindrical storage cannot be lower or equal than outlet temperature", invalidCylindricalStorage)
new CylindricalStorageInput(thermalUnitUuid, id, operator, operationTime, SystemParticipantTestData.thermalBus, storageVolumeLvl, Quantities.getQuantity(100, StandardUnits.TEMPERATURE), Quantities.getQuantity(100, StandardUnits.TEMPERATURE), c) || 1 || new InvalidEntityException("Inlet temperature of the cylindrical storage cannot be lower or equal than outlet temperature", invalidCylindricalStorage)
new CylindricalStorageInput(thermalUnitUuid, id, operator, operationTime, SystemParticipantTestData.thermalBus, Quantities.getQuantity(-100, StandardUnits.VOLUME), inletTemp, returnTemp, Quantities.getQuantity(-1.05, StandardUnits.SPECIFIC_HEAT_CAPACITY)) || 1 || new InvalidEntityException("The following quantities have to be positive: -100 ㎥, -1.05 kWh/K*m³", invalidCylindricalStorage)
invalidCylindricalStorage || expectedSize || expectedException
new CylindricalStorageInput(thermalUnitUuid, id, operator, operationTime, SystemParticipantTestData.thermalBus, storageVolumeLvl, Quantities.getQuantity(100, StandardUnits.TEMPERATURE), Quantities.getQuantity(200, StandardUnits.TEMPERATURE), c, pThermalMax) || 1 || new InvalidEntityException("Inlet temperature of the cylindrical storage cannot be lower or equal than outlet temperature", invalidCylindricalStorage)
new CylindricalStorageInput(thermalUnitUuid, id, operator, operationTime, SystemParticipantTestData.thermalBus, storageVolumeLvl, Quantities.getQuantity(100, StandardUnits.TEMPERATURE), Quantities.getQuantity(100, StandardUnits.TEMPERATURE), c, pThermalMax) || 1 || new InvalidEntityException("Inlet temperature of the cylindrical storage cannot be lower or equal than outlet temperature", invalidCylindricalStorage)
new CylindricalStorageInput(thermalUnitUuid, id, operator, operationTime, SystemParticipantTestData.thermalBus, Quantities.getQuantity(-100, StandardUnits.VOLUME), inletTemp, returnTemp, Quantities.getQuantity(-1.05, StandardUnits.SPECIFIC_HEAT_CAPACITY), pThermalMax) || 1 || new InvalidEntityException("The following quantities have to be positive: -100 ㎥, -1.05 kWh/K*m³", invalidCylindricalStorage)
new CylindricalStorageInput(thermalUnitUuid, id, operator, operationTime, SystemParticipantTestData.thermalBus, Quantities.getQuantity(-100, StandardUnits.VOLUME), inletTemp, returnTemp, Quantities.getQuantity(-1.05, StandardUnits.SPECIFIC_HEAT_CAPACITY), Quantities.getQuantity(-20, PowerSystemUnits.KILOWATT)) || 1 || new InvalidEntityException("The following quantities have to be positive: -100 ㎥, -1.05 kWh/K*m³, -20 kW", invalidCylindricalStorage)
}

def "ThermalUnitValidationUtils.check() works for complete ThermalGrid as well"() {
when:
def thermalBus = ThermalUnitInputTestData.thermalBus
def cylindricalStorageInput = [
ThermalUnitInputTestData.cylindricStorageInput
ThermalUnitInputTestData.cylindricalStorageInput
]


Expand Down
Loading