From 66a9db794e5337fdaabcbde816d3b73dc33be18a Mon Sep 17 00:00:00 2001 From: staudtMarius Date: Wed, 9 Jul 2025 14:10:34 +0200 Subject: [PATCH 1/5] Changed `SubgridContainer` to represent galvanically seperated grids --- CHANGELOG.md | 4 +- .../models/input/grid/gridcontainer.md | 13 -- .../ie3/datamodel/utils/ContainerUtils.java | 121 +----------- .../EnergyManagementValidationUtils.java | 48 ----- .../GridContainerValidationUtils.java | 20 +- .../utils/validation/ValidationUtils.java | 19 +- .../datamodel/utils/ContainerUtilsTest.groovy | 175 +----------------- .../validation/EmValidationUtilsTest.groovy | 41 ---- .../edu/ie3/test/common/GridTestData.groovy | 10 - 9 files changed, 18 insertions(+), 433 deletions(-) delete mode 100644 src/main/java/edu/ie3/datamodel/utils/validation/EnergyManagementValidationUtils.java delete mode 100644 src/test/groovy/edu/ie3/datamodel/utils/validation/EmValidationUtilsTest.groovy diff --git a/CHANGELOG.md b/CHANGELOG.md index 8b8252909..7f5fdde36 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,9 +6,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased/Snapshot] -### Added -- Extend Validation to EnergyManagement Systems. [#1356](https://github.com/ie3-institute/PowerSystemDataModel/issues/1356) - ### Fixed - Fixed handling of `CongestionResult.InputModelType` in `EntityProcessor` [#1325](https://github.com/ie3-institute/PowerSystemDataModel/issues/1325) - Fixed em fields in input models [#1331](https://github.com/ie3-institute/PowerSystemDataModel/issues/1331) @@ -17,6 +14,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed - 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) +- Changed `SubgridContainer` to represent galvanically seperated grids [#1226](https://github.com/ie3-institute/PowerSystemDataModel/issues/1226) ## [7.0.0] - 2025-05-08 diff --git a/docs/readthedocs/models/input/grid/gridcontainer.md b/docs/readthedocs/models/input/grid/gridcontainer.md index 24f8e66c4..d6411b318 100644 --- a/docs/readthedocs/models/input/grid/gridcontainer.md +++ b/docs/readthedocs/models/input/grid/gridcontainer.md @@ -20,19 +20,6 @@ Why predominant? As of convention, the `SubGridContainers` hold also reference to the transformers leading to higher sub grids and their higher voltage coupling point. -![Sub grid boundary definition for transformers with upstream switchgear](../../../_static/figures/transformerWithSwitchGear.png) - -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. - A synoptic overview of both classes' attributes is given here: ## Attributes, Units and Remarks diff --git a/src/main/java/edu/ie3/datamodel/utils/ContainerUtils.java b/src/main/java/edu/ie3/datamodel/utils/ContainerUtils.java index 5033221a2..522dfeaa4 100644 --- a/src/main/java/edu/ie3/datamodel/utils/ContainerUtils.java +++ b/src/main/java/edu/ie3/datamodel/utils/ContainerUtils.java @@ -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 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 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(); @@ -677,14 +647,8 @@ private static TransformerSubGridContainers getSubGridContainers( RawGridElements rawGridElements, Map 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()); @@ -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 traverseAlongSwitchChain( - NodeInput startNode, RawGridElements rawGridElements) { - Set 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 traverseAlongSwitchChain( - NodeInput startNode, Set switches, Set possibleJunctions) { - LinkedList traveledNodes = new LinkedList<>(); - traveledNodes.addFirst(startNode); - - /* Get the switch, that is connected to the starting node and determine the next node */ - List 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 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 newNodesToExclude = new HashSet<>(possibleJunctions); - newNodesToExclude.add(nextNode); - HashSet 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. diff --git a/src/main/java/edu/ie3/datamodel/utils/validation/EnergyManagementValidationUtils.java b/src/main/java/edu/ie3/datamodel/utils/validation/EnergyManagementValidationUtils.java deleted file mode 100644 index 9b2894cca..000000000 --- a/src/main/java/edu/ie3/datamodel/utils/validation/EnergyManagementValidationUtils.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * © 2021. TU Dortmund University, - * Institute of Energy Systems, Energy Efficiency and Energy Economics, - * Research group Distribution grid planning and operation -*/ -package edu.ie3.datamodel.utils.validation; - -import edu.ie3.datamodel.exceptions.InvalidEntityException; -import edu.ie3.datamodel.exceptions.ValidationException; -import edu.ie3.datamodel.models.input.EmInput; -import edu.ie3.datamodel.utils.Try; -import java.util.ArrayList; -import java.util.List; - -public class EnergyManagementValidationUtils extends ValidationUtils { - - /** Private Constructor as this class is not meant to be instantiated */ - private EnergyManagementValidationUtils() { - throw new IllegalStateException("Don't try and instantiate a Utility class."); - } - - /** - * Validates a energy management unit if: - * - *
    - *
  • its control strategy is not null - *
- * - * A "distribution" method, that forwards the check request to specific implementations to fulfill - * the checking task, based on the class of the given object. - * - * @param energyManagement EmInput to validate - * @return a list of try objects either containing an {@link ValidationException} or an empty - * Success - */ - protected static List> check(EmInput energyManagement) { - List> exceptions = new ArrayList<>(); - - exceptions.add( - Try.ofVoid( - energyManagement.getControlStrategy() == null, - () -> - new InvalidEntityException( - "No control strategy of energy management defined for", energyManagement))); - - return exceptions; - } -} diff --git a/src/main/java/edu/ie3/datamodel/utils/validation/GridContainerValidationUtils.java b/src/main/java/edu/ie3/datamodel/utils/validation/GridContainerValidationUtils.java index 7d9e9b26b..344e1a7cb 100644 --- a/src/main/java/edu/ie3/datamodel/utils/validation/GridContainerValidationUtils.java +++ b/src/main/java/edu/ie3/datamodel/utils/validation/GridContainerValidationUtils.java @@ -20,7 +20,6 @@ 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; @@ -28,7 +27,6 @@ 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; @@ -135,26 +133,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 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)); }); diff --git a/src/main/java/edu/ie3/datamodel/utils/validation/ValidationUtils.java b/src/main/java/edu/ie3/datamodel/utils/validation/ValidationUtils.java index 6d664aba1..80006953b 100644 --- a/src/main/java/edu/ie3/datamodel/utils/validation/ValidationUtils.java +++ b/src/main/java/edu/ie3/datamodel/utils/validation/ValidationUtils.java @@ -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; @@ -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; @@ -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); } diff --git a/src/test/groovy/edu/ie3/datamodel/utils/ContainerUtilsTest.groovy b/src/test/groovy/edu/ie3/datamodel/utils/ContainerUtilsTest.groovy index c07ff0d54..240312437 100644 --- a/src/test/groovy/edu/ie3/datamodel/utils/ContainerUtilsTest.groovy +++ b/src/test/groovy/edu/ie3/datamodel/utils/ContainerUtilsTest.groovy @@ -553,175 +553,6 @@ class ContainerUtilsTest extends Specification { * - filtering of system participants can be tested * - filtering of graphic elements can be tested */ - def "Traversing along a simple switch chain returns the correct list of traveled nodes"() { - given: - def nodeA = Mock(NodeInput) - def nodeB = Mock(NodeInput) - def nodeC = Mock(NodeInput) - def nodeD = Mock(NodeInput) - - def switchAB = Mock(SwitchInput) - switchAB.getNodeA() >> nodeA - switchAB.getNodeB() >> nodeB - switchAB.allNodes() >> List.of(nodeA, nodeB) - def switchBC = Mock(SwitchInput) - switchBC.getNodeA() >> nodeB - switchBC.getNodeB() >> nodeC - switchBC.allNodes() >> List.of(nodeB, nodeC) - def switchCD = Mock(SwitchInput) - switchCD.getNodeA() >> nodeC - switchCD.getNodeB() >> nodeD - switchCD.allNodes() >> List.of(nodeC, nodeD) - - def switches = new HashSet() - switches.add(switchAB) - switches.add(switchBC) - switches.add(switchCD) - - def possibleJunctions = new HashSet() - - def expected = new LinkedList() - expected.addFirst(nodeA) - expected.addLast(nodeB) - expected.addLast(nodeC) - expected.addLast(nodeD) - - when: - def actual = ContainerUtils.traverseAlongSwitchChain(nodeA, switches, possibleJunctions) - - then: - actual == expected - } - - def "Traversing along a switch chain with intermediate junction returns the correct list of traveled nodes"() { - given: - def nodeA = Mock(NodeInput) - def nodeB = Mock(NodeInput) - def nodeC = Mock(NodeInput) - def nodeD = Mock(NodeInput) - - def switchAB = Mock(SwitchInput) - switchAB.getNodeA() >> nodeA - switchAB.getNodeB() >> nodeB - switchAB.allNodes() >> List.of(nodeA, nodeB) - def switchBC = Mock(SwitchInput) - switchBC.getNodeA() >> nodeB - switchBC.getNodeB() >> nodeC - switchBC.allNodes() >> List.of(nodeB, nodeC) - def switchCD = Mock(SwitchInput) - switchCD.getNodeA() >> nodeC - switchCD.getNodeB() >> nodeD - switchCD.allNodes() >> List.of(nodeC, nodeD) - - def switches = new HashSet() - switches.add(switchAB) - switches.add(switchBC) - switches.add(switchCD) - - def possibleJunctions = new HashSet() - possibleJunctions.add(nodeC) - - def expected = new LinkedList() - expected.addFirst(nodeA) - expected.addLast(nodeB) - expected.addLast(nodeC) - - when: - def actual = ContainerUtils.traverseAlongSwitchChain(nodeA, switches, possibleJunctions) - - then: - actual == expected - } - - def "Traversing along a non existing switch chain returns the correct list of traveled nodes"() { - given: - def nodeA = Mock(NodeInput) - - def switches = new HashSet() - - def possibleJunctions = new HashSet() - - def expected = new LinkedList() - expected.addFirst(nodeA) - - when: - def actual = ContainerUtils.traverseAlongSwitchChain(nodeA, switches, possibleJunctions) - - then: - actual == expected - } - - def "Traversing along a cyclic switch chain throws an exception"() { - given: - def nodeA = Mock(NodeInput) - def nodeB = Mock(NodeInput) - def nodeC = Mock(NodeInput) - - def switchAB = Mock(SwitchInput) - switchAB.getNodeA() >> nodeA - switchAB.getNodeB() >> nodeB - switchAB.allNodes() >> List.of(nodeA, nodeB) - def switchBC = Mock(SwitchInput) - switchBC.getNodeA() >> nodeB - switchBC.getNodeB() >> nodeC - switchBC.allNodes() >> List.of(nodeB, nodeC) - def switchCA = Mock(SwitchInput) - switchCA.getNodeA() >> nodeC - switchCA.getNodeB() >> nodeA - switchCA.allNodes() >> List.of(nodeC, nodeA) - - def switches = new HashSet() - switches.add(switchAB) - switches.add(switchBC) - switches.add(switchCA) - - def possibleJunctions = new HashSet() - - when: - ContainerUtils.traverseAlongSwitchChain(nodeA, switches, possibleJunctions) - - then: - IllegalArgumentException ex = thrown() - ex.message == "Cannot traverse along switch chain, as there is a junction included at node Mock for type " + - "'NodeInput' named 'nodeA'" - } - - def "Traversing along a switch chain with switch junction throws an exception"() { - given: - def nodeA = Mock(NodeInput) - def nodeB = Mock(NodeInput) - def nodeC = Mock(NodeInput) - def nodeD = Mock(NodeInput) - - def switchAB = Mock(SwitchInput) - switchAB.getNodeA() >> nodeA - switchAB.getNodeB() >> nodeB - switchAB.allNodes() >> List.of(nodeA, nodeB) - def switchBC = Mock(SwitchInput) - switchBC.getNodeA() >> nodeB - switchBC.getNodeB() >> nodeC - switchBC.allNodes() >> List.of(nodeB, nodeC) - def switchBD = Mock(SwitchInput) - switchBD.getNodeA() >> nodeB - switchBD.getNodeB() >> nodeD - switchBD.allNodes() >> List.of(nodeB, nodeD) - - def switches = new HashSet() - switches.add(switchAB) - switches.add(switchBC) - switches.add(switchBD) - - def possibleJunctions = new HashSet() - - when: - ContainerUtils.traverseAlongSwitchChain(nodeA, switches, possibleJunctions) - - then: - IllegalArgumentException ex = thrown() - ex.message == "Cannot traverse along switch chain, as there is a junction included at node Mock for type " + - "'NodeInput' named 'nodeB'" - } - def "Determining the surrounding sub grid containers of a two winding transformer w/o switchgear works fine"() { given: def nodeD = Mock(NodeInput) @@ -762,13 +593,13 @@ class ContainerUtilsTest extends Specification { nodeA.getSubnet() >> 1 def nodeB = Mock(NodeInput) nodeB.getUuid() >> UUID.fromString("8361b082-9d4c-4c54-97d0-2df9ac35333c") - nodeB.getSubnet() >> 2 + nodeB.getSubnet() >> 1 def nodeC = Mock(NodeInput) nodeC.getUuid() >> UUID.fromString("b9e4f16b-0317-4794-9f53-339db45a2092") - nodeC.getSubnet() >> 2 + nodeC.getSubnet() >> 1 def nodeD = Mock(NodeInput) nodeD.getUuid() >> UUID.fromString("ae4869d5-3551-4cce-a101-d61629716c4f") - nodeD.getSubnet() >> 2 + nodeD.getSubnet() >> 1 def nodeE = Mock(NodeInput) nodeE.getUuid() >> UUID.fromString("5d4107b2-385b-40fe-a668-19414bf45d9d") nodeE.getSubnet() >> 2 diff --git a/src/test/groovy/edu/ie3/datamodel/utils/validation/EmValidationUtilsTest.groovy b/src/test/groovy/edu/ie3/datamodel/utils/validation/EmValidationUtilsTest.groovy deleted file mode 100644 index ed5139031..000000000 --- a/src/test/groovy/edu/ie3/datamodel/utils/validation/EmValidationUtilsTest.groovy +++ /dev/null @@ -1,41 +0,0 @@ -/* - * © 2025. TU Dortmund University, - * Institute of Energy Systems, Energy Efficiency and Energy Economics, - * Research group Distribution grid planning and operation - */ -package edu.ie3.datamodel.utils.validation - -import edu.ie3.datamodel.exceptions.InvalidEntityException -import edu.ie3.datamodel.exceptions.ValidationException -import edu.ie3.datamodel.utils.Try -import edu.ie3.test.common.GridTestData -import spock.lang.Specification - -class EmValidationUtilsTest extends Specification { - - def "Smoke Test: Correct energy management system throws no exception"() { - given: - def em = GridTestData.energyManagementInput - - when: - List> tries = EnergyManagementValidationUtils.check(em) - - then: - tries.every { it.success } - } - - def "The check method recognizes all potential errors for an energy management input"() { - when: - List> exceptions = EnergyManagementValidationUtils.check(invalidEm).stream().filter { it -> it.failure }.toList() - - then: - exceptions.size() == expectedSize - Exception ex = exceptions.get(0).exception.get() - ex.class == expectedException.class - ex.message == expectedException.message - - where: - invalidEm || expectedSize || expectedException - GridTestData.energyManagementInput.copy().controlStrategy(null).build() || 1 || new InvalidEntityException("No control strategy of energy management defined for", invalidEm) - } -} diff --git a/src/test/groovy/edu/ie3/test/common/GridTestData.groovy b/src/test/groovy/edu/ie3/test/common/GridTestData.groovy index 87c13de89..2ca15217c 100644 --- a/src/test/groovy/edu/ie3/test/common/GridTestData.groovy +++ b/src/test/groovy/edu/ie3/test/common/GridTestData.groovy @@ -9,7 +9,6 @@ import static edu.ie3.datamodel.models.StandardUnits.* import static edu.ie3.util.quantities.PowerSystemUnits.* import edu.ie3.datamodel.models.OperationTime -import edu.ie3.datamodel.models.input.EmInput import edu.ie3.datamodel.models.input.MeasurementUnitInput import edu.ie3.datamodel.models.input.NodeInput import edu.ie3.datamodel.models.input.OperatorInput @@ -388,13 +387,4 @@ class GridTestData { true, true ) - - public static final EmInput energyManagementInput = new EmInput( - UUID.fromString("4bef6955-5e31-4283-8920-5e4cae267a23"), - "test_energyManagement", - profBroccoli, - defaultOperationTime, - "PRIORITIZED", - null, - ) } From 37c9e08b754c73b8f67cc0e596d843de7ba0eac6 Mon Sep 17 00:00:00 2001 From: staudtMarius Date: Fri, 11 Jul 2025 12:53:49 +0200 Subject: [PATCH 2/5] Including reviewer's comments. --- .../figures/transformerWithSwitchGear.png | Bin 19186 -> 16091 bytes .../figures/transformerWithSwitchGear.tex | 12 ++-- .../models/input/grid/gridcontainer.md | 11 ++++ .../datamodel/utils/ContainerUtilsTest.groovy | 62 ------------------ 4 files changed, 17 insertions(+), 68 deletions(-) diff --git a/docs/readthedocs/_static/figures/transformerWithSwitchGear.png b/docs/readthedocs/_static/figures/transformerWithSwitchGear.png index 310ccf99f4d3b223570b7ac18c9f976c8b58d5c8..0ad8468fcbbf479dbad0f149185423b2e2a4602c 100644 GIT binary patch literal 16091 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%0A(C6HEo-(|O)=g8k8Kq}wkRph5kbxT$52 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 Uwp1&97dahyX=SNWNz)Ji17~9Zm;e9( literal 19186 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 Xb93-%@auoac0o=?`FWAF>AU{}+M8cI diff --git a/docs/readthedocs/_static/figures/transformerWithSwitchGear.tex b/docs/readthedocs/_static/figures/transformerWithSwitchGear.tex index 8ca4ed98a..ac2d44160 100644 --- a/docs/readthedocs/_static/figures/transformerWithSwitchGear.tex +++ b/docs/readthedocs/_static/figures/transformerWithSwitchGear.tex @@ -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); @@ -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} \ No newline at end of file diff --git a/docs/readthedocs/models/input/grid/gridcontainer.md b/docs/readthedocs/models/input/grid/gridcontainer.md index d6411b318..2ee0f95fe 100644 --- a/docs/readthedocs/models/input/grid/gridcontainer.md +++ b/docs/readthedocs/models/input/grid/gridcontainer.md @@ -20,6 +20,17 @@ Why predominant? As of convention, the `SubGridContainers` hold also reference to the transformers leading to higher sub grids and their higher voltage coupling point. +![Sub grid boundary definition for transformers with upstream switchgear](../../../_static/figures/transformerWithSwitchGear.png) + +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, 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: ## Attributes, Units and Remarks diff --git a/src/test/groovy/edu/ie3/datamodel/utils/ContainerUtilsTest.groovy b/src/test/groovy/edu/ie3/datamodel/utils/ContainerUtilsTest.groovy index 240312437..4dba21666 100644 --- a/src/test/groovy/edu/ie3/datamodel/utils/ContainerUtilsTest.groovy +++ b/src/test/groovy/edu/ie3/datamodel/utils/ContainerUtilsTest.groovy @@ -585,66 +585,4 @@ class ContainerUtilsTest extends Specification { then: actual == expected } - - def "Determining the surrounding sub grid containers of a two winding transformer w/ switchgear works fine"() { - given: - def nodeA = Mock(NodeInput) - nodeA.getUuid() >> UUID.fromString("a37b2501-70c5-479f-92f9-d5b0e4628b2b") - nodeA.getSubnet() >> 1 - def nodeB = Mock(NodeInput) - nodeB.getUuid() >> UUID.fromString("8361b082-9d4c-4c54-97d0-2df9ac35333c") - nodeB.getSubnet() >> 1 - def nodeC = Mock(NodeInput) - nodeC.getUuid() >> UUID.fromString("b9e4f16b-0317-4794-9f53-339db45a2092") - nodeC.getSubnet() >> 1 - def nodeD = Mock(NodeInput) - nodeD.getUuid() >> UUID.fromString("ae4869d5-3551-4cce-a101-d61629716c4f") - nodeD.getSubnet() >> 1 - def nodeE = Mock(NodeInput) - nodeE.getUuid() >> UUID.fromString("5d4107b2-385b-40fe-a668-19414bf45d9d") - nodeE.getSubnet() >> 2 - - def transformer = Mock(Transformer2WInput) - transformer.getUuid() >> UUID.fromString("ddcdd72a-5f97-4bef-913b-d32d31216e27") - transformer.getNodeA() >> nodeD - transformer.getNodeB() >> nodeE - transformer.allNodes() >> List.of(nodeD, nodeE) - - def switchAB = Mock(SwitchInput) - switchAB.getUuid() >> UUID.fromString("5fcb8705-1436-4fbe-97b3-d2dcaf6a783b") - switchAB.allNodes() >> List.of(nodeA, nodeB) - def switchBC = Mock(SwitchInput) - switchBC.getUuid() >> UUID.fromString("4ca81b0b-e06d-408e-a991-de140f4e229b") - switchBC.allNodes() >> List.of(nodeB, nodeC) - def switchCD = Mock(SwitchInput) - switchCD.getUuid() >> UUID.fromString("92ce075e-9e3b-4ee6-89b6-19e6372fba01") - switchCD.allNodes() >> List.of(nodeC, nodeD) - - def rawGridElements = new RawGridElements([ - nodeA, - nodeB, - nodeC, - nodeD, - nodeE, - transformer, - switchAB, - switchBC, - switchCD - ]) - - def subGrid1 = Mock(SubGridContainer) - def subGrid2 = Mock(SubGridContainer) - def subGridMapping = [ - 1: subGrid1, - 2: subGrid2 - ] - - def expected = new ContainerUtils.TransformerSubGridContainers(subGrid1, subGrid2) - - when: - def actual = ContainerUtils.getSubGridContainers(transformer, rawGridElements, subGridMapping) - - then: - actual == expected - } } From fac745005c7f6df255c4da7fe58d80e5f58264e4 Mon Sep 17 00:00:00 2001 From: staudtMarius Date: Fri, 11 Jul 2025 12:56:25 +0200 Subject: [PATCH 3/5] fix merge issues --- .../EnergyManagementValidationUtils.java | 48 +++++++++++++++++++ .../validation/EmValidationUtilsTest.groovy | 41 ++++++++++++++++ 2 files changed, 89 insertions(+) create mode 100644 src/main/java/edu/ie3/datamodel/utils/validation/EnergyManagementValidationUtils.java create mode 100644 src/test/groovy/edu/ie3/datamodel/utils/validation/EmValidationUtilsTest.groovy diff --git a/src/main/java/edu/ie3/datamodel/utils/validation/EnergyManagementValidationUtils.java b/src/main/java/edu/ie3/datamodel/utils/validation/EnergyManagementValidationUtils.java new file mode 100644 index 000000000..9b2894cca --- /dev/null +++ b/src/main/java/edu/ie3/datamodel/utils/validation/EnergyManagementValidationUtils.java @@ -0,0 +1,48 @@ +/* + * © 2021. TU Dortmund University, + * Institute of Energy Systems, Energy Efficiency and Energy Economics, + * Research group Distribution grid planning and operation +*/ +package edu.ie3.datamodel.utils.validation; + +import edu.ie3.datamodel.exceptions.InvalidEntityException; +import edu.ie3.datamodel.exceptions.ValidationException; +import edu.ie3.datamodel.models.input.EmInput; +import edu.ie3.datamodel.utils.Try; +import java.util.ArrayList; +import java.util.List; + +public class EnergyManagementValidationUtils extends ValidationUtils { + + /** Private Constructor as this class is not meant to be instantiated */ + private EnergyManagementValidationUtils() { + throw new IllegalStateException("Don't try and instantiate a Utility class."); + } + + /** + * Validates a energy management unit if: + * + *
    + *
  • its control strategy is not null + *
+ * + * A "distribution" method, that forwards the check request to specific implementations to fulfill + * the checking task, based on the class of the given object. + * + * @param energyManagement EmInput to validate + * @return a list of try objects either containing an {@link ValidationException} or an empty + * Success + */ + protected static List> check(EmInput energyManagement) { + List> exceptions = new ArrayList<>(); + + exceptions.add( + Try.ofVoid( + energyManagement.getControlStrategy() == null, + () -> + new InvalidEntityException( + "No control strategy of energy management defined for", energyManagement))); + + return exceptions; + } +} diff --git a/src/test/groovy/edu/ie3/datamodel/utils/validation/EmValidationUtilsTest.groovy b/src/test/groovy/edu/ie3/datamodel/utils/validation/EmValidationUtilsTest.groovy new file mode 100644 index 000000000..ed5139031 --- /dev/null +++ b/src/test/groovy/edu/ie3/datamodel/utils/validation/EmValidationUtilsTest.groovy @@ -0,0 +1,41 @@ +/* + * © 2025. TU Dortmund University, + * Institute of Energy Systems, Energy Efficiency and Energy Economics, + * Research group Distribution grid planning and operation + */ +package edu.ie3.datamodel.utils.validation + +import edu.ie3.datamodel.exceptions.InvalidEntityException +import edu.ie3.datamodel.exceptions.ValidationException +import edu.ie3.datamodel.utils.Try +import edu.ie3.test.common.GridTestData +import spock.lang.Specification + +class EmValidationUtilsTest extends Specification { + + def "Smoke Test: Correct energy management system throws no exception"() { + given: + def em = GridTestData.energyManagementInput + + when: + List> tries = EnergyManagementValidationUtils.check(em) + + then: + tries.every { it.success } + } + + def "The check method recognizes all potential errors for an energy management input"() { + when: + List> exceptions = EnergyManagementValidationUtils.check(invalidEm).stream().filter { it -> it.failure }.toList() + + then: + exceptions.size() == expectedSize + Exception ex = exceptions.get(0).exception.get() + ex.class == expectedException.class + ex.message == expectedException.message + + where: + invalidEm || expectedSize || expectedException + GridTestData.energyManagementInput.copy().controlStrategy(null).build() || 1 || new InvalidEntityException("No control strategy of energy management defined for", invalidEm) + } +} From 324986725aecbf35230409c4b2d4858d733c6bac Mon Sep 17 00:00:00 2001 From: staudtMarius Date: Fri, 11 Jul 2025 13:04:47 +0200 Subject: [PATCH 4/5] fixing merge issue. --- .../groovy/edu/ie3/test/common/GridTestData.groovy | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/test/groovy/edu/ie3/test/common/GridTestData.groovy b/src/test/groovy/edu/ie3/test/common/GridTestData.groovy index 2ca15217c..87c13de89 100644 --- a/src/test/groovy/edu/ie3/test/common/GridTestData.groovy +++ b/src/test/groovy/edu/ie3/test/common/GridTestData.groovy @@ -9,6 +9,7 @@ import static edu.ie3.datamodel.models.StandardUnits.* import static edu.ie3.util.quantities.PowerSystemUnits.* import edu.ie3.datamodel.models.OperationTime +import edu.ie3.datamodel.models.input.EmInput import edu.ie3.datamodel.models.input.MeasurementUnitInput import edu.ie3.datamodel.models.input.NodeInput import edu.ie3.datamodel.models.input.OperatorInput @@ -387,4 +388,13 @@ class GridTestData { true, true ) + + public static final EmInput energyManagementInput = new EmInput( + UUID.fromString("4bef6955-5e31-4283-8920-5e4cae267a23"), + "test_energyManagement", + profBroccoli, + defaultOperationTime, + "PRIORITIZED", + null, + ) } From 829e37bbf05451bab08b188cd2c34a6e07855b9a Mon Sep 17 00:00:00 2001 From: staudtMarius Date: Fri, 11 Jul 2025 14:48:24 +0200 Subject: [PATCH 5/5] Including reviewer's comments. --- docs/readthedocs/models/input/grid/gridcontainer.md | 5 ++--- .../groovy/edu/ie3/datamodel/utils/ContainerUtilsTest.groovy | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/docs/readthedocs/models/input/grid/gridcontainer.md b/docs/readthedocs/models/input/grid/gridcontainer.md index 2ee0f95fe..9c8eebd44 100644 --- a/docs/readthedocs/models/input/grid/gridcontainer.md +++ b/docs/readthedocs/models/input/grid/gridcontainer.md @@ -24,9 +24,8 @@ 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, 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. +We defined, that all nodes in upstream direction of the transformer, including those, which 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. diff --git a/src/test/groovy/edu/ie3/datamodel/utils/ContainerUtilsTest.groovy b/src/test/groovy/edu/ie3/datamodel/utils/ContainerUtilsTest.groovy index 4dba21666..660603fe9 100644 --- a/src/test/groovy/edu/ie3/datamodel/utils/ContainerUtilsTest.groovy +++ b/src/test/groovy/edu/ie3/datamodel/utils/ContainerUtilsTest.groovy @@ -553,7 +553,7 @@ class ContainerUtilsTest extends Specification { * - filtering of system participants can be tested * - filtering of graphic elements can be tested */ - def "Determining the surrounding sub grid containers of a two winding transformer w/o switchgear works fine"() { + def "Determining the surrounding sub grid containers of a two winding transformer works fine"() { given: def nodeD = Mock(NodeInput) nodeD.getUuid() >> UUID.fromString("ae4869d5-3551-4cce-a101-d61629716c4f")