Skip to content

Commit

Permalink
Review conditions of RegulatingControl creation at CGMES export (#3279)
Browse files Browse the repository at this point in the history
* Update conditions of generator's voltage control capability
* Create a constant for the CGMES.RegulatingControl property

Signed-off-by: Romain Courtier <[email protected]>
  • Loading branch information
rcourtier authored Jan 22, 2025
1 parent 2e3edfa commit 4887064
Show file tree
Hide file tree
Showing 11 changed files with 59 additions and 44 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -1107,4 +1107,5 @@ public Config setCreateFictitiousVoltageLevelsForEveryNode(boolean b) {
public static final String PROPERTY_CGMES_SYNCHRONOUS_MACHINE_TYPE = CGMES_PREFIX_ALIAS_PROPERTIES + "synchronousMachineType";
public static final String PROPERTY_CGMES_SYNCHRONOUS_MACHINE_OPERATING_MODE = CGMES_PREFIX_ALIAS_PROPERTIES + "synchronousMachineOperatingMode";
public static final String PROPERTY_OPERATIONAL_LIMIT_SET_IDENTIFIERS = CGMES_PREFIX_ALIAS_PROPERTIES + CgmesNames.OPERATIONAL_LIMIT_SET + "_identifiers";
public static final String PROPERTY_REGULATING_CONTROL = CGMES_PREFIX_ALIAS_PROPERTIES + CgmesNames.REGULATING_CONTROL;
}
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ private boolean setRegulatingControlVoltage(String controlId,
.withQPercent(qPercent)
.add();
}
gen.setProperty(Conversion.CGMES_PREFIX_ALIAS_PROPERTIES + "RegulatingControl", controlId);
gen.setProperty(Conversion.PROPERTY_REGULATING_CONTROL, controlId);

return true;
}
Expand Down Expand Up @@ -161,7 +161,7 @@ private boolean setRegulatingControlReactivePower(String controlId, RegulatingCo
.add();
}

gen.setProperty(Conversion.CGMES_PREFIX_ALIAS_PROPERTIES + "RegulatingControl", controlId);
gen.setProperty(Conversion.PROPERTY_REGULATING_CONTROL, controlId);

return true;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ private void setRegulatingControl(ShuntCompensator shuntCompensator, String rcId
shuntCompensator.setRegulatingTerminal(RegulatingTerminalMapper
.mapForVoltageControl(rc.cgmesTerminal, context)
.orElse(shuntCompensator.getTerminal()));
shuntCompensator.setProperty(Conversion.CGMES_PREFIX_ALIAS_PROPERTIES + "RegulatingControl", rcId);
shuntCompensator.setProperty(Conversion.PROPERTY_REGULATING_CONTROL, rcId);
}

private static class CgmesRegulatingControlForShuntCompensator {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ private void apply(StaticVarCompensator svc, CgmesRegulatingControlForStaticVarC
private boolean setRegulatingControl(CgmesRegulatingControlForStaticVarCompensator rc, RegulatingControl control, StaticVarCompensator svc) {
// Always save the original RC id,
// even if we can not complete setting all the regulation data
svc.setProperty(Conversion.CGMES_PREFIX_ALIAS_PROPERTIES + "RegulatingControl", rc.regulatingControlId);
svc.setProperty(Conversion.PROPERTY_REGULATING_CONTROL, rc.regulatingControlId);

// Take default terminal if it has not been defined in CGMES files (it is never null)
Terminal regulatingTerminal = RegulatingTerminalMapper
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import com.powsybl.commons.report.ReportNode;
import com.powsybl.iidm.network.*;
import com.powsybl.iidm.network.Identifiable;
import com.powsybl.iidm.network.extensions.RemoteReactivePowerControl;
import org.apache.commons.lang3.tuple.Pair;

import java.net.URLEncoder;
Expand All @@ -39,7 +40,6 @@
*/
public class CgmesExportContext {

private static final String REGULATING_CONTROL = "RegulatingControl";
private static final String GENERATING_UNIT = "GeneratingUnit";

private static final String DCNODE = "DCNode";
Expand Down Expand Up @@ -383,26 +383,28 @@ private void addIidmMappingsGenerators(Network network) {
generator.setProperty(Conversion.CGMES_PREFIX_ALIAS_PROPERTIES + GENERATING_UNIT, generatingUnit);
}
}
String regulatingControlId = generator.getProperty(Conversion.CGMES_PREFIX_ALIAS_PROPERTIES + REGULATING_CONTROL);
if (regulatingControlId == null && hasVoltageControlCapability(generator)) {
String regulatingControlId = generator.getProperty(Conversion.PROPERTY_REGULATING_CONTROL);
if (regulatingControlId == null && hasRegulatingControlCapability(generator)) {
regulatingControlId = namingStrategy.getCgmesId(ref(generator), Part.REGULATING_CONTROL);
generator.setProperty(Conversion.CGMES_PREFIX_ALIAS_PROPERTIES + REGULATING_CONTROL, regulatingControlId);
generator.setProperty(Conversion.PROPERTY_REGULATING_CONTROL, regulatingControlId);
}
}
}

private static boolean hasVoltageControlCapability(Generator generator) {
if (generator.getReactiveLimits() == null) {
return false;
}
private static boolean hasRegulatingControlCapability(Generator generator) {
return generator.getExtension(RemoteReactivePowerControl.class) != null
|| !Double.isNaN(generator.getTargetV()) && hasReactiveCapability(generator);
}

private static boolean hasReactiveCapability(Generator generator) {
ReactiveLimits reactiveLimits = generator.getReactiveLimits();
if (reactiveLimits.getKind() == ReactiveLimitsKind.CURVE) {
if (reactiveLimits == null) {
return false;
} else if (reactiveLimits.getKind() == ReactiveLimitsKind.CURVE) {
return hasReactiveCapability((ReactiveCapabilityCurve) reactiveLimits);
} else if (reactiveLimits.getKind() == ReactiveLimitsKind.MIN_MAX) {
return hasReactiveCapability((MinMaxReactiveLimits) reactiveLimits);
}

return false;
}

Expand Down Expand Up @@ -435,25 +437,25 @@ private void addIidmMappingsShuntCompensators(Network network) {
if ("true".equals(shuntCompensator.getProperty(Conversion.PROPERTY_IS_EQUIVALENT_SHUNT))) {
continue;
}
String regulatingControlId = shuntCompensator.getProperty(Conversion.CGMES_PREFIX_ALIAS_PROPERTIES + REGULATING_CONTROL);
String regulatingControlId = shuntCompensator.getProperty(Conversion.PROPERTY_REGULATING_CONTROL);
if (regulatingControlId == null && (CgmesExportUtil.isValidVoltageSetpoint(shuntCompensator.getTargetV())
|| !Objects.equals(shuntCompensator, shuntCompensator.getRegulatingTerminal().getConnectable()))) {
regulatingControlId = namingStrategy.getCgmesId(ref(shuntCompensator), Part.REGULATING_CONTROL);
shuntCompensator.setProperty(Conversion.CGMES_PREFIX_ALIAS_PROPERTIES + REGULATING_CONTROL, regulatingControlId);
shuntCompensator.setProperty(Conversion.PROPERTY_REGULATING_CONTROL, regulatingControlId);
}
}
}

private void addIidmMappingsStaticVarCompensators(Network network) {
for (StaticVarCompensator svc : network.getStaticVarCompensators()) {
String regulatingControlId = svc.getProperty(Conversion.CGMES_PREFIX_ALIAS_PROPERTIES + REGULATING_CONTROL);
String regulatingControlId = svc.getProperty(Conversion.PROPERTY_REGULATING_CONTROL);
boolean validVoltageSetpoint = CgmesExportUtil.isValidVoltageSetpoint(svc.getVoltageSetpoint());
boolean validReactiveSetpoint = CgmesExportUtil.isValidReactivePowerSetpoint(svc.getReactivePowerSetpoint());
if (regulatingControlId == null && (validReactiveSetpoint
|| validVoltageSetpoint
|| !Objects.equals(svc, svc.getRegulatingTerminal().getConnectable()))) {
regulatingControlId = namingStrategy.getCgmesId(ref(svc), Part.REGULATING_CONTROL);
svc.setProperty(Conversion.CGMES_PREFIX_ALIAS_PROPERTIES + REGULATING_CONTROL, regulatingControlId);
svc.setProperty(Conversion.PROPERTY_REGULATING_CONTROL, regulatingControlId);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@
public final class SteadyStateHypothesisExport {

private static final Logger LOG = LoggerFactory.getLogger(SteadyStateHypothesisExport.class);
private static final String REGULATING_CONTROL_PROPERTY = Conversion.CGMES_PREFIX_ALIAS_PROPERTIES + "RegulatingControl";
private static final String GENERATING_UNIT_PROPERTY = Conversion.CGMES_PREFIX_ALIAS_PROPERTIES + "GeneratingUnit";
private static final String ROTATING_MACHINE_P = "RotatingMachine.p";
private static final String ROTATING_MACHINE_Q = "RotatingMachine.q";
Expand Down Expand Up @@ -270,10 +269,10 @@ private static void writeShuntCompensators(Network network, String cimNamespace,
}

private static void addRegulatingControlView(ShuntCompensator s, Map<String, List<RegulatingControlView>> regulatingControlViews, CgmesExportContext context) {
if (s.hasProperty(REGULATING_CONTROL_PROPERTY)) {
if (s.hasProperty(Conversion.PROPERTY_REGULATING_CONTROL)) {
// PowSyBl has considered the control as discrete, with a certain targetDeadband
// The target value is stored in kV by PowSyBl, so unit multiplier is "k"
String rcid = context.getNamingStrategy().getCgmesIdFromProperty(s, REGULATING_CONTROL_PROPERTY);
String rcid = context.getNamingStrategy().getCgmesIdFromProperty(s, Conversion.PROPERTY_REGULATING_CONTROL);
RegulatingControlView rcv = new RegulatingControlView(rcid, RegulatingControlType.REGULATING_CONTROL, true,
s.isVoltageRegulatorOn(), s.getTargetDeadband(), s.getTargetV(), "k");
regulatingControlViews.computeIfAbsent(rcid, k -> new ArrayList<>()).add(rcv);
Expand Down Expand Up @@ -372,10 +371,10 @@ private static String obtainOperatingMode(double targetP) {
}

private static void addRegulatingControlView(Generator g, Map<String, List<RegulatingControlView>> regulatingControlViews, CgmesExportContext context) {
if (g.hasProperty(REGULATING_CONTROL_PROPERTY)) {
if (g.hasProperty(Conversion.PROPERTY_REGULATING_CONTROL)) {
// PowSyBl has considered the control as continuous and with targetDeadband of size 0
// The target value is stored in kV by PowSyBl, so unit multiplier is "k"
String rcid = context.getNamingStrategy().getCgmesIdFromProperty(g, REGULATING_CONTROL_PROPERTY);
String rcid = context.getNamingStrategy().getCgmesIdFromProperty(g, Conversion.PROPERTY_REGULATING_CONTROL);

double targetDeadband = 0;
double target;
Expand Down Expand Up @@ -422,8 +421,8 @@ private static void writeStaticVarCompensators(Network network, String cimNamesp
writer.writeEndElement();
writer.writeEndElement();

if (svc.hasProperty(REGULATING_CONTROL_PROPERTY)) {
String rcid = context.getNamingStrategy().getCgmesIdFromProperty(svc, REGULATING_CONTROL_PROPERTY);
if (svc.hasProperty(Conversion.PROPERTY_REGULATING_CONTROL)) {
String rcid = context.getNamingStrategy().getCgmesIdFromProperty(svc, Conversion.PROPERTY_REGULATING_CONTROL);
double targetDeadband = 0;
// Regulating control could be reactive power or voltage
double targetValue;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ public final class RegulatingControlEq {
public static final String REGULATING_CONTROL_CURRENT_FLOW = "RegulatingControlModeKind.currentFlow";

public static String writeRegulatingControlEq(Connectable<?> c, String terminalId, Set<String> regulatingControlsWritten, String mode, String cimNamespace, XMLStreamWriter writer, CgmesExportContext context) throws XMLStreamException {
String regulatingControlId = context.getNamingStrategy().getCgmesIdFromProperty(c, Conversion.CGMES_PREFIX_ALIAS_PROPERTIES + "RegulatingControl");
String regulatingControlId = context.getNamingStrategy().getCgmesIdFromProperty(c, Conversion.PROPERTY_REGULATING_CONTROL);
if (regulatingControlId != null && !regulatingControlsWritten.contains(regulatingControlId)) {
String regulatingControlName = "RC_" + c.getNameOrId();
CgmesExportUtil.writeStartIdName("RegulatingControl", regulatingControlId, regulatingControlName, cimNamespace, writer, context);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -616,8 +616,8 @@ void testCanGeneratorControl() throws IOException {
Generator generatorNoRcc = network.getGenerator("550ebe0d-f2b2-48c1-991f-cebea43a21aa");
Generator generatorRcc = network.getGenerator("3a3b27be-b18b-4385-b557-6735d733baf0");

generatorNoRcc.removeProperty(Conversion.CGMES_PREFIX_ALIAS_PROPERTIES + "RegulatingControl");
generatorRcc.removeProperty(Conversion.CGMES_PREFIX_ALIAS_PROPERTIES + "RegulatingControl");
generatorNoRcc.removeProperty(Conversion.PROPERTY_REGULATING_CONTROL);
generatorRcc.removeProperty(Conversion.PROPERTY_REGULATING_CONTROL);

String exportFolder = "/test-generator-control";
String baseName = "testGeneratorControl";
Expand All @@ -626,35 +626,40 @@ void testCanGeneratorControl() throws IOException {
Path tmpDir = Files.createDirectory(fs.getPath(exportFolder));
Properties exportParams = new Properties();
exportParams.put(CgmesExport.PROFILES, "EQ");
// network.write("CGMES", null, tmpDir.resolve(baseName));
new CgmesExport().export(network, exportParams, new DirectoryDataSource(tmpDir, baseName));
String eq = Files.readString(tmpDir.resolve(baseName + "_EQ.xml"));

// Check that RC are exported properly
// Check that RegulatingControl is properly exported
assertTrue(eq.contains("3a3b27be-b18b-4385-b557-6735d733baf0_RC"));
assertTrue(eq.contains("550ebe0d-f2b2-48c1-991f-cebea43a21aa_RC"));
generatorRcc.removeProperty(Conversion.CGMES_PREFIX_ALIAS_PROPERTIES + "RegulatingControl");
generatorNoRcc.removeProperty(Conversion.CGMES_PREFIX_ALIAS_PROPERTIES + "RegulatingControl");
generatorRcc.removeProperty(Conversion.PROPERTY_REGULATING_CONTROL);
generatorNoRcc.removeProperty(Conversion.PROPERTY_REGULATING_CONTROL);

// RC is exported without targetV, if reactive capability is, or it was imported
double rccTargetV = generatorRcc.getTargetV();
// RegulatingControl is exported when targetV is not NaN, even if voltage regulation is disabled
generatorRcc.setVoltageRegulatorOn(false);
generatorRcc.setTargetV(Double.NaN);

double noRccTargetV = generatorNoRcc.getTargetV();
generatorNoRcc.setVoltageRegulatorOn(false);
generatorNoRcc.setTargetV(Double.NaN);
new CgmesExport().export(network, exportParams, new DirectoryDataSource(tmpDir, baseName));
eq = Files.readString(tmpDir.resolve(baseName + "_EQ.xml"));
assertTrue(eq.contains("3a3b27be-b18b-4385-b557-6735d733baf0_RC"));
assertTrue(eq.contains("550ebe0d-f2b2-48c1-991f-cebea43a21aa_RC"));
generatorRcc.removeProperty(Conversion.PROPERTY_REGULATING_CONTROL);
generatorNoRcc.removeProperty(Conversion.PROPERTY_REGULATING_CONTROL);

// RegulatingControl isn't exported when targetV is NaN
double rccTargetV = generatorRcc.getTargetV();
generatorRcc.setTargetV(Double.NaN);
double noRccTargetV = generatorNoRcc.getTargetV();
generatorNoRcc.setTargetV(Double.NaN);
new CgmesExport().export(network, exportParams, new DirectoryDataSource(tmpDir, baseName));
eq = Files.readString(tmpDir.resolve(baseName + "_EQ.xml"));
assertFalse(eq.contains("3a3b27be-b18b-4385-b557-6735d733baf0_RC"));
assertFalse(eq.contains("550ebe0d-f2b2-48c1-991f-cebea43a21aa_RC"));
generatorRcc.setTargetV(rccTargetV);
generatorRcc.setVoltageRegulatorOn(true);
generatorNoRcc.setTargetV(noRccTargetV);
generatorNoRcc.setVoltageRegulatorOn(true);

// RC shouldn't be exported when Qmin and Qmax are the same, but it exists when it was already imported
// RegulatingControl isn't exported when Qmin and Qmax are the same
ReactiveCapabilityCurveAdder rccAdder = generatorRcc.newReactiveCapabilityCurve();
ReactiveCapabilityCurve rcc = (ReactiveCapabilityCurve) generatorRcc.getReactiveLimits();
rcc.getPoints().forEach(point -> rccAdder.beginPoint().setP(point.getP()).setMaxQ(point.getMaxQ()).setMinQ(point.getMaxQ()).endPoint());
Expand All @@ -664,11 +669,19 @@ void testCanGeneratorControl() throws IOException {
mmrlAdder.setMinQ(mmrl.getMinQ());
mmrlAdder.setMaxQ(mmrl.getMinQ());
mmrlAdder.add();
new CgmesExport().export(network, exportParams, new DirectoryDataSource(tmpDir, baseName));
eq = Files.readString(tmpDir.resolve(baseName + "_EQ.xml"));
assertFalse(eq.contains("3a3b27be-b18b-4385-b557-6735d733baf0_RC"));
assertFalse(eq.contains("550ebe0d-f2b2-48c1-991f-cebea43a21aa_RC"));

// RegulatingControl is however exported when the corresponding CGMES property is present
generatorRcc.setProperty(Conversion.PROPERTY_REGULATING_CONTROL, "3a3b27be-b18b-4385-b557-6735d733baf0_RC");
generatorNoRcc.setProperty(Conversion.PROPERTY_REGULATING_CONTROL, "550ebe0d-f2b2-48c1-991f-cebea43a21aa_RC");
new CgmesExport().export(network, exportParams, new DirectoryDataSource(tmpDir, baseName));
eq = Files.readString(tmpDir.resolve(baseName + "_EQ.xml"));
assertTrue(eq.contains("3a3b27be-b18b-4385-b557-6735d733baf0_RC"));
assertTrue(eq.contains("550ebe0d-f2b2-48c1-991f-cebea43a21aa_RC"));

}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1373,12 +1373,12 @@ void generatorRegulatingControlEQTest() throws IOException {
// Generator without control
network = EurostagTutorialExample1Factory.createWithoutControl();
eq = getEQ(network, baseName, tmpDir, exportParams);
testRcEqRcWithAttribute(eq, "_GEN_RC", "_GEN_SM_T_1", "voltage");
testRcEqRCWithoutAttribute(eq, "_GEN_RC", "", "dummy");

// Generator with remote terminal without control
network = EurostagTutorialExample1Factory.createRemoteWithoutControl();
eq = getEQ(network, baseName, tmpDir, exportParams);
testRcEqRcWithAttribute(eq, "_GEN_RC", "_NHV2_NLOAD_PT_T_1", "voltage");
testRcEqRCWithoutAttribute(eq, "_GEN_RC", "", "dummy");

// Generator without control capability
network = EurostagTutorialExample1Factory.create();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -741,12 +741,12 @@ void generatorRegulatingControlSSHTest() throws IOException {
// Generator without control
network = EurostagTutorialExample1Factory.createWithoutControl();
ssh = getSSH(network, baseName, tmpDir, exportParams);
testRcEqRcWithAttribute(ssh, "_GEN_RC", "false", "false", "0", "0", "k");
testRcEqRCWithoutAttribute(ssh, "_GEN_RC");

// Generator with remote terminal without control
network = EurostagTutorialExample1Factory.createRemoteWithoutControl();
ssh = getSSH(network, baseName, tmpDir, exportParams);
testRcEqRcWithAttribute(ssh, "_GEN_RC", "false", "false", "0", "0", "k");
testRcEqRCWithoutAttribute(ssh, "_GEN_RC");

// Generator without control capability
network = EurostagTutorialExample1Factory.create();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ void microT4SvcRCDisabled() {
StaticVarCompensator svc = network.getStaticVarCompensator(svcId);
assertNotNull(svc);
assertEquals(OFF, svc.getRegulationMode());
assertEquals(rcId, svc.getProperty(Conversion.CGMES_PREFIX_ALIAS_PROPERTIES + "RegulatingControl"));
assertEquals(rcId, svc.getProperty(Conversion.PROPERTY_REGULATING_CONTROL));
assertEquals(231.123, svc.getVoltageSetpoint(), 0.0);

// Do a full export and check that output files contain the reference to the RC
Expand Down

0 comments on commit 4887064

Please sign in to comment.