Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,7 @@ core.iidm.network.tooManyRegulatingControlEnabled = Transformer '${id}': only on
core.iidm.network.undefinedShuntSection = Shunt compensator '${id}': the current section number is undefined
core.iidm.network.validationWarningBothRatioPhase = Transformer '${id}' has both Ratio and Phase Tap Changer.
core.iidm.network.validationWarningReactiveCapabilityCurveDuplicate = Reactive capability curve for ${id} has a duplicate point for active power ${p}
core.iidm.network.validationWarningReactiveCapabilityCurveReversedMinQMaxQ = Reactive capability curve for ${id} : minQ > maxQ values have been swapped.
core.iidm.network.voltageSetpointInvalidVoltageRegulatorOn = '${id}': voltage setpoint value (${voltageSetpoint}) is invalid (voltage regulator is on)
core.iidm.serde.extensionNotFound = Extension ${extensionName} not found.
core.iidm.serde.extensionsNotFound = Not found extensions
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,7 @@ core.iidm.network.tooManyRegulatingControlEnabled = Transformateur '${id}': un s
core.iidm.network.undefinedShuntSection = Moyen de compensation statique '${id}': le num�ro de section courant n'est pas d�fini.
core.iidm.network.validationWarningBothRatioPhase = ${parent} a � la fois un r�gleur et un d�phaseur.
core.iidm.network.validationWarningReactiveCapabilityCurveDuplicate = La coubre de capacit� de r�actif pour ${id} a une une duplication de points pour la puissance active ${p}.
core.iidm.network.validationWarningReactiveCapabilityCurveReversedMinQMaxQ = Courbe de capacit� de r�actif pour ${id} : minQ > maxQ les valeurs ont �t� interchang�es.
core.iidm.network.voltageSetpointInvalidVoltageRegulatorOn = '${id}': la valeur de consigne de tension (${voltageSetpoint}) est invalide (la r�gulation de tension est active).
core.iidm.serde.extensionNotFound = Extension ${extensionName} introuvable.
core.iidm.serde.extensionsNotFound = Extensions introuvables.
Expand Down
7 changes: 5 additions & 2 deletions docs/grid_exchange_formats/iidm/import.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,13 @@ The `iidm.import.xml.throw-exception-if-extension-not-found` property is an opti
The `iidm.import.xml.included.extensions` property is an optional property that defines the list of extensions that will be imported by the XIIDM importer. By default, all extensions will be imported.
When set to an empty string, all extensions will be ignored during the import.

**iidm.import.xml.excluded.extensions**
**iidm.import.xml.excluded.extensions**
The `iidm.import.xml.excluded.extensions` property is an optional property that defines the list of extensions that will not be imported by the XIIDM importer.
When both `iidm.import.xml.included.extensions` and `iidm.import.xml.excluded.extensions` are defined, a configuration exception is thrown.
By default, no extension is excluded from the import.
By default, no extension is excluded from the import.

**iidm.import.xml.check-reverted-minqmaxq**
The `iidm.import.xml.check-reverted-minqmaxq` property is an optional property that enables a check in the XIIDM importer to detect reversed Qmin > QMax values in ReactiveCapabilityCurves and put them in the right order.

### Deprecated properties

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,14 @@ public static void parentHasDuplicatePointForActivePower(ReportNode reportNode,
.add();
}

public static void reactiveCapabilityCurveReversedMinQMaxQ(ReportNode reportNode, String id) {
reportNode.newReportNode()
.withMessageTemplate("core.iidm.network.validationWarningReactiveCapabilityCurveReversedMinQMaxQ")
.withTypedValue("id", id, TypedValue.ID)
.withSeverity(TypedValue.WARN_SEVERITY)
.add();
}

public static ReportNode runIidmNetworkValidationCHecks(ReportNode reportNode, String networkId) {
return Objects.requireNonNull(reportNode).newReportNode()
.withMessageTemplate("core.iidm.network.IIDMValidation")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,11 +80,15 @@ public ReactiveCapabilityCurveAdder endPoint() {
owner.getMessageHeader().id(), p);
}
}
// TODO: to be activated in IIDM v1.1
// if (maxQ < minQ) {
// throw new ValidationException(owner,
// "maximum reactive power is expected to be greater than or equal to minimum reactive power");
// }
if (Boolean.parseBoolean(owner.getNetwork().getProperty("iidm.import.xml.check-minqmaxq-inversion")) && maxQ < minQ) {
double temp = minQ;
minQ = maxQ;
maxQ = temp;
LOGGER.warn("Maximum reactive power is expected to be greater than or equal to minimum reactive power. minQ and maxQ values have been swapped");
NetworkReports.reactiveCapabilityCurveReversedMinQMaxQ(owner.getNetwork().getReportNodeContext().getReportNode(),
owner.getMessageHeader().id());
}

points.put(p, new PointImpl(p, minQ, maxQ));
return ReactiveCapabilityCurveAdderImpl.this;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,26 @@
*/
package com.powsybl.iidm.network.impl;

import com.powsybl.commons.report.PowsyblCoreReportResourceBundle;
import com.powsybl.commons.report.ReportNode;
import com.powsybl.commons.test.PowsyblTestReportResourceBundle;
import com.powsybl.iidm.network.Generator;
import com.powsybl.iidm.network.Network;
import com.powsybl.iidm.network.ReactiveCapabilityCurve;
import com.powsybl.iidm.network.ReactiveCapabilityCurve.Point;
import com.powsybl.iidm.network.impl.ReactiveCapabilityCurveImpl.PointImpl;
import com.powsybl.iidm.network.test.FictitiousSwitchFactory;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;

import java.io.IOException;
import java.io.StringWriter;
import java.util.TreeMap;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;

/**
*
* @author Geoffroy Jamgotchian {@literal <geoffroy.jamgotchian at rte-france.com>}
Expand All @@ -30,6 +41,13 @@ private ReactiveCapabilityCurveImpl createCurve(Point... points) {
return new ReactiveCapabilityCurveImpl(map, "ReactiveCapabilityCurve owner");
}

private static boolean checkReportNode(String expected, ReportNode reportNode) throws IOException {
StringWriter sw = new StringWriter();
reportNode.print(sw);
assertEquals(expected, sw.toString());
return true;
}

@Test
void testReactiveCapabilityCurve() {
ReactiveCapabilityCurveImpl curve = createCurve(new PointImpl(100.0, 200.0, 300.0),
Expand All @@ -53,6 +71,62 @@ void testReactiveCapabilityCurve() {
assertEquals(400.0, curve.getMaxQ(1000.0), 0.0);
}

@Test
void testReactiveCapabilityCurveRevertedMinQMaxQ() throws IOException {

Network network = FictitiousSwitchFactory.create();

ReportNode reportNode = ReportNode.newRootReportNode()
.withResourceBundles(PowsyblTestReportResourceBundle.TEST_BASE_NAME, PowsyblCoreReportResourceBundle.BASE_NAME)
.withMessageTemplate("key1")
.build();
network.getReportNodeContext().pushReportNode(reportNode);

Generator generator = network.getGenerator("CB");

ReactiveCapabilityCurve reactiveCapabilityCurve1 = generator.newReactiveCapabilityCurve()
.beginPoint()
.setP(1.0)
.setMaxQ(5.0)
.setMinQ(1.0)
.endPoint()
.beginPoint()
.setP(100.0)
.setMaxQ(2.0) // here minQ > maxQ : this is incorrect
.setMinQ(10.0)
.endPoint()
.add();

assertEquals(1.0, reactiveCapabilityCurve1.getMinQ(1.0), 0.0);
assertEquals(5.0, reactiveCapabilityCurve1.getMaxQ(1.0), 0.0);
// reversed minQ and maxQ remain unchanged :
assertEquals(10.0, reactiveCapabilityCurve1.getMinQ(100.0), 0.0);
assertEquals(2.0, reactiveCapabilityCurve1.getMaxQ(100.0), 0.0);

network.setProperty("iidm.import.xml.check-minqmaxq-inversion", "true");

ReactiveCapabilityCurve reactiveCapabilityCurve2 = generator.newReactiveCapabilityCurve()
.beginPoint()
.setP(1.0)
.setMaxQ(5.0)
.setMinQ(1.0)
.endPoint()
.beginPoint()
.setP(100.0)
.setMaxQ(2.0) // here minQ > maxQ : this is incorrect
.setMinQ(10.0)
.endPoint()
.add();

assertEquals(1.0, reactiveCapabilityCurve2.getMinQ(1.0), 0.0);
assertEquals(5.0, reactiveCapabilityCurve2.getMaxQ(1.0), 0.0);
// reversed minQ and maxQ values have been put in the right order and this information is mentioned in the report node :
assertEquals(2.0, reactiveCapabilityCurve2.getMinQ(100.0), 0.0);
assertEquals(10.0, reactiveCapabilityCurve2.getMaxQ(100.0), 0.0);
assertTrue(checkReportNode("+ name1" + System.lineSeparator() +
" Reactive capability curve for CB : minQ > maxQ values have been swapped." + System.lineSeparator(), network.getReportNodeContext().getReportNode()));
}

@ParameterizedTest
@ValueSource(booleans = {true, false})
void testReactiveCapabilityCurveWithReactiveLimitsExtrapolation(boolean extrapolate) {
Expand Down