Skip to content

Commit

Permalink
Add tests
Browse files Browse the repository at this point in the history
  • Loading branch information
ckittl committed Oct 15, 2021
1 parent 26b6152 commit 00bd76a
Show file tree
Hide file tree
Showing 3 changed files with 236 additions and 7 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added
- added `EvcsLocationType` support in `EvcsInput` and `EvcsInputFactory` [#406](https://github.com/ie3-institute/PowerSystemDataModel/issues/406)
- Opportunity to close writer in `CsvFileSink`
- Graph with impedance weighted edges including facilities to create it [#440](https://github.com/ie3-institute/PowerSystemDataModel/issues/440)

### Fixed
- adapted `LineInput` constructor to convert line length to `StandardUnits.LINE_LENGTH` [#412](https://github.com/ie3-institute/PowerSystemDataModel/issues/412)
Expand Down
152 changes: 152 additions & 0 deletions src/main/java/edu/ie3/datamodel/utils/ContainerUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@
*/
package edu.ie3.datamodel.utils;

import static edu.ie3.util.quantities.PowerSystemUnits.KILOMETRE;
import static edu.ie3.util.quantities.PowerSystemUnits.OHM_PER_KILOMETRE;
import static java.lang.Math.pow;
import static java.lang.Math.sqrt;
import static tech.units.indriya.unit.Units.OHM;

import edu.ie3.datamodel.exceptions.InvalidGridException;
import edu.ie3.datamodel.exceptions.TopologyException;
import edu.ie3.datamodel.graph.*;
Expand All @@ -16,13 +22,16 @@
import edu.ie3.datamodel.models.input.graphics.NodeGraphicInput;
import edu.ie3.datamodel.models.input.system.*;
import edu.ie3.datamodel.models.voltagelevels.VoltageLevel;
import edu.ie3.util.quantities.interfaces.SpecificResistance;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.measure.quantity.Length;
import org.jgrapht.graph.DirectedMultigraph;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import tech.units.indriya.ComparableQuantity;

/** Offers functionality useful for grouping different models together */
public class ContainerUtils {
Expand Down Expand Up @@ -131,6 +140,149 @@ private static void addDistanceGraphEdge(
graph.getEdge(nodeA, nodeB), GridAndGeoUtils.distanceBetweenNodes(nodeA, nodeB));
}

/**
* Returns the topology of the provided grid container as a {@link ImpedanceWeightedGraph} if the
* provided grid container's {@link RawGridElements} allows the creation of a valid topology graph
* or an empty optional otherwise.
*
* @param grid the grid container that should be converted into topology graph
* @return either an optional holding the impedance topology graph instance or an empty optional
*/
public static Optional<ImpedanceWeightedGraph> getImpedanceTopologyGraph(GridContainer grid) {
return getImpedanceTopologyGraph(grid.getRawGrid());
}

/**
* Returns the topology of the provided {@link RawGridElements} as a {@link
* ImpedanceWeightedGraph}, if they allow the creation of a valid topology graph or an empty
* optional otherwise.
*
* @param rawGridElements raw grids elements as base of the distance weighted topology graph
* @return either an optional holding the impedance topology graph instance or an empty optional
*/
public static Optional<ImpedanceWeightedGraph> getImpedanceTopologyGraph(
RawGridElements rawGridElements) {

ImpedanceWeightedGraph graph = new ImpedanceWeightedGraph();

try {
rawGridElements.getNodes().forEach(graph::addVertex);
} catch (NullPointerException ex) {
log.error("At least one node entity of provided RawGridElements is null. ", ex);
return Optional.empty();
}

try {
rawGridElements.getLines().forEach(line -> addImpedanceGraphEdge(graph, line));
} catch (NullPointerException | IllegalArgumentException | UnsupportedOperationException ex) {
log.error("Error adding line edges to graph: ", ex);
return Optional.empty();
}

try {
rawGridElements
.getSwitches()
.forEach(
switchInput -> {
if (switchInput.isClosed()) addImpedanceGraphEdge(graph, switchInput);
});
} catch (NullPointerException | IllegalArgumentException | UnsupportedOperationException ex) {
log.error("Error adding switch edges to graph: ", ex);
return Optional.empty();
}

try {
rawGridElements.getTransformer2Ws().forEach(trafo2w -> addImpedanceGraphEdge(graph, trafo2w));
} catch (NullPointerException | IllegalArgumentException | UnsupportedOperationException ex) {
log.error("Error adding 2 winding transformer edges to graph: ", ex);
return Optional.empty();
}

try {
rawGridElements.getTransformer3Ws().forEach(trafo3w -> addImpedanceGraphEdge(graph, trafo3w));
} catch (NullPointerException | IllegalArgumentException | UnsupportedOperationException ex) {
log.error("Error adding 3 winding transformer edges to graph: ", ex);
return Optional.empty();
}

// if we reached this point, we can safely return a valid graph
return Optional.of(graph);
}

/**
* Adds an {@link ImpedanceWeightedEdge} to the provided graph between the provided nodes a and b.
* By implementation of jGraphT this side effect cannot be removed. :(
*
* @param graph the graph to be altered
* @param connectorInput the connector input element
*/
private static void addImpedanceGraphEdge(
ImpedanceWeightedGraph graph, ConnectorInput connectorInput) {
NodeInput nodeA = connectorInput.getNodeA();
NodeInput nodeB = connectorInput.getNodeB();
/* Add an edge if it is not a switch or the switch is closed */
if (!(connectorInput instanceof SwitchInput) || ((SwitchInput) connectorInput).isClosed())
graph.addEdge(nodeA, nodeB);

if (connectorInput instanceof LineInput) {
LineInput line = (LineInput) connectorInput;
graph.setEdgeWeight(
graph.getEdge(nodeA, nodeB),
calcImpedance(line.getType().getR(), line.getType().getX(), line.getLength()));
}
if (connectorInput instanceof SwitchInput) {
SwitchInput sw = (SwitchInput) connectorInput;
// assumption: closed switch has a resistance of 1 OHM
if (sw.isClosed()) graph.setEdgeWeight(graph.getEdge(nodeA, nodeB), 1);
}
if (connectorInput instanceof Transformer2WInput) {
Transformer2WInput trafo2w = (Transformer2WInput) connectorInput;
graph.setEdgeWeight(
graph.getEdge(nodeA, nodeB),
sqrt(
pow(trafo2w.getType().getrSc().to(OHM).getValue().doubleValue(), 2)
+ pow(trafo2w.getType().getxSc().to(OHM).getValue().doubleValue(), 2)));
}
if (connectorInput instanceof Transformer3WInput) {
Transformer3WInput trafo3w = (Transformer3WInput) connectorInput;
graph.addEdge(nodeA, trafo3w.getNodeC());

graph.setEdgeWeight(
graph.getEdge(nodeA, nodeB),
sqrt(
pow(
trafo3w.getType().getrScA().to(OHM).getValue().doubleValue()
+ trafo3w.getType().getrScB().to(OHM).getValue().doubleValue(),
2)
+ pow(
trafo3w.getType().getxScA().to(OHM).getValue().doubleValue()
+ trafo3w.getType().getxScB().to(OHM).getValue().doubleValue(),
2)));

graph.setEdgeWeight(
graph.getEdge(nodeA, trafo3w.getNodeC()),
sqrt(
pow(
trafo3w.getType().getrScA().to(OHM).getValue().doubleValue()
+ trafo3w.getType().getrScC().to(OHM).getValue().doubleValue(),
2)
+ pow(
trafo3w.getType().getxScA().to(OHM).getValue().doubleValue()
+ trafo3w.getType().getxScC().to(OHM).getValue().doubleValue(),
2)));
}
}

private static double calcImpedance(
ComparableQuantity<SpecificResistance> r,
ComparableQuantity<SpecificResistance> x,
ComparableQuantity<Length> length) {
return sqrt(
pow(r.to(OHM_PER_KILOMETRE).getValue().doubleValue(), 2)
+ pow(x.to(OHM_PER_KILOMETRE).getValue().doubleValue(), 2))
* length.to(KILOMETRE).getValue().doubleValue();
}

/**
* Filters all raw grid elements for the provided subnet. For each transformer all nodes (and not
* only the the node of the grid the transformer is located in) are added as well. Two winding
Expand Down
90 changes: 83 additions & 7 deletions src/test/groovy/edu/ie3/datamodel/utils/ContainerUtilsTest.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ package edu.ie3.datamodel.utils

import edu.ie3.datamodel.exceptions.InvalidGridException
import edu.ie3.datamodel.graph.DistanceWeightedGraph
import edu.ie3.datamodel.graph.ImpedanceWeightedGraph
import edu.ie3.datamodel.graph.SubGridTopologyGraph
import edu.ie3.datamodel.models.OperationTime
import edu.ie3.datamodel.models.input.NodeInput
Expand Down Expand Up @@ -439,25 +440,25 @@ class ContainerUtilsTest extends Specification {

def "The container utils build a valid distance weighted graph model correctly"(){
given:
JointGridContainer grid = ComplexTopology.grid
def grid = ComplexTopology.grid

when:
Optional<DistanceWeightedGraph> resultingGraphOpt = ContainerUtils.getDistanceTopologyGraph(grid)
def resultingGraphOpt = ContainerUtils.getDistanceTopologyGraph(grid)

then:
resultingGraphOpt.present
DistanceWeightedGraph resultingGraph = resultingGraphOpt.get()
def resultingGraph = resultingGraphOpt.get()

resultingGraph.vertexSet() == ComplexTopology.grid.getRawGrid().getNodes()

resultingGraph.edgeSet().size() == 7
}

def "The container utils build a valid istance of DistanceWeightedEdge during graph generation"(){
def "The container utils build a valid instance of DistanceWeightedEdge during graph generation"(){
given:
DistanceWeightedGraph graph = new DistanceWeightedGraph()
NodeInput nodeA = GridTestData.nodeA
NodeInput nodeB = GridTestData.nodeB
def graph = new DistanceWeightedGraph()
def nodeA = GridTestData.nodeA
def nodeB = GridTestData.nodeB

when:
graph.addVertex(nodeA)
Expand All @@ -472,8 +473,83 @@ class ContainerUtilsTest extends Specification {
assert source == nodeA
assert target == nodeB
}
}

def "The container utils build a valid impedance weighted graph model correctly"(){
given:
def grid = ComplexTopology.grid

when:
def resultingGraphOpt = ContainerUtils.getImpedanceTopologyGraph(grid)

then:
resultingGraphOpt.present
def resultingGraph = resultingGraphOpt.get()

resultingGraph.vertexSet() == ComplexTopology.grid.getRawGrid().getNodes()

resultingGraph.edgeSet().size() == 7
}

def "The container utils build a valid instance of ImpedanceWeightedEdge during graph generation"(){
given:
def graph = new ImpedanceWeightedGraph()

when:
/* Add a transformer */
def transformer = GridTestData.transformerBtoD
graph.addVertex(transformer.getNodeA())
graph.addVertex(transformer.getNodeB())
ContainerUtils.addImpedanceGraphEdge(graph, transformer)

and:
/* Add a line */
def line = GridTestData.lineCtoD
graph.addVertex(line.getNodeA())
graph.addVertex(line.getNodeB())
ContainerUtils.addImpedanceGraphEdge(graph, line)

and:
/* Add a closed switch */
def swtchClosed = new SwitchInput(UUID.randomUUID(), "test_switch", GridTestData.nodeD, GridTestData.nodeE, true)
graph.addVertex(swtchClosed.getNodeA())
graph.addVertex(swtchClosed.getNodeB())
ContainerUtils.addImpedanceGraphEdge(graph, swtchClosed)
def swtchOpen = new SwitchInput(UUID.randomUUID(), "test_switch", GridTestData.nodeE, GridTestData.nodeF, false)
graph.addVertex(swtchOpen.getNodeA())
graph.addVertex(swtchOpen.getNodeB())
ContainerUtils.addImpedanceGraphEdge(graph, swtchOpen)

and:
/* Add a three winding transformer */
def transformer3w = GridTestData.transformerAtoBtoC
graph.addVertex(transformer3w.getNodeA())
graph.addVertex(transformer3w.getNodeB())
ContainerUtils.addImpedanceGraphEdge(graph, transformer3w)

then:
graph.vertexSet().size() == 6
graph.edgeSet().size() == 5

/* Check impedance of two winding transformer */
graph.getEdge(transformer.nodeA, transformer.nodeB).with {
assert weight == 112.33121875062159d
}
/* Check impedance of three winding transformer */
graph.getEdge(transformer3w.nodeA, transformer3w.nodeB).with {
assert weight == 1.1278408575681236d
}
graph.getEdge(transformer3w.nodeA, transformer3w.nodeC).with {
assert weight == 1.0471340124358486d
}
/* Check impedance of line */
graph.getEdge(line.nodeA, line.nodeB).with {
assert weight == 0.0016909597866300668d
}
/* Check impedance of switch */
graph.getEdge(swtchClosed.nodeA, swtchClosed.nodeB).with {
assert weight == 1.0d
}
}

/* TODO: Extend testing data so that,
Expand Down

0 comments on commit 00bd76a

Please sign in to comment.