-
Notifications
You must be signed in to change notification settings - Fork 43
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Cgmes active reactive regulating control #2995
Changes from all commits
c8d2664
e231350
067a67f
22f0609
3280a7d
2387807
eddf3b5
84ac79b
479d07e
ab40413
3b5cac9
be45894
6a707ad
d8301f0
2a47c9c
be01bfb
3f21ced
553d59f
d1abecd
d67b407
bcee577
39d5d5a
5b6ca00
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -388,7 +388,7 @@ private void addIidmMappingsGenerators(Network network) { | |
} | ||
|
||
private static boolean hasVoltageControlCapability(Generator generator) { | ||
if (Double.isNaN(generator.getTargetV()) || generator.getReactiveLimits() == null) { | ||
if (generator.getReactiveLimits() == null) { | ||
return false; | ||
} | ||
|
||
|
@@ -432,7 +432,8 @@ private void addIidmMappingsShuntCompensators(Network network) { | |
continue; | ||
} | ||
String regulatingControlId = shuntCompensator.getProperty(Conversion.CGMES_PREFIX_ALIAS_PROPERTIES + REGULATING_CONTROL); | ||
if (regulatingControlId == null && (shuntCompensator.isVoltageRegulatorOn() || !Objects.equals(shuntCompensator, shuntCompensator.getRegulatingTerminal().getConnectable()))) { | ||
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); | ||
} | ||
|
@@ -442,7 +443,11 @@ private void addIidmMappingsShuntCompensators(Network network) { | |
private void addIidmMappingsStaticVarCompensators(Network network) { | ||
for (StaticVarCompensator svc : network.getStaticVarCompensators()) { | ||
String regulatingControlId = svc.getProperty(Conversion.CGMES_PREFIX_ALIAS_PROPERTIES + REGULATING_CONTROL); | ||
if (regulatingControlId == null && (StaticVarCompensator.RegulationMode.VOLTAGE.equals(svc.getRegulationMode()) || !Objects.equals(svc, svc.getRegulatingTerminal().getConnectable()))) { | ||
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); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In concordance with the previous comment, we can export it if:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Again, this is related to RC creation from scratch (no CGMES import). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I am in favor of exporting the maximum information if the information is valid. In this case, I prefer to export the voltage control as disabled than do not export anything, but of course both options are valid. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We have a use case where we export only an EQ file, without SSH file. |
||
svc.setProperty(Conversion.CGMES_PREFIX_ALIAS_PROPERTIES + REGULATING_CONTROL, regulatingControlId); | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -20,6 +20,7 @@ | |
import com.powsybl.commons.exceptions.UncheckedXmlStreamException; | ||
import com.powsybl.iidm.network.*; | ||
import com.powsybl.iidm.network.Identifiable; | ||
import com.powsybl.iidm.network.extensions.RemoteReactivePowerControl; | ||
import com.powsybl.iidm.network.extensions.VoltagePerReactivePowerControl; | ||
|
||
import org.apache.commons.lang3.tuple.Pair; | ||
|
@@ -45,9 +46,6 @@ | |
public final class EquipmentExport { | ||
|
||
private static final String AC_DC_CONVERTER_DC_TERMINAL = "ACDCConverterDCTerminal"; | ||
private static final String PHASE_TAP_CHANGER_REGULATION_MODE_ACTIVE_POWER = "activePower"; | ||
private static final String PHASE_TAP_CHANGER_REGULATION_MODE_CURRENT_FLOW = "currentFlow"; | ||
private static final String RATIO_TAP_CHANGER_REGULATION_MODE_VOLTAGE = "voltage"; | ||
private static final String TERMINAL_BOUNDARY = "Terminal_Boundary"; | ||
private static final Logger LOG = LoggerFactory.getLogger(EquipmentExport.class); | ||
|
||
|
@@ -382,7 +380,9 @@ private static void writeGenerators(Network network, Map<Terminal, String> mapTe | |
Set<String> generatingUnitsWritten = new HashSet<>(); | ||
for (Generator generator : network.getGenerators()) { | ||
String cgmesOriginalClass = generator.getProperty(Conversion.PROPERTY_CGMES_ORIGINAL_CLASS, CgmesNames.SYNCHRONOUS_MACHINE); | ||
|
||
RemoteReactivePowerControl rrpc = generator.getExtension(RemoteReactivePowerControl.class); | ||
String mode = CgmesExportUtil.getGeneratorRegulatingControlMode(generator, rrpc); | ||
Terminal regulatingTerminal = mode.equals(RegulatingControlEq.REGULATING_CONTROL_VOLTAGE) ? generator.getRegulatingTerminal() : rrpc.getRegulatingTerminal(); | ||
switch (cgmesOriginalClass) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. My proposal:
We can define the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The difficulty here is in priorities: Is the RRPC extension priority or voltage control? Example: rrpc exists, voltage control is enabled -> We export voltage control, regardless of rrpc status. Since rrpc has to be created explicitly as an extension, assumption is that it has the priority. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If the reactive power control extension is present and enabled, it does not override the voltage control if enabled. Maybe it is a mistake but it is what is developed inside open loadflow. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I prefer to select the control priority using the electrical information rather than the software information. The final user, in most of the cases, will not know how the model is internally implemented. Then, if both controls have the same valid attributes I prefer to export the voltage control. But as before, both options are valid. |
||
case CgmesNames.EQUIVALENT_INJECTION: | ||
String reactiveCapabilityCurveId = writeReactiveCapabilityCurve(generator, cimNamespace, writer, context); | ||
|
@@ -393,14 +393,14 @@ private static void writeGenerators(Network network, Map<Terminal, String> mapTe | |
cimNamespace, writer, context); | ||
break; | ||
case CgmesNames.EXTERNAL_NETWORK_INJECTION: | ||
String regulatingControlId = RegulatingControlEq.writeKindVoltage(generator, exportedTerminalId(mapTerminal2Id, generator.getRegulatingTerminal()), regulatingControlsWritten, cimNamespace, writer, context); | ||
String regulatingControlId = RegulatingControlEq.writeRegulatingControlEq(generator, exportedTerminalId(mapTerminal2Id, regulatingTerminal), regulatingControlsWritten, mode, cimNamespace, writer, context); | ||
ExternalNetworkInjectionEq.write(context.getNamingStrategy().getCgmesId(generator), generator.getNameOrId(), | ||
context.getNamingStrategy().getCgmesId(generator.getTerminal().getVoltageLevel()), | ||
obtainGeneratorGovernorScd(generator), generator.getMaxP(), obtainMaxQ(generator), generator.getMinP(), obtainMinQ(generator), | ||
regulatingControlId, cimNamespace, writer, context); | ||
break; | ||
case CgmesNames.SYNCHRONOUS_MACHINE: | ||
regulatingControlId = RegulatingControlEq.writeKindVoltage(generator, exportedTerminalId(mapTerminal2Id, generator.getRegulatingTerminal()), regulatingControlsWritten, cimNamespace, writer, context); | ||
regulatingControlId = RegulatingControlEq.writeRegulatingControlEq(generator, exportedTerminalId(mapTerminal2Id, regulatingTerminal), regulatingControlsWritten, mode, cimNamespace, writer, context); | ||
writeSynchronousMachine(generator, cimNamespace, writeInitialP, | ||
generator.getMinP(), generator.getMaxP(), generator.getTargetP(), generator.getRatedS(), generator.getEnergySource(), | ||
regulatingControlId, writer, context, generatingUnitsWritten); | ||
|
@@ -574,13 +574,15 @@ private static void writeShuntCompensators(Network network, Map<Terminal, String | |
context.getNamingStrategy().getCgmesId(s.getTerminal().getVoltageLevel()), | ||
cimNamespace, writer, context); | ||
} else { | ||
// Shunt can only regulate voltage | ||
String mode = RegulatingControlEq.REGULATING_CONTROL_VOLTAGE; | ||
double bPerSection = 0.0; | ||
double gPerSection = Double.NaN; | ||
if (s.getModelType().equals(ShuntCompensatorModelType.LINEAR)) { | ||
bPerSection = ((ShuntCompensatorLinearModel) s.getModel()).getBPerSection(); | ||
gPerSection = ((ShuntCompensatorLinearModel) s.getModel()).getGPerSection(); | ||
} | ||
String regulatingControlId = RegulatingControlEq.writeKindVoltage(s, exportedTerminalId(mapTerminal2Id, s.getRegulatingTerminal()), regulatingControlsWritten, cimNamespace, writer, context); | ||
String regulatingControlId = RegulatingControlEq.writeRegulatingControlEq(s, exportedTerminalId(mapTerminal2Id, s.getRegulatingTerminal()), regulatingControlsWritten, mode, cimNamespace, writer, context); | ||
ShuntCompensatorEq.write(context.getNamingStrategy().getCgmesId(s), s.getNameOrId(), s.getSectionCount(), s.getMaximumSectionCount(), s.getTerminal().getVoltageLevel().getNominalV(), s.getModelType(), bPerSection, gPerSection, regulatingControlId, | ||
context.getNamingStrategy().getCgmesId(s.getTerminal().getVoltageLevel()), cimNamespace, writer, context); | ||
if (s.getModelType().equals(ShuntCompensatorModelType.NON_LINEAR)) { | ||
|
@@ -600,7 +602,8 @@ private static void writeShuntCompensators(Network network, Map<Terminal, String | |
private static void writeStaticVarCompensators(Network network, Map<Terminal, String> mapTerminal2Id, Set<String> regulatingControlsWritten, String cimNamespace, | ||
XMLStreamWriter writer, CgmesExportContext context) throws XMLStreamException { | ||
for (StaticVarCompensator svc : network.getStaticVarCompensators()) { | ||
String regulatingControlId = RegulatingControlEq.writeKindVoltage(svc, exportedTerminalId(mapTerminal2Id, svc.getRegulatingTerminal()), regulatingControlsWritten, cimNamespace, writer, context); | ||
String mode = CgmesExportUtil.getSvcMode(svc); | ||
String regulatingControlId = RegulatingControlEq.writeRegulatingControlEq(svc, exportedTerminalId(mapTerminal2Id, svc.getRegulatingTerminal()), regulatingControlsWritten, mode, cimNamespace, writer, context); | ||
double inductiveRating = svc.getBmin() != 0 ? 1 / svc.getBmin() : 0; | ||
double capacitiveRating = svc.getBmax() != 0 ? 1 / svc.getBmax() : 0; | ||
StaticVarCompensatorEq.write(context.getNamingStrategy().getCgmesId(svc), svc.getNameOrId(), context.getNamingStrategy().getCgmesId(svc.getTerminal().getVoltageLevel()), regulatingControlId, inductiveRating, capacitiveRating, svc.getExtension(VoltagePerReactivePowerControl.class), svc.getRegulationMode(), svc.getVoltageSetpoint(), cimNamespace, writer, context); | ||
|
@@ -838,7 +841,7 @@ private static <C extends Connectable<C>> void writePhaseTapChanger(C eq, PhaseT | |
Optional<String> regulatingControlId = getTapChangerControlId(eq, tapChangerId); | ||
String cgmesRegulatingControlId = null; | ||
if (regulatingControlId.isPresent() && CgmesExportUtil.regulatingControlIsDefined(ptc)) { | ||
String mode = getPhaseTapChangerRegulationMode(ptc); | ||
String mode = CgmesExportUtil.getPhaseTapChangerRegulationMode(ptc); | ||
String controlName = twtName + "_PTC_RC"; | ||
String terminalId = CgmesExportUtil.getTerminalId(ptc.getRegulationTerminal(), context); | ||
cgmesRegulatingControlId = context.getNamingStrategy().getCgmesId(regulatingControlId.get()); | ||
|
@@ -879,14 +882,6 @@ private static <C extends Connectable<C>> Optional<String> getTapChangerControlI | |
return Optional.empty(); | ||
} | ||
|
||
private static String getPhaseTapChangerRegulationMode(PhaseTapChanger ptc) { | ||
return switch (ptc.getRegulationMode()) { | ||
case CURRENT_LIMITER -> PHASE_TAP_CHANGER_REGULATION_MODE_CURRENT_FLOW; | ||
case ACTIVE_POWER_CONTROL -> PHASE_TAP_CHANGER_REGULATION_MODE_ACTIVE_POWER; | ||
default -> throw new PowsyblException("Unexpected regulation mode: " + ptc.getRegulationMode()); | ||
}; | ||
} | ||
|
||
private static int getPhaseTapChangerNeutralStep(PhaseTapChanger ptc) { | ||
int neutralStep = ptc.getLowTapPosition(); | ||
double minAlpha = Math.abs(ptc.getStep(neutralStep).getAlpha()); | ||
|
@@ -922,8 +917,11 @@ private static <C extends Connectable<C>> void writeRatioTapChanger(C eq, RatioT | |
String terminalId = CgmesExportUtil.getTerminalId(rtc.getRegulationTerminal(), context); | ||
cgmesRegulatingControlId = context.getNamingStrategy().getCgmesId(regulatingControlId.get()); | ||
if (!regulatingControlsWritten.contains(cgmesRegulatingControlId)) { | ||
// Regulating control mode is always "voltage" | ||
TapChangerEq.writeControl(cgmesRegulatingControlId, controlName, RATIO_TAP_CHANGER_REGULATION_MODE_VOLTAGE, terminalId, cimNamespace, writer, context); | ||
String tccMode = CgmesExportUtil.getTcMode(rtc); | ||
if (tccMode.equals(RegulatingControlEq.REGULATING_CONTROL_REACTIVE_POWER)) { | ||
controlMode = "reactive"; | ||
} | ||
TapChangerEq.writeControl(cgmesRegulatingControlId, controlName, tccMode, terminalId, cimNamespace, writer, context); | ||
regulatingControlsWritten.add(cgmesRegulatingControlId); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We can use a method to obtain the tccMode and define it in the CgmesExportUtil class and use it during the SSH export to be always coherent with the EQ export. |
||
} | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe create also the control when the regulating control is remote. If cgmes
EQ
file is imported (noSSH
file), then only the regulating terminal is defined, given that the setpoint is in theSSH
file. Better do not lose the regulating control in this case if the iidm model is exported. In iidm regulating terminal null is the same as local control, then only if the regulating control is remote we have to export it.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is RC creation from scratch (
regulatingControlId == null
) so it comes down to deciding if Shunt has voltage regulating capability from IIDM.If targetV is not set, in case of full CGMES export from IIDM, SSH will have incorrect values for the control.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If the
targetV
is not set, the validation level is EQUIPMENT and does not make sense to export the SSH file.I prefer to export properly the EQ file, including the control, in concordance with the validation level.