diff --git a/open-reac/src/main/java/com/powsybl/openreac/OpenReacRunner.java b/open-reac/src/main/java/com/powsybl/openreac/OpenReacRunner.java index 80e6690d..6ea11480 100644 --- a/open-reac/src/main/java/com/powsybl/openreac/OpenReacRunner.java +++ b/open-reac/src/main/java/com/powsybl/openreac/OpenReacRunner.java @@ -118,6 +118,6 @@ private static void checkParameters(Network network, String variantId, OpenReacP Objects.requireNonNull(config); Objects.requireNonNull(manager); Objects.requireNonNull(reportNode); - parameters.checkIntegrity(network); + parameters.checkIntegrity(network, Reports.createParameterIntegrityReporter(reportNode, network.getId())); } } diff --git a/open-reac/src/main/java/com/powsybl/openreac/Reports.java b/open-reac/src/main/java/com/powsybl/openreac/Reports.java index 070d36da..bb27751e 100644 --- a/open-reac/src/main/java/com/powsybl/openreac/Reports.java +++ b/open-reac/src/main/java/com/powsybl/openreac/Reports.java @@ -50,4 +50,12 @@ public static void reportVariableShuntCompensatorsSize(ReportNode reportNode, in .withUntypedValue("size", variableShuntCompensatorsSize) .add(); } + + public static ReportNode createParameterIntegrityReporter(ReportNode reportNode, String networkId) { + return reportNode.newReportNode() + .withMessageTemplate("openReacParameterIntegrity", "Open reac parameter integrity on network '${networkId}'") + .withUntypedValue("networkId", networkId) + .add(); + } + } diff --git a/open-reac/src/main/java/com/powsybl/openreac/parameters/input/OpenReacParameters.java b/open-reac/src/main/java/com/powsybl/openreac/parameters/input/OpenReacParameters.java index 6ee5a51b..4d945ff4 100644 --- a/open-reac/src/main/java/com/powsybl/openreac/parameters/input/OpenReacParameters.java +++ b/open-reac/src/main/java/com/powsybl/openreac/parameters/input/OpenReacParameters.java @@ -6,15 +6,17 @@ */ package com.powsybl.openreac.parameters.input; +import com.powsybl.commons.report.ReportNode; +import com.powsybl.commons.report.TypedValue; import com.powsybl.iidm.network.Network; import com.powsybl.iidm.network.VoltageLevel; import com.powsybl.openreac.exceptions.InvalidParametersException; import com.powsybl.openreac.parameters.input.algo.*; +import org.apache.commons.lang3.tuple.Pair; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.*; -import java.util.stream.Collectors; /** * This class stores all inputs parameters specific to the OpenReac optimizer. @@ -533,13 +535,69 @@ public List getAllAlgorithmParams() { return allAlgoParams; } + private void checkLowAndHighVoltageLimitIntegrity(Network network, boolean integrityVoltageLimitOverrides, + Map> voltageLevelsWithInconsistentLimits, + ReportNode reportNode) { + Map> voltageLevelsWithMissingLimits = new TreeMap<>(); + + // Check integrity of low/high voltage limits, taking into account voltage limit overrides + boolean integrityVoltageLevelLimits = checkLowVoltageLevelLimits(network, voltageLevelsWithMissingLimits); + integrityVoltageLevelLimits &= checkHighVoltageLevelLimits(network, voltageLevelsWithMissingLimits); + + if (!integrityVoltageLevelLimits || !integrityVoltageLimitOverrides) { + if (!voltageLevelsWithMissingLimits.isEmpty()) { + reportNode.newReportNode() + .withMessageTemplate("nbVoltageLevelsWithMissingLimits", "${size} voltage level(s) have undefined low and/or high voltage limits") + .withSeverity(TypedValue.INFO_SEVERITY) + .withUntypedValue("size", voltageLevelsWithMissingLimits.size()) + .add(); + voltageLevelsWithMissingLimits.forEach((key, value) -> { + String messageSuffix = "has undefined low and high voltage limits"; + if (value.getLeft() == 0) { + messageSuffix = "has undefined high voltage limit"; + } else if (value.getRight() == 0) { + messageSuffix = "has undefined low voltage limit"; + } + reportNode.newReportNode() + .withMessageTemplate("voltageLevelWithMissingLimits", "${vlId} " + messageSuffix) + .withSeverity(TypedValue.TRACE_SEVERITY) + .withUntypedValue("vlId", key) + .add(); + }); + } + if (!voltageLevelsWithInconsistentLimits.isEmpty()) { + reportNode.newReportNode() + .withMessageTemplate("nbVoltageLevelsWithInconsistentLimits", "${size} voltage level(s) have inconsistent low and/or high voltage limits") + .withSeverity(TypedValue.INFO_SEVERITY) + .withUntypedValue("size", voltageLevelsWithInconsistentLimits.size()) + .add(); + voltageLevelsWithInconsistentLimits.forEach((key, value) -> reportNode.newReportNode() + .withMessageTemplate("voltageLevelWithInconsistentLimits", "${vlId} has one or two inconsistent voltage limits (low voltage limit = ${low}, high voltage limit = ${high})") + .withSeverity(TypedValue.TRACE_SEVERITY) + .withUntypedValue("vlId", key) + .withUntypedValue("low", value.getLeft()) + .withUntypedValue("high", value.getRight()) + .add()); + } + + if (!integrityVoltageLevelLimits) { + throw new InvalidParametersException("At least one voltage level has an undefined or incorrect voltage limit."); + } else { + throw new InvalidParametersException("At least one voltage limit override is inconsistent."); + } + } + } + /** * Do some checks on the parameters given, such as provided IDs must correspond to the given network element * - * @param network Network on which ID are going to be infered + * @param network Network on which ID are going to be infered + * @param reportNode aggregates functional logging * @throws InvalidParametersException if the parameters contain some incoherences. */ - public void checkIntegrity(Network network) throws InvalidParametersException { + public void checkIntegrity(Network network, ReportNode reportNode) throws InvalidParametersException { + Map> voltageLevelsWithInconsistentLimits = new TreeMap<>(); + for (String shuntId : getVariableShuntCompensators()) { if (network.getShuntCompensator(shuntId) == null) { throw new InvalidParametersException("Shunt " + shuntId + " not found in the network."); @@ -562,17 +620,10 @@ public void checkIntegrity(Network network) throws InvalidParametersException { } // Check integrity of voltage overrides - boolean integrityVoltageLimitOverrides = checkVoltageLimitOverrides(network); - if (!integrityVoltageLimitOverrides) { - throw new InvalidParametersException("At least one voltage limit override is inconsistent."); - } + boolean integrityVoltageLimitOverrides = checkVoltageLimitOverrides(network, voltageLevelsWithInconsistentLimits); // Check integrity of low/high voltage limits, taking into account voltage limit overrides - boolean integrityVoltageLevelLimits = checkLowVoltageLevelLimits(network); - integrityVoltageLevelLimits &= checkHighVoltageLevelLimits(network); - if (!integrityVoltageLevelLimits) { - throw new InvalidParametersException("At least one voltage level has an undefined or incorrect voltage limit."); - } + checkLowAndHighVoltageLimitIntegrity(network, integrityVoltageLimitOverrides, voltageLevelsWithInconsistentLimits, reportNode); boolean integrityAlgorithmParameters = checkAlgorithmParametersIntegrity(); if (!integrityAlgorithmParameters) { @@ -580,6 +631,16 @@ public void checkIntegrity(Network network) throws InvalidParametersException { } } + /** + * Do some checks on the parameters given, such as provided IDs must correspond to the given network element + * + * @param network Network on which ID are going to be infered + * @throws InvalidParametersException if the parameters contain some incoherences. + */ + public void checkIntegrity(Network network) throws InvalidParametersException { + checkIntegrity(network, ReportNode.NO_OP); + } + /** * @return true if the algorithm parameters are consistent, false otherwise. */ @@ -628,7 +689,7 @@ public boolean checkAlgorithmParametersIntegrity() { * @return true if the low voltage level limits are correct taking into account low voltage limit overrides, * false otherwise. */ - boolean checkLowVoltageLevelLimits(Network network) { + boolean checkLowVoltageLevelLimits(Network network, Map> voltageLevelsWithMissingLimits) { boolean integrityVoltageLevelLimits = true; for (VoltageLevel vl : network.getVoltageLevels()) { @@ -637,9 +698,11 @@ boolean checkLowVoltageLevelLimits(Network network) { if (Double.isNaN(lowLimit)) { List overrides = getSpecificVoltageLimits(vl.getId(), VoltageLimitOverride.VoltageLimitType.LOW_VOLTAGE_LIMIT); if (overrides.size() != 1) { + voltageLevelsWithMissingLimits.merge(vl.getId(), Pair.of(1, 0), (old, value) -> Pair.of(old.getLeft() + 1, old.getRight())); LOGGER.warn("Voltage level {} has no low voltage limit defined. Please add one or use a voltage limit override.", vl.getId()); integrityVoltageLevelLimits = false; } else if (overrides.get(0).isRelative()) { // we have one and just one + voltageLevelsWithMissingLimits.merge(vl.getId(), Pair.of(1, 0), (old, value) -> Pair.of(old.getLeft() + 1, old.getRight())); LOGGER.warn("Relative voltage override impossible on undefined low voltage limit for voltage level {}.", vl.getId()); integrityVoltageLevelLimits = false; } @@ -655,7 +718,7 @@ boolean checkLowVoltageLevelLimits(Network network) { * @return true if the high voltage level limits are correct taking into account high voltage limit overrides, * false otherwise. */ - boolean checkHighVoltageLevelLimits(Network network) { + boolean checkHighVoltageLevelLimits(Network network, Map> voltageLevelsWithMissingLimits) { boolean integrityVoltageLevelLimits = true; for (VoltageLevel vl : network.getVoltageLevels()) { @@ -664,9 +727,11 @@ boolean checkHighVoltageLevelLimits(Network network) { if (Double.isNaN(highLimit)) { List overrides = getSpecificVoltageLimits(vl.getId(), VoltageLimitOverride.VoltageLimitType.HIGH_VOLTAGE_LIMIT); if (overrides.size() != 1) { + voltageLevelsWithMissingLimits.merge(vl.getId(), Pair.of(0, 1), (old, value) -> Pair.of(old.getLeft(), old.getRight() + 1)); LOGGER.warn("Voltage level {} has no high voltage limit defined. Please add one or use a voltage limit override.", vl.getId()); integrityVoltageLevelLimits = false; } else if (overrides.get(0).isRelative()) { + voltageLevelsWithMissingLimits.merge(vl.getId(), Pair.of(0, 1), (old, value) -> Pair.of(old.getLeft(), old.getRight() + 1)); LOGGER.warn("Relative voltage override impossible on undefined high voltage limit for voltage level {}.", vl.getId()); integrityVoltageLevelLimits = false; } @@ -677,12 +742,57 @@ boolean checkHighVoltageLevelLimits(Network network) { return integrityVoltageLevelLimits; } + private boolean checkRelativeVoltageLimitOverrides(VoltageLimitOverride voltageLimitOverride, VoltageLevel voltageLevel, + Map> voltageLevelsLimitsAfterOverride, + Map> voltageLevelsWithInconsistentLimits) { + boolean integrityVoltageLimitOverrides = true; + double value = voltageLimitOverride.getVoltageLimitType() == VoltageLimitOverride.VoltageLimitType.LOW_VOLTAGE_LIMIT ? + voltageLevel.getLowVoltageLimit() : voltageLevel.getHighVoltageLimit(); + if (Double.isNaN(value)) { + LOGGER.warn("Voltage level {} has undefined {}, relative voltage limit override is impossible.", + voltageLevel.getId(), voltageLimitOverride.getVoltageLimitType()); + integrityVoltageLimitOverrides = false; + } else { + // verify voltage limit override does not lead to negative limit value + double valueAfter = value + voltageLimitOverride.getLimit(); + if (voltageLimitOverride.getVoltageLimitType() == VoltageLimitOverride.VoltageLimitType.LOW_VOLTAGE_LIMIT) { + voltageLevelsLimitsAfterOverride.merge(voltageLevel.getId(), Pair.of(valueAfter, voltageLevel.getHighVoltageLimit()), (old, newValue) -> Pair.of(valueAfter, old.getRight())); + if (valueAfter < 0) { + voltageLevelsWithInconsistentLimits.merge(voltageLevel.getId(), Pair.of(valueAfter, voltageLevel.getHighVoltageLimit()), (old, newValue) -> Pair.of(valueAfter, old.getRight())); + } + } else { + voltageLevelsLimitsAfterOverride.merge(voltageLevel.getId(), Pair.of(voltageLevel.getLowVoltageLimit(), valueAfter), (old, newValue) -> Pair.of(old.getLeft(), valueAfter)); + if (valueAfter < 0) { + voltageLevelsWithInconsistentLimits.merge(voltageLevel.getId(), Pair.of(voltageLevel.getLowVoltageLimit(), valueAfter), (old, newValue) -> Pair.of(old.getLeft(), valueAfter)); + } + } + if (valueAfter < 0) { + LOGGER.warn("Voltage level {} relative override leads to a negative {}.", + voltageLevel.getId(), voltageLimitOverride.getVoltageLimitType()); + integrityVoltageLimitOverrides = false; + } + } + return integrityVoltageLimitOverrides; + } + + private void checkAbsoluteVoltageLimitOverrides(VoltageLimitOverride voltageLimitOverride, VoltageLevel voltageLevel, + Map> voltageLevelsLimitsAfterOverride) { + if (voltageLimitOverride.getVoltageLimitType() == VoltageLimitOverride.VoltageLimitType.LOW_VOLTAGE_LIMIT) { + voltageLevelsLimitsAfterOverride.merge(voltageLevel.getId(), Pair.of(voltageLimitOverride.getLimit(), voltageLevel.getHighVoltageLimit()), (old, newValue) -> Pair.of(voltageLimitOverride.getLimit(), old.getRight())); + } else { + voltageLevelsLimitsAfterOverride.merge(voltageLevel.getId(), Pair.of(voltageLevel.getLowVoltageLimit(), voltageLimitOverride.getLimit()), (old, newValue) -> Pair.of(old.getLeft(), voltageLimitOverride.getLimit())); + } + } + /** * @param network the network on which are applied voltage limit overrides. * @return true if the integrity of voltage limit overrides is verified, false otherwise. */ - boolean checkVoltageLimitOverrides(Network network) { + boolean checkVoltageLimitOverrides(Network network, + Map> voltageLevelsWithInconsistentLimits) { // Check integrity of voltage overrides + Map> voltageLevelsLimitsAfterOverride = new HashMap<>(); + boolean integrityVoltageLimitOverrides = true; for (VoltageLimitOverride voltageLimitOverride : getSpecificVoltageLimits()) { String voltageLevelId = voltageLimitOverride.getVoltageLevelId(); @@ -696,26 +806,28 @@ boolean checkVoltageLimitOverrides(Network network) { } if (voltageLimitOverride.isRelative()) { - double value = voltageLimitOverride.getVoltageLimitType() == VoltageLimitOverride.VoltageLimitType.LOW_VOLTAGE_LIMIT ? - voltageLevel.getLowVoltageLimit() : voltageLevel.getHighVoltageLimit(); - if (Double.isNaN(value)) { - LOGGER.warn("Voltage level {} has undefined {}, relative voltage limit override is impossible.", - voltageLevelId, voltageLimitOverride.getVoltageLimitType()); - integrityVoltageLimitOverrides = false; - } - // verify voltage limit override does not lead to negative limit value - if (value + voltageLimitOverride.getLimit() < 0) { - LOGGER.warn("Voltage level {} relative override leads to a negative {}.", - voltageLevelId, voltageLimitOverride.getVoltageLimitType()); + integrityVoltageLimitOverrides &= checkRelativeVoltageLimitOverrides(voltageLimitOverride, voltageLevel, voltageLevelsLimitsAfterOverride, voltageLevelsWithInconsistentLimits); + } else { + checkAbsoluteVoltageLimitOverrides(voltageLimitOverride, voltageLevel, voltageLevelsLimitsAfterOverride); + } + } + + // check low limit is lower than high limit after overrides + if (integrityVoltageLimitOverrides) { + for (Map.Entry> entry : voltageLevelsLimitsAfterOverride.entrySet()) { + String vlId = entry.getKey(); + if (entry.getValue().getLeft() > entry.getValue().getRight()) { + voltageLevelsWithInconsistentLimits.merge(vlId, Pair.of(entry.getValue().getLeft(), entry.getValue().getRight()), (old, newValue) -> Pair.of(entry.getValue().getLeft(), entry.getValue().getRight())); integrityVoltageLimitOverrides = false; } } } + return integrityVoltageLimitOverrides; } private List getSpecificVoltageLimits(String voltageLevelId, VoltageLimitOverride.VoltageLimitType type) { return specificVoltageLimits.stream() - .filter(limit -> limit.getVoltageLevelId().equals(voltageLevelId) && limit.getVoltageLimitType() == type).collect(Collectors.toList()); + .filter(limit -> limit.getVoltageLevelId().equals(voltageLevelId) && limit.getVoltageLimitType() == type).toList(); } } diff --git a/open-reac/src/test/java/com/powsybl/openreac/OpenReacParametersTest.java b/open-reac/src/test/java/com/powsybl/openreac/OpenReacParametersTest.java index 509e9b56..4b4f3665 100644 --- a/open-reac/src/test/java/com/powsybl/openreac/OpenReacParametersTest.java +++ b/open-reac/src/test/java/com/powsybl/openreac/OpenReacParametersTest.java @@ -10,6 +10,7 @@ import com.powsybl.iidm.network.*; import com.powsybl.openreac.exceptions.InvalidParametersException; import com.powsybl.openreac.parameters.input.OpenReacParameters; +import com.powsybl.openreac.parameters.input.VoltageLimitOverride; import com.powsybl.openreac.parameters.input.algo.OpenReacAlgoParam; import com.powsybl.openreac.parameters.input.algo.OpenReacAmplLogLevel; import com.powsybl.openreac.parameters.input.algo.OpenReacOptimisationObjective; @@ -26,7 +27,7 @@ * @author Nicolas PIERRE {@literal } * @author Pierre ARVY {@literal } */ -public class OpenReacParametersTest { +class OpenReacParametersTest { @Test void testObjectiveIntegrity() { @@ -412,6 +413,33 @@ void testListsOfParametersIntegrity() { testBusesWithReactiveSlacksParametersIntegrity(network, wrongId); } + @Test + void testMissingVoltageLevelsLimitsIntegrity() { + Network network = IeeeCdfNetworkFactory.create57(); + OpenReacParameters parameters = new OpenReacParameters(); + InvalidParametersException e = assertThrows(InvalidParametersException.class, () -> parameters.checkIntegrity(network)); + assertEquals("At least one voltage level has an undefined or incorrect voltage limit.", e.getMessage()); + } + + @Test + void testInconsistentVoltageLevelsLimitsIntegrity() { + Network network = IeeeCdfNetworkFactory.create57(); + setDefaultVoltageLimits(network); // set default voltage limits to every voltage levels of the network + VoltageLevel vl = network.getVoltageLevels().iterator().next(); + OpenReacParameters parameters = new OpenReacParameters(); + + // if low relative voltage override leads to low limit < 0, throws exception + parameters.addSpecificVoltageLimits(List.of(new VoltageLimitOverride(vl.getId(), VoltageLimitOverride.VoltageLimitType.LOW_VOLTAGE_LIMIT, true, -2.))); + InvalidParametersException e = assertThrows(InvalidParametersException.class, () -> parameters.checkIntegrity(network)); + assertEquals("At least one voltage limit override is inconsistent.", e.getMessage()); + + // if high relative voltage override leads to high limit lower than low limit, throws exception + parameters.getSpecificVoltageLimits().clear(); + parameters.addSpecificVoltageLimits(List.of(new VoltageLimitOverride(vl.getId(), VoltageLimitOverride.VoltageLimitType.HIGH_VOLTAGE_LIMIT, true, -0.5))); + InvalidParametersException e1 = assertThrows(InvalidParametersException.class, () -> parameters.checkIntegrity(network)); + assertEquals("At least one voltage limit override is inconsistent.", e1.getMessage()); + } + void testTwoWindingsTransformersParametersIntegrity(Network network, String wrongId) { OpenReacParameters parameters = new OpenReacParameters(); parameters.addVariableTwoWindingsTransformers(network.getTwoWindingsTransformerStream().map(TwoWindingsTransformer::getId).collect(Collectors.toList())); diff --git a/open-reac/src/test/java/com/powsybl/openreac/parameters/input/VoltageLevelLimitsOverrideInputTest.java b/open-reac/src/test/java/com/powsybl/openreac/parameters/input/VoltageLevelLimitsOverrideInputTest.java index aa085101..0d55b3d7 100644 --- a/open-reac/src/test/java/com/powsybl/openreac/parameters/input/VoltageLevelLimitsOverrideInputTest.java +++ b/open-reac/src/test/java/com/powsybl/openreac/parameters/input/VoltageLevelLimitsOverrideInputTest.java @@ -157,6 +157,7 @@ void testVoltageOverrideWithNegativeVoltageLimit() { params2.getSpecificVoltageLimits().clear(); voltageLimitsOverride2.clear(); + vl.setLowVoltageLimit(0.); voltageLimitsOverride2.add(new VoltageLimitOverride(vl.getId(), VoltageLimitOverride.VoltageLimitType.HIGH_VOLTAGE_LIMIT, true, -480)); params2.addSpecificVoltageLimits(voltageLimitsOverride2); assertDoesNotThrow(() -> params2.checkIntegrity(network)); // zero value