Skip to content

Changed SubgridContainer to represent galvanically seperated grids #1366

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
Show file tree
Hide file tree
Changes from 5 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 @@ -18,6 +18,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Updated dependabot workflow and added CODEOWNERS [#1328](https://github.com/ie3-institute/PowerSystemDataModel/issues/1328)
- Extend azimuth angle range to [-180°, 180°] for PV inputs [#1330](https://github.com/ie3-institute/PowerSystemDataModel/issues/1330)
- Improved error messages when reading and validating an invalid grid [#1354](https://github.com/ie3-institute/PowerSystemDataModel/issues/1354)
- Changed `SubgridContainer` to represent galvanically seperated grids [#1226](https://github.com/ie3-institute/PowerSystemDataModel/issues/1226)

## [7.0.0] - 2025-05-08

Expand Down
Binary file modified docs/readthedocs/_static/figures/transformerWithSwitchGear.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
12 changes: 6 additions & 6 deletions docs/readthedocs/_static/figures/transformerWithSwitchGear.tex
Original file line number Diff line number Diff line change
Expand Up @@ -159,9 +159,9 @@
\begin{tikzpicture}
% === Anschlüsse ===
\node[circle, draw = tuGreen, fill = tuGreen, inner sep = 0, minimum height = 1.5mm] (port_a) at (0,0){};
\node[circle, draw = tuOrange, fill = tuOrange, inner sep = 0, minimum height = 1.5mm] (port_b) at (15mm,0){};
\node[circle, draw = tuOrange, fill = tuOrange, inner sep = 0, minimum height = 1.5mm] (port_c) at (30mm,0){};
\node[circle, draw = tuOrange, fill = tuOrange, inner sep = 0, minimum height = 1.5mm] (port_d) at (45mm,0){};
\node[circle, draw = tuOrange, fill = tuGreen, inner sep = 0, minimum height = 1.5mm] (port_b) at (15mm,0){};
\node[circle, draw = tuOrange, fill = tuGreen, inner sep = 0, minimum height = 1.5mm] (port_c) at (30mm,0){};
\node[circle, draw = tuOrange, fill = tuGreen, inner sep = 0, minimum height = 1.5mm] (port_d) at (45mm,0){};
\node[circle, draw = tuOrange, fill = tuOrange, inner sep = 0, minimum height = 1.5mm] (port_e) at (62.5mm,0){};

\draw (port_a.west) -- ++(-3mm, -1mm) -- ++(0mm, -2mm) edge[densely dotted] ++(0mm, -1.8mm);
Expand All @@ -176,9 +176,9 @@
\draw (port_d.east) -- (winding_a.west) (winding_b.east) -- (port_e.west);

\node[tuGreen, anchor = north, inner sep = 2mm, text width = 10mm, align = center] at (port_a.south) {A \\ \SI{110}{\kV} \\ 1};
\node[tuOrange, anchor = north, inner sep = 2mm, text width = 10mm, align = center] at (port_b.south) {B \\ \SI{110}{\kV} \\ 2};
\node[tuOrange, anchor = north, inner sep = 2mm, text width = 10mm, align = center] at (port_c.south) {C \\ \SI{110}{\kV} \\ 2};
\node[tuOrange, anchor = north, inner sep = 2mm, text width = 10mm, align = center] at (port_d.south) {D \\ \SI{110}{\kV} \\ 2};
\node[tuOrange, anchor = north, inner sep = 2mm, text width = 10mm, align = center] at (port_b.south) {B \\ \SI{110}{\kV} \\ 1};
\node[tuOrange, anchor = north, inner sep = 2mm, text width = 10mm, align = center] at (port_c.south) {C \\ \SI{110}{\kV} \\ 1};
\node[tuOrange, anchor = north, inner sep = 2mm, text width = 10mm, align = center] at (port_d.south) {D \\ \SI{110}{\kV} \\ 1};
\node[tuOrange, anchor = north, inner sep = 2mm, text width = 10mm, align = center] at (port_e.south) {E \\ \SI{10}{\kV} \\ 2};
\end{tikzpicture}
\end{document}
14 changes: 6 additions & 8 deletions docs/readthedocs/models/input/grid/gridcontainer.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,12 @@ and their higher voltage coupling point.

Let's shed a more detailed light on the boundaries of a sub grid as of our definition.
This especially is important, if the switchgear of the transformer is modeled in detail.
We defined, that all nodes in upstream direction of the transformer, that are connected by switches *only* (therefore
are within the switchgear) are counted towards the inferior sub grid structure (here "2"), although they belong to a
different voltage level.
This decision is taken, because we assume, that the interest to operate on the given switchgear will most likely be
placed in the inferior grid structure.

The "real" coupling node A is not comprised in the sub grids node collection, but obviously has reference through the
switch between nodes A and B.
We defined, that all nodes in upstream direction of the transformer, including those, which are connected by switches *only*
(therefore are within the switchgear) are counted towards the superior sub grid structure (here "1"), because they belong
to a different voltage level.
If a switchgear should be operated by the inferior grid, one can set the operator, that is used in the inferior grid, for
the switchgear. This can be necessary, if we assume, that the interest to operate on the given switchgear will most likely
be placed in the inferior grid structure.

A synoptic overview of both classes' attributes is given here:

Expand Down
121 changes: 5 additions & 116 deletions src/main/java/edu/ie3/datamodel/utils/ContainerUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -426,41 +426,11 @@ public static GraphicElements filterForSubnet(GraphicElements input, int subnet)
*/
public static VoltageLevel determinePredominantVoltLvl(RawGridElements rawGrid, int subnet)
throws InvalidGridException {
/* Exclude all nodes, that are at the high voltage side of the transformer */
Set<NodeInput> gridNodes = new HashSet<>(rawGrid.getNodes());
gridNodes.removeAll(
/* Remove all nodes, that are upstream of transformers, this comprises all those, that are connected by
* switches */
rawGrid.getTransformer2Ws().stream()
.flatMap(
transformer ->
ContainerUtils.traverseAlongSwitchChain(transformer.getNodeA(), rawGrid)
.stream())
.collect(Collectors.toSet()));
gridNodes.removeAll(
rawGrid.getTransformer3Ws().stream()
.flatMap(
transformer -> {
if (transformer.getNodeA().getSubnet() == subnet)
return Stream.of(transformer.getNodeB(), transformer.getNodeC());
else if (transformer.getNodeB().getSubnet() == subnet)
return Stream.concat(
ContainerUtils.traverseAlongSwitchChain(transformer.getNodeA(), rawGrid)
.stream(),
Stream.of(transformer.getNodeC(), transformer.getNodeInternal()));
else
return Stream.concat(
ContainerUtils.traverseAlongSwitchChain(transformer.getNodeA(), rawGrid)
.stream(),
Stream.of(transformer.getNodeB(), transformer.getNodeInternal()));
})
.collect(Collectors.toSet()));

/* Build a mapping, which voltage level appears how often */
Map<VoltageLevel, Long> voltageLevelCount =
gridNodes.stream()
.map(NodeInput::getVoltLvl)
.collect(Collectors.groupingBy(voltLvl -> voltLvl, Collectors.counting()));
rawGrid.getNodes().stream()
.filter(n -> n.getSubnet() == subnet)
.collect(Collectors.groupingBy(NodeInput::getVoltLvl, Collectors.counting()));

/* At this point only one voltage level should be apparent */
int amountOfVoltLvl = voltageLevelCount.size();
Expand Down Expand Up @@ -677,14 +647,8 @@ private static TransformerSubGridContainers getSubGridContainers(
RawGridElements rawGridElements,
Map<Integer, SubGridContainer> subGrids)
throws TopologyException {
/* Get the sub grid container at port A - travel upstream as long as nodes are connected
* _only_ by switches */
NodeInput topNode = traverseAlongSwitchChain(transformer.getNodeA(), rawGridElements).getLast();
if (Objects.isNull(topNode))
throw new TopologyException(
"Cannot find most upstream node of transformer '" + transformer + "'");

SubGridContainer containerA = subGrids.get(topNode.getSubnet());
/* Get the sub grid container at port A */
SubGridContainer containerA = subGrids.get(transformer.getNodeA().getSubnet());

/* Get the sub grid container at port B */
SubGridContainer containerB = subGrids.get(transformer.getNodeB().getSubnet());
Expand All @@ -696,81 +660,6 @@ private static TransformerSubGridContainers getSubGridContainers(
} else return new TransformerSubGridContainers(containerA, containerB);
}

/**
* Traversing along a chain of switches and return the traveled nodes. The end thereby is defined
* by a node, that either is a dead end or is connected to any other type of connector (e.g.
* lines, transformers) and therefore leads to other parts of a "real" grid. If the starting node
* is not part of any switch, the starting node is returned.
*
* @param startNode Node that is meant to be the start of the switch chain
* @param rawGridElements Elements of the pure grid structure.
* @return The end node of the switch chain
*/
public static LinkedList<NodeInput> traverseAlongSwitchChain(
NodeInput startNode, RawGridElements rawGridElements) {
Set<NodeInput> possibleJunctions =
Stream.concat(
Stream.concat(
rawGridElements.getLines().parallelStream(),
rawGridElements.getTransformer2Ws().parallelStream()),
rawGridElements.getTransformer3Ws().parallelStream())
.flatMap(connector -> connector.allNodes().parallelStream())
.collect(Collectors.toSet());
return traverseAlongSwitchChain(startNode, rawGridElements.getSwitches(), possibleJunctions);
}

/**
* Traversing along a chain of switches and return the traveled nodes. The end thereby is defined
* by a node, that either is a dead end or part of the provided node set. If the starting node is
* not part of any switch, the starting node is returned.
*
* @param startNode Node that is meant to be the start of the switch chain
* @param switches Set of available switches
* @param possibleJunctions Set of nodes that denote possible junctions to "real" grid
* @return The end node of the switch chain
*/
private static LinkedList<NodeInput> traverseAlongSwitchChain(
NodeInput startNode, Set<SwitchInput> switches, Set<NodeInput> possibleJunctions) {
LinkedList<NodeInput> traveledNodes = new LinkedList<>();
traveledNodes.addFirst(startNode);

/* Get the switch, that is connected to the starting node and determine the next node */
List<SwitchInput> nextSwitches =
switches.stream().filter(switcher -> switcher.allNodes().contains(startNode)).toList();
switch (nextSwitches.size()) {
case 0:
/* No further switch found -> Return the starting node */
break;
case 1:
/* One next switch has been found -> Travel in this direction */
SwitchInput nextSwitch = nextSwitches.get(0);
Optional<NodeInput> candidateNodes =
nextSwitch.allNodes().stream().filter(node -> node != startNode).findFirst();
NodeInput nextNode =
candidateNodes.orElseThrow(
() ->
new IllegalArgumentException(
"There is no further node available at switch " + nextSwitch));
if (possibleJunctions.contains(nextNode)) {
/* This is a junction, leading to another Connector than a switch */
traveledNodes.addLast(nextNode);
} else {
/* Add the traveled nodes to the nodes to be excluded, to avoid endless loops in cyclic switch topologies */
HashSet<NodeInput> newNodesToExclude = new HashSet<>(possibleJunctions);
newNodesToExclude.add(nextNode);
HashSet<SwitchInput> newSwitches = new HashSet<>(switches);
newSwitches.remove(nextSwitch);
traveledNodes.addAll(traverseAlongSwitchChain(nextNode, newSwitches, newNodesToExclude));
}
break;
default:
throw new IllegalArgumentException(
"Cannot traverse along switch chain, as there is a junction included at node "
+ startNode);
}
return traveledNodes;
}

/**
* Combines a given collection of sub grid containers to a joint model. If the single models do
* not fit together, exceptions are thrown.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,13 @@
import edu.ie3.datamodel.models.input.container.*;
import edu.ie3.datamodel.models.input.graphics.GraphicInput;
import edu.ie3.datamodel.models.input.system.SystemParticipantInput;
import edu.ie3.datamodel.utils.ContainerUtils;
import edu.ie3.datamodel.utils.Try;
import edu.ie3.datamodel.utils.Try.Failure;
import edu.ie3.datamodel.utils.Try.Success;
import java.time.ZonedDateTime;
import java.util.*;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.jgrapht.Graph;
import org.jgrapht.alg.connectivity.ConnectivityInspector;
import org.jgrapht.graph.DefaultEdge;
Expand Down Expand Up @@ -140,26 +138,12 @@ private GridContainerValidationUtils() {
exceptions.addAll(ConnectorValidationUtils.check(transformer));
});

/* Checking switches
* Because of the fact, that a transformer with switch gear in "upstream" direction has its corresponding node in
* upper grid connected to a switch, instead of to the transformer directly: Collect all nodes at the end of the
* upstream switch chain and add them to the set of allowed nodes */
HashSet<NodeInput> validSwitchNodes = new HashSet<>(nodes);
validSwitchNodes.addAll(
Stream.of(rawGridElements.getTransformer2Ws(), rawGridElements.getTransformer2Ws())
.flatMap(Set::stream)
.parallel()
.map(
transformer ->
ContainerUtils.traverseAlongSwitchChain(transformer.getNodeA(), rawGridElements)
.getLast())
.toList());

/* Checking switches */
rawGridElements
.getSwitches()
.forEach(
switcher -> {
exceptions.add(checkNodeAvailability(switcher, validSwitchNodes));
exceptions.add(checkNodeAvailability(switcher, nodes));
exceptions.addAll(ConnectorValidationUtils.check(switcher));
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@
*/
package edu.ie3.datamodel.utils.validation;

import edu.ie3.datamodel.exceptions.FailedValidationException;
import edu.ie3.datamodel.exceptions.InvalidEntityException;
import edu.ie3.datamodel.exceptions.UnsafeEntityException;
import edu.ie3.datamodel.exceptions.ValidationException;
import edu.ie3.datamodel.exceptions.*;
import edu.ie3.datamodel.models.UniqueEntity;
import edu.ie3.datamodel.models.input.*;
import edu.ie3.datamodel.models.input.AssetInput;
import edu.ie3.datamodel.models.input.AssetTypeInput;
import edu.ie3.datamodel.models.input.MeasurementUnitInput;
import edu.ie3.datamodel.models.input.NodeInput;
import edu.ie3.datamodel.models.input.connector.ConnectorInput;
import edu.ie3.datamodel.models.input.connector.type.LineTypeInput;
import edu.ie3.datamodel.models.input.connector.type.Transformer2WTypeInput;
Expand All @@ -22,11 +22,8 @@
import edu.ie3.datamodel.models.input.system.type.SystemParticipantTypeInput;
import edu.ie3.datamodel.models.input.thermal.ThermalUnitInput;
import edu.ie3.datamodel.utils.Try;
import edu.ie3.datamodel.utils.Try.Failure;
import edu.ie3.datamodel.utils.Try.Success;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import edu.ie3.datamodel.utils.Try.*;
import java.util.*;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import javax.measure.Quantity;
Expand Down Expand Up @@ -160,8 +157,6 @@ else if (ThermalUnitInput.class.isAssignableFrom(assetInput.getClass()))
exceptions.addAll(ThermalValidationUtils.check((ThermalUnitInput) assetInput));
else if (ThermalGrid.class.isAssignableFrom(assetInput.getClass()))
exceptions.addAll(ThermalValidationUtils.check((ThermalUnitInput) assetInput));
else if (EmInput.class.isAssignableFrom(assetInput.getClass()))
exceptions.addAll(EnergyManagementValidationUtils.check((EmInput) assetInput));
else {
logNotImplemented(assetInput);
}
Expand Down
Loading