Skip to content

Commit a8f892d

Browse files
Merge branch 'dev' into ps/#554-HandlingNoWeatherData
2 parents 69838ed + 9af34b1 commit a8f892d

16 files changed

+200
-29
lines changed

CHANGELOG.md

+3
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1212
- Added explicit handling for cases where no weather data is received from any source [#554](https://github.com/ie3-institute/PowerSystemDataModel/issues/554)
1313
- Adding timeseries for voltage values [#1128](https://github.com/ie3-institute/PowerSystemDataModel/issues/1128)
1414
- Added Staudt to list of reviewers [#1190](https://github.com/ie3-institute/PowerSystemDataModel/issues/1190)
15+
- Extend ValidationUtils for validating ThermalGrids [#1216](https://github.com/ie3-institute/PowerSystemDataModel/issues/1216)
16+
- Enhance `TimeSeriesSource` with method to retrieve the previous value before a given key [#1182](https://github.com/ie3-institute/PowerSystemDataModel/issues/1182)
1517

1618

1719
### Fixed
20+
- Removing opened `SwitchInput` during connectivity check [#1221](https://github.com/ie3-institute/PowerSystemDataModel/issues/1221)
1821

1922
### Changed
2023
- Storage minimum level parameter removed from cylindrical thermal storage [#1123](https://github.com/ie3-institute/PowerSystemDataModel/issues/1123)

docs/readthedocs/io/ValidationUtils.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ The methods in ValidationUtils and subclasses can be used to check that objects
1111
The general validation checks:
1212
- if assigned values are valid, e.g. lines are not allowed to have negative lengths or the rated power factor of any unit must be between 0 and 1
1313
- furthermore, several connections are checked, e.g. that lines only connect nodes of the same voltage level or that the voltage levels indicated for the transformer sides match the voltage levels of the nodes they are connected to.
14-
- the connectivity of the given grid for all defined operation intervals
14+
- the connectivity of the given grid for all defined operation intervals, if a switch is opened, it is filtered out for the connectivity check
1515

1616
The uniqueness validation checks if a collection of given objects are unique in either:
1717
- a specific field

src/main/java/edu/ie3/datamodel/io/source/TimeSeriesSource.java

+3-1
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,9 @@ protected Try<TimeBasedValue<V>, FactoryException> createTimeBasedValue(
5050
public abstract IndividualTimeSeries<V> getTimeSeries(ClosedInterval<ZonedDateTime> timeInterval)
5151
throws SourceException;
5252

53-
public abstract Optional<V> getValue(ZonedDateTime time) throws SourceException;
53+
public abstract Optional<V> getValue(ZonedDateTime time);
54+
55+
public abstract Optional<TimeBasedValue<V>> getPreviousTimeBasedValue(ZonedDateTime time);
5456

5557
/**
5658
* Method to return all time keys after a given timestamp.

src/main/java/edu/ie3/datamodel/io/source/csv/CsvTimeSeriesSource.java

+9
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,15 @@ public Optional<V> getValue(ZonedDateTime time) {
128128
return timeSeries.getValue(time);
129129
}
130130

131+
@Override
132+
public Optional<TimeBasedValue<V>> getPreviousTimeBasedValue(ZonedDateTime time) {
133+
return timeSeries.getPreviousTimeBasedValue(time);
134+
}
135+
136+
public Optional<TimeBasedValue<V>> getNextTimeBasedValue(ZonedDateTime time) {
137+
return timeSeries.getNextTimeBasedValue(time);
138+
}
139+
131140
@Override
132141
public List<ZonedDateTime> getTimeKeysAfter(ZonedDateTime time) {
133142
return timeSeries.getTimeKeysAfter(time);

src/main/java/edu/ie3/datamodel/io/source/sql/SqlTimeSeriesSource.java

+40-8
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ public class SqlTimeSeriesSource<V extends Value> extends TimeSeriesSource<V> {
4848

4949
private final String queryTimeInterval;
5050
private final String queryTimeKeysAfter;
51+
private final String queryForValueBefore;
5152
private final String queryTime;
5253

5354
public SqlTimeSeriesSource(
@@ -63,15 +64,16 @@ public SqlTimeSeriesSource(
6364
final ColumnScheme columnScheme = ColumnScheme.parse(valueClass).orElseThrow();
6465
this.tableName = sqlDataSource.databaseNamingStrategy.getTimeSeriesEntityName(columnScheme);
6566

67+
String schemaName = sqlDataSource.schemaName;
68+
6669
String dbTimeColumnName =
6770
sqlDataSource.getDbColumnName(factory.getTimeFieldString(), tableName);
6871

69-
this.queryFull = createQueryFull(sqlDataSource.schemaName, tableName);
70-
this.queryTimeInterval =
71-
createQueryForTimeInterval(sqlDataSource.schemaName, tableName, dbTimeColumnName);
72-
this.queryTimeKeysAfter =
73-
createQueryForTimeKeysAfter(sqlDataSource.schemaName, tableName, dbTimeColumnName);
74-
this.queryTime = createQueryForTime(sqlDataSource.schemaName, tableName, dbTimeColumnName);
72+
this.queryFull = createQueryFull(schemaName, tableName);
73+
this.queryTimeInterval = createQueryForTimeInterval(schemaName, tableName, dbTimeColumnName);
74+
this.queryTimeKeysAfter = createQueryForTimeKeysAfter(schemaName, tableName, dbTimeColumnName);
75+
this.queryForValueBefore = createQueryForValueBefore(schemaName, tableName, dbTimeColumnName);
76+
this.queryTime = createQueryForTime(schemaName, tableName, dbTimeColumnName);
7577
}
7678

7779
/**
@@ -179,6 +181,14 @@ public Optional<V> getValue(ZonedDateTime time) {
179181
return Optional.of(timeBasedValues.stream().toList().get(0).getValue());
180182
}
181183

184+
@Override
185+
public Optional<TimeBasedValue<V>> getPreviousTimeBasedValue(ZonedDateTime time) {
186+
return getTimeBasedValueSet(
187+
queryForValueBefore, ps -> ps.setTimestamp(1, Timestamp.from(time.toInstant())))
188+
.stream()
189+
.max(TimeBasedValue::compareTo);
190+
}
191+
182192
@Override
183193
public List<ZonedDateTime> getTimeKeysAfter(ZonedDateTime time) {
184194
return dataSource
@@ -278,8 +288,30 @@ private String createQueryForTimeKeysAfter(
278288
}
279289

280290
/**
281-
* Creates a basic query to retrieve an entry for the given time series uuid and time with the
282-
* following pattern: <br>
291+
* Creates a base query to retrieve all time keys after a given time for given time series with
292+
* the following pattern: <br>
293+
* {@code <base query> WHERE time_series = $timeSeriesUuid AND <time column> < ?;}
294+
*
295+
* @param schemaName the name of the database schema
296+
* @param tableName the name of the database table
297+
* @param timeColumnName the name of the column holding the timestamp info
298+
* @return the query string
299+
*/
300+
private String createQueryForValueBefore(
301+
String schemaName, String tableName, String timeColumnName) {
302+
return createBaseQueryString(schemaName, tableName)
303+
+ WHERE
304+
+ TIME_SERIES
305+
+ " = '"
306+
+ timeSeriesUuid.toString()
307+
+ "' AND "
308+
+ timeColumnName
309+
+ " < ?"
310+
+ "ORDER BY time DESC LIMIT 1;";
311+
}
312+
/**
313+
* Creates a base query to retrieve all time keys before a given time for given time series with
314+
* the following pattern: <br>
283315
* {@code <base query> WHERE time_series = $timeSeriesUuid AND <time column>=?;}
284316
*
285317
* @param schemaName the name of the database schema

src/main/java/edu/ie3/datamodel/models/timeseries/individual/IndividualTimeSeries.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -55,14 +55,14 @@ public Optional<V> getValue(ZonedDateTime time) {
5555
@Override
5656
protected Optional<ZonedDateTime> getPreviousDateTime(ZonedDateTime time) {
5757
return timeToValue.keySet().stream()
58-
.filter(valueTime -> valueTime.compareTo(time) <= 0)
58+
.filter(valueTime -> valueTime.compareTo(time) < 0)
5959
.max(Comparator.naturalOrder());
6060
}
6161

6262
@Override
6363
protected Optional<ZonedDateTime> getNextDateTime(ZonedDateTime time) {
6464
return timeToValue.keySet().stream()
65-
.filter(valueTime -> valueTime.compareTo(time) >= 0)
65+
.filter(valueTime -> valueTime.compareTo(time) > 0)
6666
.min(Comparator.naturalOrder());
6767
}
6868

src/main/java/edu/ie3/datamodel/models/timeseries/repetitive/LoadProfileInput.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -42,13 +42,13 @@ public PValue calc(ZonedDateTime time) {
4242
@Override
4343
protected Optional<ZonedDateTime> getPreviousDateTime(ZonedDateTime time) {
4444
// dummy value
45-
return Optional.of(time.minusHours(1));
45+
return Optional.of(time.minusMinutes(15));
4646
}
4747

4848
@Override
4949
protected Optional<ZonedDateTime> getNextDateTime(ZonedDateTime time) {
5050
// dummy value
51-
return Optional.of(time.plusHours(1));
51+
return Optional.of(time.plusMinutes(15));
5252
}
5353

5454
@Override

src/main/java/edu/ie3/datamodel/utils/validation/GridContainerValidationUtils.java

+2-3
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,7 @@
1616
import edu.ie3.datamodel.models.input.AssetInput;
1717
import edu.ie3.datamodel.models.input.MeasurementUnitInput;
1818
import edu.ie3.datamodel.models.input.NodeInput;
19-
import edu.ie3.datamodel.models.input.connector.ConnectorInput;
20-
import edu.ie3.datamodel.models.input.connector.LineInput;
21-
import edu.ie3.datamodel.models.input.connector.Transformer3WInput;
19+
import edu.ie3.datamodel.models.input.connector.*;
2220
import edu.ie3.datamodel.models.input.container.*;
2321
import edu.ie3.datamodel.models.input.graphics.GraphicInput;
2422
import edu.ie3.datamodel.models.input.system.SystemParticipantInput;
@@ -241,6 +239,7 @@ protected static Try<Void, InvalidGridException> checkConnectivity(
241239
graph.addEdge(connector.getNodeA().getUuid(), connector.getNodeB().getUuid()));
242240
rawGridElements.getSwitches().stream()
243241
.filter(isInOperation)
242+
.filter(SwitchInput::isClosed)
244243
.forEach(
245244
connector ->
246245
graph.addEdge(connector.getNodeA().getUuid(), connector.getNodeB().getUuid()));

src/main/java/edu/ie3/datamodel/utils/validation/ThermalUnitValidationUtils.java src/main/java/edu/ie3/datamodel/utils/validation/ThermalValidationUtils.java

+39-2
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,18 @@
77

88
import edu.ie3.datamodel.exceptions.InvalidEntityException;
99
import edu.ie3.datamodel.exceptions.ValidationException;
10+
import edu.ie3.datamodel.models.input.container.ThermalGrid;
1011
import edu.ie3.datamodel.models.input.thermal.*;
1112
import edu.ie3.datamodel.utils.Try;
1213
import edu.ie3.datamodel.utils.Try.Failure;
1314
import java.util.ArrayList;
1415
import java.util.List;
1516
import javax.measure.Quantity;
1617

17-
public class ThermalUnitValidationUtils extends ValidationUtils {
18+
public class ThermalValidationUtils extends ValidationUtils {
1819

1920
/** Private Constructor as this class is not meant to be instantiated */
20-
private ThermalUnitValidationUtils() {
21+
private ThermalValidationUtils() {
2122
throw new IllegalStateException("Don't try and instantiate a Utility class.");
2223
}
2324

@@ -57,6 +58,42 @@ private ThermalUnitValidationUtils() {
5758
return exceptions;
5859
}
5960

61+
/**
62+
* Validates a thermal grid if:
63+
*
64+
* <ul>
65+
* <li>it is not null
66+
* </ul>
67+
*
68+
* A "distribution" method, that forwards the check request to specific implementations to fulfill
69+
* the checking task, based on the class of the given object.
70+
*
71+
* @param thermalGrid ThermalGrid to validate
72+
* @return a list of try objects either containing an {@link ValidationException} or an empty
73+
* Success
74+
*/
75+
protected static List<Try<Void, ? extends ValidationException>> check(ThermalGrid thermalGrid) {
76+
Try<Void, InvalidEntityException> isNull = checkNonNull(thermalGrid, "a thermal grid");
77+
78+
if (isNull.isFailure()) {
79+
return List.of(isNull);
80+
}
81+
82+
List<Try<Void, ? extends ValidationException>> exceptions = new ArrayList<>();
83+
84+
// Validate houses
85+
for (ThermalHouseInput house : thermalGrid.houses()) {
86+
exceptions.addAll(checkThermalHouse(house));
87+
}
88+
89+
// Validate storages
90+
for (ThermalStorageInput storage : thermalGrid.storages()) {
91+
exceptions.addAll(check(storage));
92+
}
93+
94+
return exceptions;
95+
}
96+
6097
/**
6198
* Validates a thermalSinkInput if:
6299
*

src/main/java/edu/ie3/datamodel/utils/validation/ValidationUtils.java

+6-1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import edu.ie3.datamodel.models.input.connector.type.Transformer2WTypeInput;
1717
import edu.ie3.datamodel.models.input.connector.type.Transformer3WTypeInput;
1818
import edu.ie3.datamodel.models.input.container.GridContainer;
19+
import edu.ie3.datamodel.models.input.container.ThermalGrid;
1920
import edu.ie3.datamodel.models.input.graphics.GraphicInput;
2021
import edu.ie3.datamodel.models.input.system.SystemParticipantInput;
2122
import edu.ie3.datamodel.models.input.system.type.SystemParticipantTypeInput;
@@ -68,6 +69,8 @@ public static void check(Object obj) throws ValidationException {
6869
exceptions.addAll(GraphicValidationUtils.check((GraphicInput) obj));
6970
} else if (AssetTypeInput.class.isAssignableFrom(obj.getClass())) {
7071
exceptions.addAll(checkAssetType((AssetTypeInput) obj));
72+
} else if (ThermalGrid.class.isAssignableFrom(obj.getClass())) {
73+
exceptions.addAll(ThermalValidationUtils.check((ThermalGrid) obj));
7174
} else {
7275
logNotImplemented(obj);
7376
}
@@ -151,7 +154,9 @@ else if (SystemParticipantInput.class.isAssignableFrom(assetInput.getClass()))
151154
exceptions.addAll(
152155
SystemParticipantValidationUtils.check((SystemParticipantInput) assetInput));
153156
else if (ThermalUnitInput.class.isAssignableFrom(assetInput.getClass()))
154-
exceptions.addAll(ThermalUnitValidationUtils.check((ThermalUnitInput) assetInput));
157+
exceptions.addAll(ThermalValidationUtils.check((ThermalUnitInput) assetInput));
158+
else if (ThermalGrid.class.isAssignableFrom(assetInput.getClass()))
159+
exceptions.addAll(ThermalValidationUtils.check((ThermalUnitInput) assetInput));
155160
else {
156161
logNotImplemented(assetInput);
157162
}

src/test/groovy/edu/ie3/datamodel/io/source/csv/CsvTimeSeriesSourceIT.groovy

+20
Original file line numberDiff line numberDiff line change
@@ -85,4 +85,24 @@ class CsvTimeSeriesSourceIT extends Specification implements CsvTestDataMeta {
8585
actual.present
8686
actual.get() == PH_VALUE_15MIN
8787
}
88+
89+
def "A csv time series source is able to return the previous value for a given time"() {
90+
when:
91+
def actual = source.getPreviousTimeBasedValue(TIME_15MIN)
92+
93+
then:
94+
actual.isPresent()
95+
actual.get().time == TIME_00MIN
96+
actual.get().value == PH_VALUE_00MIN
97+
}
98+
99+
def "A csv time series source is able to return the next value for a given time"() {
100+
when:
101+
def actual = source.getNextTimeBasedValue(TIME_00MIN)
102+
103+
then:
104+
actual.isPresent()
105+
actual.get().time == TIME_15MIN
106+
actual.get().value == PH_VALUE_15MIN
107+
}
88108
}

src/test/groovy/edu/ie3/datamodel/io/source/sql/SqlTimeSeriesSourceIT.groovy

+16
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import edu.ie3.datamodel.io.connectors.SqlConnector
1212
import edu.ie3.datamodel.io.naming.DatabaseNamingStrategy
1313
import edu.ie3.datamodel.io.naming.timeseries.ColumnScheme
1414
import edu.ie3.datamodel.io.naming.timeseries.IndividualTimeSeriesMetaInformation
15+
import edu.ie3.datamodel.models.timeseries.individual.TimeBasedValue
1516
import edu.ie3.datamodel.models.value.*
1617
import edu.ie3.test.helper.TestContainerHelper
1718
import edu.ie3.util.TimeUtil
@@ -123,6 +124,21 @@ class SqlTimeSeriesSourceIT extends Specification implements TestContainerHelper
123124
value.get() == P_VALUE_00MIN
124125
}
125126

127+
def "A SqlTimeSeriesSource is able to return the previous value for a given time"() {
128+
when:
129+
def actual = pSource.getPreviousTimeBasedValue(time)
130+
131+
then:
132+
actual.isPresent() == expectedResult.isPresent()
133+
actual == expectedResult
134+
135+
where:
136+
time | expectedResult
137+
TIME_00MIN | Optional.empty()
138+
TIME_15MIN | Optional.of(new TimeBasedValue<>(TIME_00MIN, P_VALUE_00MIN))
139+
TIME_30MIN | Optional.of(new TimeBasedValue<>(TIME_15MIN, P_VALUE_15MIN))
140+
}
141+
126142
def "A SqlTimeSeriesSource can read multiple time series values for a time interval"() {
127143
given:
128144
def timeInterval = new ClosedInterval(TIME_00MIN, TIME_15MIN)

src/test/groovy/edu/ie3/datamodel/models/timeseries/IndividualTimeSeriesTest.groovy

+2-2
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ class IndividualTimeSeriesTest extends Specification implements TimeSeriesTestDa
7272
Optional<TimeBasedValue<IntValue>> expected = Optional.of(new TimeBasedValue<>(ZonedDateTime.of(1990, 1, 1, 0, 0, 0, 0, ZoneId.of("UTC")), new IntValue(3)))
7373

7474
when:
75-
Optional<TimeBasedValue<IntValue>> actual = individualIntTimeSeries.getPreviousTimeBasedValue(ZonedDateTime.of(1990, 1, 1, 0, 0, 0, 0, ZoneId.of("UTC")))
75+
Optional<TimeBasedValue<IntValue>> actual = individualIntTimeSeries.getPreviousTimeBasedValue(ZonedDateTime.of(1990, 1, 1, 0, 5, 0, 0, ZoneId.of("UTC")))
7676

7777
then:
7878
expected.present
@@ -103,7 +103,7 @@ class IndividualTimeSeriesTest extends Specification implements TimeSeriesTestDa
103103
Optional<TimeBasedValue<IntValue>> expected = Optional.of(new TimeBasedValue<>(ZonedDateTime.of(1990, 1, 1, 0, 15, 0, 0, ZoneId.of("UTC")), new IntValue(4)))
104104

105105
when:
106-
Optional<TimeBasedValue<IntValue>> actual = individualIntTimeSeries.getNextTimeBasedValue(ZonedDateTime.of(1990, 1, 1, 0, 15, 0, 0, ZoneId.of("UTC")))
106+
Optional<TimeBasedValue<IntValue>> actual = individualIntTimeSeries.getNextTimeBasedValue(ZonedDateTime.of(1990, 1, 1, 0, 10, 0, 0, ZoneId.of("UTC")))
107107

108108
then:
109109
expected.present

src/test/groovy/edu/ie3/datamodel/utils/validation/GridContainerValidationUtilsTest.groovy

+21
Original file line numberDiff line numberDiff line change
@@ -103,4 +103,25 @@ class GridContainerValidationUtilsTest extends Specification {
103103
GTD.nodeG.uuid
104104
])
105105
}
106+
107+
def "The GridContainerValidationUtils should return an exception if the grid is not properly connected, because a switch is open"() {
108+
given:
109+
def nodeA = GTD.nodeA.copy().operationTime(OperationTime.notLimited()).build()
110+
def nodeB = GTD.nodeB.copy().operationTime(OperationTime.notLimited()).build()
111+
112+
def switchAtoB = GTD.switchAtoB.copy()
113+
.nodeA(nodeA)
114+
.nodeB(nodeB)
115+
.operationTime(OperationTime.notLimited())
116+
.closed(false)
117+
.build()
118+
119+
def rawGrid = new RawGridElements([nodeA, nodeB] as Set, [] as Set, [] as Set, [] as Set, [switchAtoB] as Set, [] as Set)
120+
121+
when:
122+
def actual = GridContainerValidationUtils.checkConnectivity(rawGrid, Optional.of(start) as Optional<ZonedDateTime>)
123+
124+
then:
125+
actual.exception.get().message == "The grid contains unconnected elements for time "+start+": [47d29df0-ba2d-4d23-8e75-c82229c5c758]"
126+
}
106127
}

0 commit comments

Comments
 (0)