diff --git a/iidm/iidm-modification/src/main/java/com/powsybl/iidm/modification/scalable/AbstractCompoundScalable.java b/iidm/iidm-modification/src/main/java/com/powsybl/iidm/modification/scalable/AbstractCompoundScalable.java index 19efeda98a9..0c0431b2a3a 100644 --- a/iidm/iidm-modification/src/main/java/com/powsybl/iidm/modification/scalable/AbstractCompoundScalable.java +++ b/iidm/iidm-modification/src/main/java/com/powsybl/iidm/modification/scalable/AbstractCompoundScalable.java @@ -17,6 +17,8 @@ * @author Geoffroy Jamgotchian {@literal } */ abstract class AbstractCompoundScalable extends AbstractScalable { + protected double minValue = -Double.MAX_VALUE; + protected double maxValue = Double.MAX_VALUE; abstract Collection getScalables(); @@ -52,7 +54,7 @@ public double maximumValue(Network n, ScalingConvention powerConvention) { for (Scalable scalable : getScalables()) { value += scalable.maximumValue(n, powerConvention); } - return value; + return (powerConvention == ScalingConvention.GENERATOR) ? Math.min(maxValue, value) : Math.min(-minValue, value); } @Override @@ -68,7 +70,7 @@ public double minimumValue(Network n, ScalingConvention powerConvention) { for (Scalable scalable : getScalables()) { value += scalable.minimumValue(n, powerConvention); } - return value; + return (powerConvention == ScalingConvention.GENERATOR) ? Math.max(minValue, value) : Math.max(-maxValue, value); } @Override @@ -77,4 +79,17 @@ public void filterInjections(Network n, List injections, List scalable.filterInjections(n, injections, notFoundInjections); } } + + /** + * Returns the value of scaling asked, bounded by the minValue and maxValue. + * @param variationAsked unbounded value of scaling asked on the scalable + * @param currentGlobalPower current global power in the network + * @param scalingConvention This is required because the minValue and maxValue are in GENERATOR convention, so we need to know wwhat convention we use for the scaling. + * @return the value of scaling asked bounded by the minValue and maxValue, according to the scalingConvention. + */ + protected double getBoundedVariation(double variationAsked, double currentGlobalPower, ScalingConvention scalingConvention) { + double minWithConvention = scalingConvention == ScalingConvention.GENERATOR ? minValue : -maxValue; + double maxWithConvention = scalingConvention == ScalingConvention.GENERATOR ? maxValue : -minValue; + return Math.min(maxWithConvention - currentGlobalPower, Math.max(minWithConvention - currentGlobalPower, variationAsked)); + } } diff --git a/iidm/iidm-modification/src/main/java/com/powsybl/iidm/modification/scalable/ProportionalScalable.java b/iidm/iidm-modification/src/main/java/com/powsybl/iidm/modification/scalable/ProportionalScalable.java index a21c9516504..6d328d5e718 100644 --- a/iidm/iidm-modification/src/main/java/com/powsybl/iidm/modification/scalable/ProportionalScalable.java +++ b/iidm/iidm-modification/src/main/java/com/powsybl/iidm/modification/scalable/ProportionalScalable.java @@ -75,14 +75,24 @@ void setIterationPercentage(double iterationPercentage) { private final List scalablePercentageList; ProportionalScalable(List percentages, List scalables) { + this(percentages, scalables, -Double.MAX_VALUE, Double.MAX_VALUE); + } + + ProportionalScalable(List percentages, List scalables, double minValue, double maxValue) { checkPercentages(percentages, scalables); this.scalablePercentageList = new ArrayList<>(); for (int i = 0; i < scalables.size(); i++) { this.scalablePercentageList.add(new ScalablePercentage(scalables.get(i), percentages.get(i))); } + this.minValue = minValue; + this.maxValue = maxValue; } public ProportionalScalable(List injections, DistributionMode distributionMode) { + this(injections, distributionMode, -Double.MAX_VALUE, Double.MAX_VALUE); + } + + public ProportionalScalable(List injections, DistributionMode distributionMode, double minValue, double maxValue) { // Create the scalable for each injection List injectionScalables = injections.stream().map(ScalableAdapter::new).collect(Collectors.toList()); @@ -117,6 +127,9 @@ public ProportionalScalable(List injections, DistributionMo for (int i = 0; i < injectionScalables.size(); i++) { this.scalablePercentageList.add(new ScalablePercentage(injectionScalables.get(i), percentages.get(i))); } + + this.minValue = minValue; + this.maxValue = maxValue; } private double computeTotalDistribution(List injections, DistributionMode distributionMode) { @@ -263,16 +276,18 @@ public double scale(Network n, double asked, ScalingParameters parameters) { // Variation asked double variationAsked = Scalable.getVariationAsked(parameters, asked, currentGlobalPower); + double boundedVariation = getBoundedVariation(variationAsked, currentGlobalPower, parameters.getScalingConvention()); + // Adapt the asked value if needed - only used in RESPECT_OF_DISTRIBUTION mode if (parameters.getPriority() == RESPECT_OF_DISTRIBUTION) { - variationAsked = resizeAskedForFixedDistribution(n, variationAsked, parameters); + boundedVariation = resizeAskedForFixedDistribution(n, boundedVariation, parameters); } reinitIterationPercentage(); if (parameters.getPriority() == RESPECT_OF_VOLUME_ASKED) { - return iterativeScale(n, variationAsked, parameters); + return iterativeScale(n, boundedVariation, parameters); } else { - return scaleIteration(n, variationAsked, parameters); + return scaleIteration(n, boundedVariation, parameters); } } diff --git a/iidm/iidm-modification/src/main/java/com/powsybl/iidm/modification/scalable/Scalable.java b/iidm/iidm-modification/src/main/java/com/powsybl/iidm/modification/scalable/Scalable.java index 59edfb91c07..c608724b8f1 100644 --- a/iidm/iidm-modification/src/main/java/com/powsybl/iidm/modification/scalable/Scalable.java +++ b/iidm/iidm-modification/src/main/java/com/powsybl/iidm/modification/scalable/Scalable.java @@ -194,10 +194,18 @@ static ProportionalScalable proportional(List injections, P return new ProportionalScalable(injections, distributionMode); } + static ProportionalScalable proportional(List injections, ProportionalScalable.DistributionMode distributionMode, double minValue, double maxValue) { + return new ProportionalScalable(injections, distributionMode, minValue, maxValue); + } + static ProportionalScalable proportional(List percentages, List scalables) { return new ProportionalScalable(percentages, scalables); } + static ProportionalScalable proportional(List percentages, List scalables, double minValue, double maxValue) { + return new ProportionalScalable(percentages, scalables, minValue, maxValue); + } + static ProportionalScalable proportional(double percentage, Scalable scalable) { return new ProportionalScalable(Collections.singletonList(percentage), Collections.singletonList(scalable)); } @@ -227,28 +235,52 @@ static StackScalable stack(Injection... injections) { return new StackScalable(injectionScalables); } + static StackScalable stack(double minValue, double maxValue, Injection... injections) { + List injectionScalables = Arrays.stream(injections).map(ScalableAdapter::new).collect(Collectors.toList()); + return new StackScalable(injectionScalables, minValue, maxValue); + } + static StackScalable stack(List> injections) { List injectionScalables = injections.stream().map(ScalableAdapter::new).collect(Collectors.toList()); return new StackScalable(injectionScalables); } + static StackScalable stack(List> injections, double minValue, double maxValue) { + List injectionScalables = injections.stream().map(ScalableAdapter::new).collect(Collectors.toList()); + return new StackScalable(injectionScalables, minValue, maxValue); + } + static StackScalable stack(Scalable... scalables) { return new StackScalable(scalables); } + static StackScalable stack(double minValue, double maxValue, Scalable... scalables) { + return new StackScalable(minValue, maxValue, scalables); + } + static StackScalable stack(String... ids) { List identifierScalables = Arrays.stream(ids).map(ScalableAdapter::new).collect(Collectors.toList()); return new StackScalable(identifierScalables); } + static StackScalable stack(double minValue, double maxValue, String... ids) { + List identifierScalables = Arrays.stream(ids).map(ScalableAdapter::new).collect(Collectors.toList()); + return new StackScalable(identifierScalables, minValue, maxValue); + } + static UpDownScalable upDown(Scalable upScalable, Scalable downScalable) { return new UpDownScalable(upScalable, downScalable); } + static UpDownScalable upDown(Scalable upScalable, Scalable downScalable, double minValue, double maxValue) { + return new UpDownScalable(upScalable, downScalable, minValue, maxValue); + } + /** * Returns the value that has to be added to the network, depending on the type of variation chosen in the parameters - * @param scalingParameters Scaling parameters including a variation type (DELTA_P or TARGET_P) and a variation value - * @param currentGlobalPower current global power + * @param scalingParameters Scaling parameters including a variation type (DELTA_P or TARGET_P) + * @param askedValue value of scaling asked on the scalable + * @param currentGlobalPower current global power in the network * @return the variation value if the type is DELTA_P, else the difference between the variation value and the current global value sum */ static double getVariationAsked(ScalingParameters scalingParameters, double askedValue, double currentGlobalPower) { @@ -260,6 +292,8 @@ static double getVariationAsked(ScalingParameters scalingParameters, double aske /** * Returns the current power value for the injections corresponding to this Scalable * @param network Network in which the injections are defined + * @param asked value of scaling asked on the scalable. This is used to know in which direction we want to scale for UpDownScalables. + * @param scalingConvention The value is computed either with Generator or Load convention according to this parameter. * @return the current power value */ double getSteadyStatePower(Network network, double asked, ScalingConvention scalingConvention); diff --git a/iidm/iidm-modification/src/main/java/com/powsybl/iidm/modification/scalable/StackScalable.java b/iidm/iidm-modification/src/main/java/com/powsybl/iidm/modification/scalable/StackScalable.java index a5d21711fc2..9bb3f8bfcc2 100644 --- a/iidm/iidm-modification/src/main/java/com/powsybl/iidm/modification/scalable/StackScalable.java +++ b/iidm/iidm-modification/src/main/java/com/powsybl/iidm/modification/scalable/StackScalable.java @@ -22,11 +22,21 @@ class StackScalable extends AbstractCompoundScalable { private final List scalables; StackScalable(Scalable... scalables) { - this(Arrays.asList(scalables)); + this(Arrays.asList(scalables), -Double.MAX_VALUE, Double.MAX_VALUE); + } + + StackScalable(double minValue, double maxValue, Scalable... scalables) { + this(Arrays.asList(scalables), minValue, maxValue); } StackScalable(List scalables) { + this(scalables, -Double.MAX_VALUE, Double.MAX_VALUE); + } + + StackScalable(List scalables, double minValue, double maxValue) { this.scalables = Objects.requireNonNull(scalables); + this.minValue = minValue; + this.maxValue = maxValue; } @Override @@ -44,8 +54,10 @@ public double scale(Network n, double asked, ScalingParameters parameters) { // Variation asked double variationAsked = Scalable.getVariationAsked(parameters, asked, currentGlobalPower); + double boundedVariation = getBoundedVariation(variationAsked, currentGlobalPower, parameters.getScalingConvention()); + double done = 0; - double remaining = variationAsked; + double remaining = boundedVariation; for (Scalable scalable : scalables) { if (Math.abs(remaining) > EPSILON) { double v = scalable.scale(n, remaining, parameters); diff --git a/iidm/iidm-modification/src/main/java/com/powsybl/iidm/modification/scalable/UpDownScalable.java b/iidm/iidm-modification/src/main/java/com/powsybl/iidm/modification/scalable/UpDownScalable.java index c10a5ed2f43..99faa046860 100644 --- a/iidm/iidm-modification/src/main/java/com/powsybl/iidm/modification/scalable/UpDownScalable.java +++ b/iidm/iidm-modification/src/main/java/com/powsybl/iidm/modification/scalable/UpDownScalable.java @@ -18,10 +18,18 @@ class UpDownScalable extends AbstractScalable { private final Scalable upScalable; private final Scalable downScalable; + private final double minValue; + private final double maxValue; public UpDownScalable(Scalable upScalable, Scalable downScalable) { + this(upScalable, downScalable, -Double.MAX_VALUE, Double.MAX_VALUE); + } + + public UpDownScalable(Scalable upScalable, Scalable downScalable, double minValue, double maxValue) { this.upScalable = Objects.requireNonNull(upScalable); this.downScalable = Objects.requireNonNull(downScalable); + this.minValue = minValue; + this.maxValue = maxValue; } @Override @@ -38,18 +46,18 @@ public void reset(Network n) { @Override public double maximumValue(Network n, ScalingConvention scalingConvention) { if (scalingConvention == ScalingConvention.LOAD) { - return downScalable.maximumValue(n, scalingConvention) - upScalable.initialValue(n); + return Math.min(downScalable.maximumValue(n, scalingConvention) - upScalable.initialValue(n), maxValue); } else { - return upScalable.maximumValue(n, scalingConvention) + downScalable.initialValue(n); + return Math.min(upScalable.maximumValue(n, scalingConvention) + downScalable.initialValue(n), maxValue); } } @Override public double minimumValue(Network n, ScalingConvention scalingConvention) { if (scalingConvention == ScalingConvention.LOAD) { - return upScalable.minimumValue(n, scalingConvention) - downScalable.initialValue(n); + return Math.max(upScalable.minimumValue(n, scalingConvention) - downScalable.initialValue(n), minValue); } else { - return downScalable.minimumValue(n, scalingConvention) + upScalable.initialValue(n); + return Math.max(downScalable.minimumValue(n, scalingConvention) + upScalable.initialValue(n), minValue); } } @@ -61,7 +69,12 @@ public void filterInjections(Network network, List injections, List 0 ? upScalable.scale(n, asked, parameters) : downScalable.scale(n, asked, parameters); + double minWithConvention = parameters.getScalingConvention() == ScalingConvention.GENERATOR ? minValue : -maxValue; + double maxWithConvention = parameters.getScalingConvention() == ScalingConvention.GENERATOR ? maxValue : -minValue; + double boundedAsked = asked > 0 ? + Math.min(asked, maxWithConvention - getSteadyStatePower(n, asked, parameters.getScalingConvention())) : + Math.max(asked, minWithConvention - getSteadyStatePower(n, asked, parameters.getScalingConvention())); + return asked > 0 ? upScalable.scale(n, boundedAsked, parameters) : downScalable.scale(n, boundedAsked, parameters); } @Override diff --git a/iidm/iidm-modification/src/test/java/com/powsybl/iidm/modification/scalable/ProportionalScalableTest.java b/iidm/iidm-modification/src/test/java/com/powsybl/iidm/modification/scalable/ProportionalScalableTest.java index 6e950d6a56a..43e717cddaf 100644 --- a/iidm/iidm-modification/src/test/java/com/powsybl/iidm/modification/scalable/ProportionalScalableTest.java +++ b/iidm/iidm-modification/src/test/java/com/powsybl/iidm/modification/scalable/ProportionalScalableTest.java @@ -510,4 +510,152 @@ void testResizeAskedForVentilation() { PowsyblException e1 = assertThrows(PowsyblException.class, () -> proportionalScalable.scale(network, 100.0, scalingParametersProportional)); assertEquals("RESPECT_OF_DISTRIBUTION mode can only be used with a Generator, not class com.powsybl.iidm.network.impl.LoadImpl", e1.getMessage()); } + + @Test + void testMaxValueBoundsScalingUpGenConvention() { + ReporterModel reporterModel = new ReporterModel("scaling", "default"); + List> injectionsList = Arrays.asList(network.getLoad("l1"), network.getLoad("l2"), network.getDanglingLine("dl1")); + ProportionalScalable proportionalScalable; + double variationDone; + + double initialValue = injectionsList.stream().mapToDouble(injection -> { + if (injection instanceof Generator generator) { + return generator.getTargetP(); + } else if (injection instanceof Load load) { + return -load.getP0(); + } else if (injection instanceof DanglingLine danglingLine) { + return -danglingLine.getP0(); + } else { + throw new PowsyblException("Unexpected injection type"); + } + }).sum(); + double maxValue = initialValue + 75.0; + + // Proportional to P0 + ScalingParameters scalingParametersProportional = new ScalingParameters(Scalable.ScalingConvention.GENERATOR, + true, true, RESPECT_OF_VOLUME_ASKED, true, DELTA_P); + proportionalScalable = Scalable.proportional(injectionsList, PROPORTIONAL_TO_P0, -Double.MAX_VALUE, maxValue); + variationDone = proportionalScalable.scale(network, 100.0, scalingParametersProportional); + scalingReport(reporterModel, + "loads and dangling lines", + PROPORTIONAL_TO_P0, + scalingParametersProportional.getScalingType(), + 100.0, variationDone); + assertEquals(75, variationDone, 1e-5); + assertEquals(100.0 * (1.0 - 75 / 230.0), network.getLoad("l1").getP0(), 1e-5); + assertEquals(80 * (1.0 - 75 / 230.0), network.getLoad("l2").getP0(), 1e-5); + assertEquals(50.0 * (1.0 - 75 / 230.0), network.getDanglingLine("dl1").getP0(), 1e-5); + reset(); + } + + @Test + void testMaxValueBoundsScalingDownLoadConvention() { + ReporterModel reporterModel = new ReporterModel("scaling", "default"); + List> injectionsList = Arrays.asList(network.getLoad("l1"), network.getLoad("l2"), network.getDanglingLine("dl1")); + ProportionalScalable proportionalScalable; + double variationDone; + + double initialValue = injectionsList.stream().mapToDouble(injection -> { + if (injection instanceof Generator generator) { + return generator.getTargetP(); + } else if (injection instanceof Load load) { + return -load.getP0(); + } else if (injection instanceof DanglingLine danglingLine) { + return -danglingLine.getP0(); + } else { + throw new PowsyblException("Unexpected injection type"); + } + }).sum(); + double maxValue = initialValue + 75.0; + + // Proportional to P0 + ScalingParameters scalingParametersProportional = new ScalingParameters(Scalable.ScalingConvention.LOAD, + true, true, RESPECT_OF_VOLUME_ASKED, true, DELTA_P); + proportionalScalable = Scalable.proportional(injectionsList, PROPORTIONAL_TO_P0, -Double.MAX_VALUE, maxValue); + variationDone = proportionalScalable.scale(network, -100.0, scalingParametersProportional); + scalingReport(reporterModel, + "loads and dangling lines", + PROPORTIONAL_TO_P0, + scalingParametersProportional.getScalingType(), + -100.0, variationDone); + assertEquals(-75, variationDone, 1e-5); + assertEquals(100.0 * (1.0 - 75 / 230.0), network.getLoad("l1").getP0(), 1e-5); + assertEquals(80 * (1.0 - 75 / 230.0), network.getLoad("l2").getP0(), 1e-5); + assertEquals(50.0 * (1.0 - 75 / 230.0), network.getDanglingLine("dl1").getP0(), 1e-5); + reset(); + } + + @Test + void testMinValueBoundsScalingDownGenConvention() { + ReporterModel reporterModel = new ReporterModel("scaling", "default"); + List> injectionsList = Arrays.asList(network.getLoad("l1"), network.getLoad("l2"), network.getDanglingLine("dl1")); + ProportionalScalable proportionalScalable; + double variationDone; + + double initialValue = injectionsList.stream().mapToDouble(injection -> { + if (injection instanceof Generator generator) { + return generator.getTargetP(); + } else if (injection instanceof Load load) { + return -load.getP0(); + } else if (injection instanceof DanglingLine danglingLine) { + return -danglingLine.getP0(); + } else { + throw new PowsyblException("Unexpected injection type"); + } + }).sum(); + double minValue = initialValue - 75.0; + + // Proportional to P0 + ScalingParameters scalingParametersProportional = new ScalingParameters(Scalable.ScalingConvention.GENERATOR, + true, true, RESPECT_OF_VOLUME_ASKED, true, DELTA_P); + proportionalScalable = Scalable.proportional(injectionsList, PROPORTIONAL_TO_P0, minValue, Double.MAX_VALUE); + variationDone = proportionalScalable.scale(network, -100.0, scalingParametersProportional); + scalingReport(reporterModel, + "loads and dangling lines", + PROPORTIONAL_TO_P0, + scalingParametersProportional.getScalingType(), + -100.0, variationDone); + assertEquals(-75, variationDone, 1e-5); + assertEquals(100.0 * (1.0 + 75 / 230.0), network.getLoad("l1").getP0(), 1e-5); + assertEquals(80 * (1.0 + 75 / 230.0), network.getLoad("l2").getP0(), 1e-5); + assertEquals(50.0 * (1.0 + 75 / 230.0), network.getDanglingLine("dl1").getP0(), 1e-5); + reset(); + } + + @Test + void testMinValueBoundsScalingUpLoadConvention() { + ReporterModel reporterModel = new ReporterModel("scaling", "default"); + List> injectionsList = Arrays.asList(network.getLoad("l1"), network.getLoad("l2"), network.getDanglingLine("dl1")); + ProportionalScalable proportionalScalable; + double variationDone; + + double initialValue = injectionsList.stream().mapToDouble(injection -> { + if (injection instanceof Generator generator) { + return generator.getTargetP(); + } else if (injection instanceof Load load) { + return -load.getP0(); + } else if (injection instanceof DanglingLine danglingLine) { + return -danglingLine.getP0(); + } else { + throw new PowsyblException("Unexpected injection type"); + } + }).sum(); + double minValue = initialValue - 75.0; + + // Proportional to P0 + ScalingParameters scalingParametersProportional = new ScalingParameters(Scalable.ScalingConvention.LOAD, + true, true, RESPECT_OF_VOLUME_ASKED, true, DELTA_P); + proportionalScalable = Scalable.proportional(injectionsList, PROPORTIONAL_TO_P0, minValue, Double.MAX_VALUE); + variationDone = proportionalScalable.scale(network, 100.0, scalingParametersProportional); + scalingReport(reporterModel, + "loads and dangling lines", + PROPORTIONAL_TO_P0, + scalingParametersProportional.getScalingType(), + 100.0, variationDone); + assertEquals(75, variationDone, 1e-5); + assertEquals(100.0 * (1.0 + 75 / 230.0), network.getLoad("l1").getP0(), 1e-5); + assertEquals(80 * (1.0 + 75 / 230.0), network.getLoad("l2").getP0(), 1e-5); + assertEquals(50.0 * (1.0 + 75 / 230.0), network.getDanglingLine("dl1").getP0(), 1e-5); + reset(); + } } diff --git a/iidm/iidm-modification/src/test/java/com/powsybl/iidm/modification/scalable/StackScalableTest.java b/iidm/iidm-modification/src/test/java/com/powsybl/iidm/modification/scalable/StackScalableTest.java index 12b47bdb42f..406487a03c6 100644 --- a/iidm/iidm-modification/src/test/java/com/powsybl/iidm/modification/scalable/StackScalableTest.java +++ b/iidm/iidm-modification/src/test/java/com/powsybl/iidm/modification/scalable/StackScalableTest.java @@ -43,18 +43,18 @@ class StackScalableTest { void setUp() { network = createNetworkwithDanglingLineAndBattery(); - g1 = Scalable.onGenerator("g1"); - g2 = Scalable.onGenerator("g2"); - g3 = Scalable.onGenerator("g3", -10, 80); - s = Scalable.onGenerator("s"); + g1 = Scalable.onGenerator("g1"); //initial targetP : 80MW + g2 = Scalable.onGenerator("g2"); //initial targetP : 50MW + g3 = Scalable.onGenerator("g3", -10, 80); //initial targetP : 30MW + s = Scalable.onGenerator("s"); //initial targetP : 0MW unknownGenerator = Scalable.onGenerator("unknown"); - l1 = Scalable.onLoad("l1"); - l2 = Scalable.onLoad("l2", 20, 80); - l3 = Scalable.onLoad("l3", -50, 100); + l1 = Scalable.onLoad("l1"); //initial P0 : 100MW + l2 = Scalable.onLoad("l2", 20, 80); //initial P0 : 80MW + l3 = Scalable.onLoad("l3", -50, 100); //initial P0 : 50MW unknownLoad = Scalable.onLoad("unknown"); unknownDanglingLine = Scalable.onDanglingLine("unknown"); - dl1 = Scalable.onDanglingLine("dl1", 20, 80); + dl1 = Scalable.onDanglingLine("dl1", 20, 80); //initial P0 : 50MW } private void reset() { @@ -125,4 +125,52 @@ void testScaleOnGeneratorsStackingTargetPLessThanCurrent() { assertEquals(30.0, network.getGenerator("g3").getTargetP(), 1e-5); reset(); } + + @Test + void testMaxValueBoundsScalingUp() { + ReporterModel reporterModel = new ReporterModel("scaling", "default"); + List generatorList = Arrays.asList(network.getGenerator("g1"), network.getGenerator("g2"), network.getGenerator("g3")); + ScalingParameters scalingParameters = new ScalingParameters(Scalable.ScalingConvention.GENERATOR, + true, true, ONESHOT, true, DELTA_P); + + double initialValue = generatorList.stream().mapToDouble(Generator::getTargetP).sum(); + double maxValue = initialValue + 75.0; + + // Proportional to Target P + StackScalable stackScalable = Scalable.stack(generatorList, -Double.MAX_VALUE, maxValue); + double variationDone = stackScalable.scale(network, 100.0, scalingParameters); + scalingReport(reporterModel, + "generators", + scalingParameters.getScalingType(), + 100.0, variationDone); + assertEquals(75.0, variationDone, 1e-5); + assertEquals(150.0, network.getGenerator("g1").getTargetP(), 1e-5); + assertEquals(55.0, network.getGenerator("g2").getTargetP(), 1e-5); + assertEquals(30.0, network.getGenerator("g3").getTargetP(), 1e-5); + reset(); + } + + @Test + void testMinValueBoundsScalingDown() { + ReporterModel reporterModel = new ReporterModel("scaling", "default"); + List generatorList = Arrays.asList(network.getGenerator("g1"), network.getGenerator("g2"), network.getGenerator("g3")); + ScalingParameters scalingParameters = new ScalingParameters(Scalable.ScalingConvention.GENERATOR, + true, true, ONESHOT, true, DELTA_P); + + double initialValue = generatorList.stream().mapToDouble(Generator::getTargetP).sum(); + double minValue = initialValue - 75.0; + + // Proportional to Target P + StackScalable stackScalable = Scalable.stack(generatorList, minValue, Double.MAX_VALUE); + double variationDone = stackScalable.scale(network, -100.0, scalingParameters); + scalingReport(reporterModel, + "generators", + scalingParameters.getScalingType(), + -100.0, variationDone); + assertEquals(-75.0, variationDone, 1e-5); + assertEquals(5.0, network.getGenerator("g1").getTargetP(), 1e-5); + assertEquals(50.0, network.getGenerator("g2").getTargetP(), 1e-5); + assertEquals(30.0, network.getGenerator("g3").getTargetP(), 1e-5); + reset(); + } } diff --git a/iidm/iidm-modification/src/test/java/com/powsybl/iidm/modification/scalable/UpDownScalableTest.java b/iidm/iidm-modification/src/test/java/com/powsybl/iidm/modification/scalable/UpDownScalableTest.java index b3f5a0a43c4..43f12d59866 100644 --- a/iidm/iidm-modification/src/test/java/com/powsybl/iidm/modification/scalable/UpDownScalableTest.java +++ b/iidm/iidm-modification/src/test/java/com/powsybl/iidm/modification/scalable/UpDownScalableTest.java @@ -6,8 +6,7 @@ */ package com.powsybl.iidm.modification.scalable; -import com.powsybl.iidm.network.Injection; -import com.powsybl.iidm.network.Network; +import com.powsybl.iidm.network.*; import org.junit.jupiter.api.Test; import java.util.ArrayList; @@ -83,7 +82,7 @@ void checkInjectionFilteringWorksAsExpected() { } @Test - void checkErrorOnGetCurrentPower() { + void checkGetCurrentPowerInBothDirections() { Network testNetwork = ScalableTestNetwork.createNetwork(); Scalable upScalable = Scalable.proportional(50, Scalable.onGenerator("g2"), 50, Scalable.onGenerator("unknown generator")); Scalable downScalable = Scalable.onLoad("l1", 50, 200); @@ -99,4 +98,70 @@ void checkErrorOnGetCurrentPower() { assertEquals(42, upDownScalable.getSteadyStatePower(testNetwork, asked, Scalable.ScalingConvention.LOAD)); assertEquals(-42, upDownScalable.getSteadyStatePower(testNetwork, asked, Scalable.ScalingConvention.GENERATOR)); } + + @Test + void testMaxValueBoundsScalingUpGenConvention() { + Network testNetwork = ScalableTestNetwork.createNetwork(); + Scalable upScalable = Scalable.proportional(100, Scalable.onGenerator("g2")); + Scalable downScalable = Scalable.onLoad("l1"); + + double initialValueUp = testNetwork.getGenerator("g2").getTargetP(); + Scalable upDownScalable = Scalable.upDown(upScalable, downScalable, -Double.MAX_VALUE, initialValueUp + 35); + + ScalingParameters parameters = new ScalingParameters(); + parameters.setScalingConvention(Scalable.ScalingConvention.GENERATOR); + + double asked = 100; + assertEquals(35, upDownScalable.scale(testNetwork, asked, parameters)); + } + + @Test + void testMaxValueBoundsScalingDownLoadConvention() { + Network testNetwork = ScalableTestNetwork.createNetwork(); + Scalable upScalable = Scalable.proportional(100, Scalable.onGenerator("g2")); + Scalable downScalable = Scalable.onLoad("l1"); + + double initialValueDown = -testNetwork.getLoad("l1").getP0(); + Scalable upDownScalable = Scalable.upDown(upScalable, downScalable, -Double.MAX_VALUE, initialValueDown + 35); + + ScalingParameters parameters = new ScalingParameters(); + parameters.setScalingConvention(Scalable.ScalingConvention.LOAD); + + double asked = -100; + assertEquals(-35, upDownScalable.scale(testNetwork, asked, parameters)); + } + + @Test + void testMinValueBoundsScalingDownGenConvention() { + Network testNetwork = ScalableTestNetwork.createNetwork(); + Scalable upScalable = Scalable.proportional(100, Scalable.onGenerator("g2")); + Scalable downScalable = Scalable.onLoad("l1"); + + double initialValueDown = -testNetwork.getLoad("l1").getP0(); + Scalable upDownScalable = Scalable.upDown(upScalable, downScalable, initialValueDown - 35, Double.MAX_VALUE); + + ScalingParameters parameters = new ScalingParameters(); + parameters.setScalingConvention(Scalable.ScalingConvention.GENERATOR); + + double asked = -100; + assertEquals(-35, upDownScalable.scale(testNetwork, asked, parameters)); + } + + @Test + void testMinValueBoundsScalingUpLoadConvention() { + Network testNetwork = ScalableTestNetwork.createNetwork(); + testNetwork.getGenerator("g2").setTargetP(50); + + Scalable upScalable = Scalable.proportional(100, Scalable.onGenerator("g2")); + Scalable downScalable = Scalable.onLoad("l1"); + + double initialValueUp = testNetwork.getGenerator("g2").getTargetP(); + Scalable upDownScalable = Scalable.upDown(upScalable, downScalable, initialValueUp - 35, Double.MAX_VALUE); + + ScalingParameters parameters = new ScalingParameters(); + parameters.setScalingConvention(Scalable.ScalingConvention.LOAD); + + double asked = 100; + assertEquals(35, upDownScalable.scale(testNetwork, asked, parameters)); + } }