Skip to content

Commit 5ed1c73

Browse files
Merge branch 'refs/heads/dev' into sh/#872-storage-documentation
2 parents 935f80c + d9c33bf commit 5ed1c73

File tree

7 files changed

+260
-6
lines changed

7 files changed

+260
-6
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
88

99
### Added
1010
- Enhancing `VoltageLevel` with `equals` method [#1063](https://github.com/ie3-institute/PowerSystemDataModel/issues/1063)
11+
- `ConnectorValidationUtils` checks if parallel devices is > 0 [#1077](https://github.com/ie3-institute/PowerSystemDataModel/issues/1077)
12+
- `GridContainerValidationUtils` checks the connectivity for all defined operation time intervals [#1091](https://github.com/ie3-institute/PowerSystemDataModel/issues/1091)
1113

1214
### Fixed
1315
- Fixed `MappingEntryies` not getting processed by adding `Getter` methods for record fields [#1084](https://github.com/ie3-institute/PowerSystemDataModel/issues/1084)

build.gradle

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ plugins {
55
id 'signing'
66
id 'pmd' // code check, working on source code
77
id 'com.diffplug.spotless' version '6.25.0' //code format
8-
id 'com.github.spotbugs' version '6.0.14' // code check, working on byte code
8+
id 'com.github.spotbugs' version '6.0.15' // code check, working on byte code
99
id 'de.undercouch.download' version '5.6.0'
1010
id 'kr.motd.sphinx' version '2.10.1' // documentation generation
1111
id 'jacoco' // java code coverage plugin
@@ -73,7 +73,7 @@ dependencies {
7373
testImplementation 'org.junit.jupiter:junit-jupiter:5.10.2'
7474
testImplementation "org.spockframework:spock-core:2.3-groovy-$groovyVersion"
7575
testImplementation 'org.objenesis:objenesis:3.4' // Mock creation with constructor parameters
76-
testImplementation 'net.bytebuddy:byte-buddy:1.14.15' // Mocks of classes
76+
testImplementation 'net.bytebuddy:byte-buddy:1.14.17' // Mocks of classes
7777

7878
// testcontainers (docker framework for testing)
7979
testImplementation "org.testcontainers:testcontainers:$testcontainersVersion"
@@ -95,7 +95,7 @@ dependencies {
9595

9696
implementation 'commons-io:commons-io:2.16.1' // I/O functionalities
9797
implementation 'commons-codec:commons-codec:1.17.0' // needed by commons-compress
98-
implementation 'org.apache.commons:commons-compress:1.26.1' // I/O functionalities
98+
implementation 'org.apache.commons:commons-compress:1.26.2' // I/O functionalities
9999
}
100100

101101
tasks.withType(JavaCompile) {

docs/readthedocs/io/ValidationUtils.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +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
1415

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

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

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ protected static List<Try<Void, InvalidEntityException>> check(ConnectorInput co
6565

6666
List<Try<Void, InvalidEntityException>> exceptions = new ArrayList<>();
6767
exceptions.add(connectsDifferentNodes(connector));
68+
exceptions.add(lessThanOneParallelDevice(connector));
6869

6970
// Further checks for subclasses
7071
if (LineInput.class.isAssignableFrom(connector.getClass())) {
@@ -443,6 +444,23 @@ private static Try<Void, InvalidEntityException> connectsDifferentNodes(
443444
connectorInput));
444445
}
445446

447+
/**
448+
* Check that the given connector has at least one parallel device.
449+
*
450+
* @param connectorInput to check
451+
* @return a try
452+
*/
453+
private static Try<Void, InvalidEntityException> lessThanOneParallelDevice(
454+
ConnectorInput connectorInput) {
455+
return Try.ofVoid(
456+
connectorInput.getParallelDevices() < 1,
457+
() ->
458+
new InvalidEntityException(
459+
connectorInput.getClass().getSimpleName()
460+
+ " needs to have at least one parallel device",
461+
connectorInput));
462+
}
463+
446464
/**
447465
* Check if subnets of connector's nodes are correct depending on if they should be equal or not
448466
*

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

Lines changed: 113 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,19 +8,33 @@
88
import static edu.ie3.datamodel.utils.validation.UniquenessValidationUtils.checkAssetUniqueness;
99
import static edu.ie3.datamodel.utils.validation.UniquenessValidationUtils.checkUniqueEntities;
1010

11-
import edu.ie3.datamodel.exceptions.*;
11+
import edu.ie3.datamodel.exceptions.DuplicateEntitiesException;
12+
import edu.ie3.datamodel.exceptions.InvalidEntityException;
13+
import edu.ie3.datamodel.exceptions.InvalidGridException;
14+
import edu.ie3.datamodel.exceptions.ValidationException;
15+
import edu.ie3.datamodel.models.OperationTime;
1216
import edu.ie3.datamodel.models.input.AssetInput;
1317
import edu.ie3.datamodel.models.input.MeasurementUnitInput;
1418
import edu.ie3.datamodel.models.input.NodeInput;
15-
import edu.ie3.datamodel.models.input.connector.*;
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;
1622
import edu.ie3.datamodel.models.input.container.*;
1723
import edu.ie3.datamodel.models.input.graphics.GraphicInput;
1824
import edu.ie3.datamodel.models.input.system.SystemParticipantInput;
1925
import edu.ie3.datamodel.utils.ContainerUtils;
2026
import edu.ie3.datamodel.utils.Try;
21-
import edu.ie3.datamodel.utils.Try.*;
27+
import edu.ie3.datamodel.utils.Try.Failure;
28+
import edu.ie3.datamodel.utils.Try.Success;
29+
import java.time.ZonedDateTime;
2230
import java.util.*;
31+
import java.util.function.Predicate;
32+
import java.util.stream.Collectors;
2333
import java.util.stream.Stream;
34+
import org.jgrapht.Graph;
35+
import org.jgrapht.alg.connectivity.ConnectivityInspector;
36+
import org.jgrapht.graph.DefaultEdge;
37+
import org.jgrapht.graph.SimpleGraph;
2438

2539
public class GridContainerValidationUtils extends ValidationUtils {
2640

@@ -155,9 +169,105 @@ private GridContainerValidationUtils() {
155169
exceptions.add(MeasurementUnitValidationUtils.check(measurement));
156170
});
157171

172+
exceptions.addAll(checkConnectivity(rawGridElements));
173+
158174
return exceptions;
159175
}
160176

177+
/**
178+
* Checks the connectivity of the given grid for all defined {@link OperationTime}s. If every
179+
* {@link AssetInput} is set to {@link OperationTime#notLimited()}, the connectivity is only
180+
* checked once.
181+
*
182+
* @param rawGridElements to check
183+
* @return a try
184+
*/
185+
protected static List<Try<Void, InvalidGridException>> checkConnectivity(
186+
RawGridElements rawGridElements) {
187+
Set<ZonedDateTime> times =
188+
rawGridElements.allEntitiesAsList().stream()
189+
.map(AssetInput::getOperationTime)
190+
.filter(OperationTime::isLimited)
191+
.map(OperationTime::getOperationLimit)
192+
.filter(Optional::isPresent)
193+
.map(Optional::get)
194+
.map(interval -> Set.of(interval.getLower(), interval.getUpper()))
195+
.flatMap(Collection::stream)
196+
.collect(Collectors.toSet());
197+
198+
if (times.isEmpty()) {
199+
return List.of(checkConnectivity(rawGridElements, Optional.empty()));
200+
} else {
201+
return times.stream()
202+
.sorted()
203+
.map(time -> checkConnectivity(rawGridElements, Optional.of(time)))
204+
.toList();
205+
}
206+
}
207+
208+
/**
209+
* Checks if the given {@link RawGridElements} from a connected grid.
210+
*
211+
* @param rawGridElements to check
212+
* @param time for operation filtering
213+
* @return a try
214+
*/
215+
protected static Try<Void, InvalidGridException> checkConnectivity(
216+
RawGridElements rawGridElements, Optional<ZonedDateTime> time) {
217+
218+
Predicate<AssetInput> isInOperation =
219+
assetInput -> time.map(assetInput::inOperationOn).orElse(true);
220+
221+
// build graph
222+
Graph<UUID, DefaultEdge> graph = new SimpleGraph<>(DefaultEdge.class);
223+
224+
rawGridElements.getNodes().stream()
225+
.filter(isInOperation)
226+
.forEach(node -> graph.addVertex(node.getUuid()));
227+
rawGridElements.getLines().stream()
228+
.filter(isInOperation)
229+
.forEach(
230+
connector ->
231+
graph.addEdge(connector.getNodeA().getUuid(), connector.getNodeB().getUuid()));
232+
rawGridElements.getTransformer2Ws().stream()
233+
.filter(isInOperation)
234+
.forEach(
235+
connector ->
236+
graph.addEdge(connector.getNodeA().getUuid(), connector.getNodeB().getUuid()));
237+
rawGridElements.getTransformer3Ws().stream()
238+
.filter(isInOperation)
239+
.forEach(
240+
connector ->
241+
graph.addEdge(connector.getNodeA().getUuid(), connector.getNodeB().getUuid()));
242+
rawGridElements.getSwitches().stream()
243+
.filter(isInOperation)
244+
.forEach(
245+
connector ->
246+
graph.addEdge(connector.getNodeA().getUuid(), connector.getNodeB().getUuid()));
247+
248+
ConnectivityInspector<UUID, DefaultEdge> inspector = new ConnectivityInspector<>(graph);
249+
250+
if (inspector.isConnected()) {
251+
return Success.empty();
252+
} else {
253+
List<Set<UUID>> sets = inspector.connectedSets();
254+
255+
List<UUID> unconnected =
256+
sets.stream()
257+
.max(Comparator.comparing(Set::size))
258+
.map(set -> graph.vertexSet().stream().filter(v -> !set.contains(v)).toList())
259+
.orElse(List.of());
260+
261+
String message = "The grid contains unconnected elements";
262+
263+
if (time.isPresent()) {
264+
message += " for time " + time.get();
265+
}
266+
267+
return Failure.of(new InvalidGridException(message + ": " + unconnected));
268+
}
269+
}
270+
161271
/**
162272
* Checks the validity of each and every system participant. Moreover, it checks, if the systems
163273
* are connected to a node that is not in the provided set

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

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,23 @@ class ConnectorValidationUtilsTest extends Specification {
6262
OlmCharacteristicInput.CONSTANT_CHARACTERISTIC
6363
)
6464

65+
def "A ConnectorInput needs at least one parallel device"() {
66+
when:
67+
def actual = ConnectorValidationUtils.lessThanOneParallelDevice(invalidConnector)
68+
69+
then:
70+
actual.failure
71+
actual.exception.get().class == InvalidEntityException
72+
actual.exception.get().message.contains(expectedMessage)
73+
74+
where:
75+
invalidConnector || expectedMessage
76+
GridTestData.lineFtoG.copy().parallelDevices(0).build() || "LineInput needs to have at least one parallel device"
77+
GridTestData.lineCtoD.copy().parallelDevices(-1).build() || "LineInput needs to have at least one parallel device"
78+
GridTestData.transformerBtoE.copy().parallelDevices(0).build() || "Transformer2WInput needs to have at least one parallel device"
79+
GridTestData.transformerAtoBtoC.copy().parallelDevices(0).build() || "Transformer3WInput needs to have at least one parallel device"
80+
}
81+
6582
def "ConnectorValidationUtils.checkLine() recognizes all potential errors for a line"() {
6683
when:
6784
List<Try<Void, InvalidEntityException>> exceptions = ConnectorValidationUtils.check(invalidLine).stream().filter { it -> it.failure }.toList()
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
/*
2+
* © 2024. TU Dortmund University,
3+
* Institute of Energy Systems, Energy Efficiency and Energy Economics,
4+
* Research group Distribution grid planning and operation
5+
*/
6+
package edu.ie3.datamodel.utils.validation
7+
8+
import edu.ie3.datamodel.exceptions.InvalidGridException
9+
import edu.ie3.datamodel.models.OperationTime
10+
import edu.ie3.datamodel.models.input.container.RawGridElements
11+
import edu.ie3.datamodel.utils.Try
12+
import edu.ie3.test.common.GridTestData as GTD
13+
import spock.lang.Shared
14+
import spock.lang.Specification
15+
16+
import java.time.ZonedDateTime
17+
18+
class GridContainerValidationUtilsTest extends Specification {
19+
@Shared
20+
private ZonedDateTime start
21+
22+
@Shared
23+
private RawGridElements limitedElements
24+
25+
def setupSpec() {
26+
start = ZonedDateTime.now()
27+
28+
def operationTimeFrame1 = OperationTime.builder().withStart(start).withEnd(start.plusHours(2)).build()
29+
def operationTimeFrame2 = OperationTime.builder().withStart(start.plusHours(1)).withEnd(start.plusHours(3)).build()
30+
31+
def nodes = [
32+
GTD.nodeC,
33+
GTD.nodeD,
34+
GTD.nodeE,
35+
GTD.nodeF,
36+
GTD.nodeG
37+
] as Set
38+
39+
def lines = [
40+
GTD.lineCtoD.copy().operationTime(operationTimeFrame1).build(),
41+
GTD.lineFtoG.copy().operationTime(operationTimeFrame2).build()
42+
] as Set
43+
44+
def transformers = [
45+
GTD.transformerCtoF.copy().operationTime(operationTimeFrame1).build(),
46+
GTD.transformerCtoE.copy().operationTime(operationTimeFrame2).build()
47+
] as Set
48+
49+
limitedElements = new RawGridElements(nodes, lines, transformers, [] as Set, [] as Set, [] as Set)
50+
}
51+
52+
def "The GridContainerValidationUtils should check the connectivity for all operation intervals correctly"() {
53+
when:
54+
def actual = GridContainerValidationUtils.checkConnectivity(limitedElements)
55+
56+
then:
57+
actual.size() == 4
58+
actual.get(0).failure
59+
actual.get(1).success
60+
actual.get(2).success
61+
actual.get(3).failure
62+
63+
actual.get(0).exception.get().message == "The grid contains unconnected elements for time " + start + ": " + [
64+
GTD.nodeE.uuid,
65+
GTD.nodeG.uuid
66+
]
67+
actual.get(3).exception.get().message == "The grid contains unconnected elements for time " + start.plusHours(3) + ": " + [
68+
GTD.nodeD.uuid,
69+
GTD.nodeF.uuid,
70+
GTD.nodeG.uuid
71+
]
72+
}
73+
74+
def "The GridContainerValidationUtils should check the connectivity correctly"() {
75+
when:
76+
def actual = GridContainerValidationUtils.checkConnectivity(limitedElements, time as Optional<ZonedDateTime>)
77+
78+
then:
79+
actual == expectedResult
80+
81+
where:
82+
time || expectedResult
83+
Optional.empty() || Try.Success.empty()
84+
Optional.of(start.plusHours(1)) || Try.Success.empty()
85+
}
86+
87+
def "The GridContainerValidationUtils should return an exception if the grid is not properly connected"() {
88+
when:
89+
def actual = GridContainerValidationUtils.checkConnectivity(limitedElements, time as Optional<ZonedDateTime>)
90+
91+
then:
92+
actual.exception.get().message == expectedException.message
93+
94+
where:
95+
time || expectedException
96+
Optional.of(start) || new InvalidGridException("The grid contains unconnected elements for time " + start + ": " + [
97+
GTD.nodeE.uuid,
98+
GTD.nodeG.uuid
99+
])
100+
Optional.of(start.plusHours(3)) || new InvalidGridException("The grid contains unconnected elements for time " + start.plusHours(3) + ": " + [
101+
GTD.nodeD.uuid,
102+
GTD.nodeF.uuid,
103+
GTD.nodeG.uuid
104+
])
105+
}
106+
}

0 commit comments

Comments
 (0)