Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -285,4 +285,12 @@ public static void unexpectedPointToPointDcConfigurationReport(ReportNode report
.withSeverity(TypedValue.WARN_SEVERITY)
.add();
}

public static void phaseTapChangerCurrentLimiterModeNotSupportedReport(ReportNode reportNode, String phaseTapChangerId) {
reportNode.newReportNode()
.withMessageTemplate("core.cgmes.conversion.phaseTapChangerCurrentLimiterModeNotSupported")
.withUntypedValue("phaseTapChangerId", phaseTapChangerId)
.withSeverity(TypedValue.WARN_SEVERITY)
.add();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -559,13 +559,6 @@ public static String getTcMode(RatioTapChanger rtc) {
};
}

public static String getPhaseTapChangerRegulationMode(PhaseTapChanger ptc) {
return switch (ptc.getRegulationMode()) {
case CURRENT_LIMITER -> RegulatingControlEq.REGULATING_CONTROL_CURRENT_FLOW;
case ACTIVE_POWER_CONTROL -> RegulatingControlEq.REGULATING_CONTROL_ACTIVE_POWER;
};
}

public static boolean isMinusOrMaxValue(double value) {
return value == -Double.MAX_VALUE || value == Double.MAX_VALUE;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
package com.powsybl.cgmes.conversion.export;

import com.powsybl.cgmes.conversion.CgmesExport;
import com.powsybl.cgmes.conversion.CgmesReports;
import com.powsybl.cgmes.conversion.Conversion;
import com.powsybl.cgmes.conversion.naming.NamingStrategy;
import com.powsybl.cgmes.conversion.export.elements.*;
Expand Down Expand Up @@ -660,7 +661,7 @@
// In the rest of situations, we keep the same id under alias for tc1.
adjustTapChangerAliases2wt(twt, twt.getPhaseTapChanger(), CgmesNames.PHASE_TAP_CHANGER);
adjustTapChangerAliases2wt(twt, twt.getRatioTapChanger(), CgmesNames.RATIO_TAP_CHANGER);
writePhaseTapChanger(twt, twt.getPhaseTapChanger(), twt.getNameOrId(), endNumber, end1Id, twt.getRatedU1(), regulatingControlsWritten, cimNamespace, writer, context);
writePhaseTapChanger(twt, twt.getPhaseTapChanger(), twt.getNameOrId(), endNumber, end1Id, twt.getRatedU1(), regulatingControlsWritten, cimNamespace, euNamespace, exportedLimitTypes, writer, context);
writeRatioTapChanger(twt, twt.getRatioTapChanger(), twt.getNameOrId(), endNumber, end1Id, twt.getRatedU1(), regulatingControlsWritten, cimNamespace, writer, context);
writeBranchLimits(twt, exportedTerminalId(mapTerminal2Id, twt.getTerminal1()), exportedTerminalId(mapTerminal2Id, twt.getTerminal2()), cimNamespace, euNamespace, exportedLimitTypes, writer, context);
}
Expand Down Expand Up @@ -832,12 +833,12 @@
double b = leg.getB() / a02;
BaseVoltageMapping.BaseVoltageSource baseVoltage = context.getBaseVoltageByNominalVoltage(leg.getTerminal().getVoltageLevel().getNominalV());
PowerTransformerEq.writeEnd(endId, twtName, twtId, endNumber, r, x, g, b, leg.getRatedS(), leg.getRatedU(), terminalId, baseVoltage.getId(), cimNamespace, writer, context);
writePhaseTapChanger(twt, leg.getPhaseTapChanger(), twtName, legNumber, endId, leg.getRatedU(), regulatingControlsWritten, cimNamespace, writer, context);
writePhaseTapChanger(twt, leg.getPhaseTapChanger(), twtName, legNumber, endId, leg.getRatedU(), regulatingControlsWritten, cimNamespace, euNamespace, exportedLimitTypes, writer, context);
writeRatioTapChanger(twt, leg.getRatioTapChanger(), twtName, legNumber, endId, leg.getRatedU(), regulatingControlsWritten, cimNamespace, writer, context);
writeFlowsLimits(leg, terminalId, cimNamespace, euNamespace, exportedLimitTypes, writer, context);
}

private static <C extends Connectable<C>> void writePhaseTapChanger(C eq, PhaseTapChanger ptc, String twtName, int endNumber, String endId, double neutralU, Set<String> regulatingControlsWritten, String cimNamespace, XMLStreamWriter writer, CgmesExportContext context) throws XMLStreamException {
private static <C extends Connectable<C>> void writePhaseTapChanger(C eq, PhaseTapChanger ptc, String twtName, int endNumber, String endId, double neutralU, Set<String> regulatingControlsWritten, String cimNamespace, String euNamespace, Set<String> exportedLimitTypes, XMLStreamWriter writer, CgmesExportContext context) throws XMLStreamException {

Check warning on line 841 in cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/export/EquipmentExport.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Method has 12 parameters, which is greater than 7 authorized.

See more on https://sonarcloud.io/project/issues?id=com.powsybl%3Apowsybl-core&issues=AZqjm6O9lUOexDP2ZOxf&open=AZqjm6O9lUOexDP2ZOxf&pullRequest=3662

Check failure on line 841 in cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/export/EquipmentExport.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Refactor this method to reduce its Cognitive Complexity from 18 to the 15 allowed.

See more on https://sonarcloud.io/project/issues?id=com.powsybl%3Apowsybl-core&issues=AZqjm6O9lUOexDP2ZOxg&open=AZqjm6O9lUOexDP2ZOxg&pullRequest=3662
if (ptc != null) {
String aliasType = Conversion.CGMES_PREFIX_ALIAS_PROPERTIES + CgmesNames.PHASE_TAP_CHANGER + endNumber;
String tapChangerId = eq.getAliasFromType(aliasType).orElseThrow();
Expand All @@ -848,11 +849,30 @@
Optional<String> regulatingControlId = getTapChangerControlId(eq, tapChangerId);
String cgmesRegulatingControlId = null;
if (regulatingControlId.isPresent() && CgmesExportUtil.regulatingControlIsDefined(ptc)) {
String mode = CgmesExportUtil.getPhaseTapChangerRegulationMode(ptc);
String controlName = twtName + "_PTC_RC";
String terminalId = CgmesExportUtil.getTerminalId(ptc.getRegulationTerminal(), context);
cgmesRegulatingControlId = context.getNamingStrategy().getCgmesId(regulatingControlId.get());
if (!regulatingControlsWritten.contains(cgmesRegulatingControlId)) {
String mode = RegulatingControlEq.REGULATING_CONTROL_ACTIVE_POWER;
String controlName = twtName + "_PTC_RC";
String terminalId = CgmesExportUtil.getTerminalId(ptc.getRegulationTerminal(), context);
if (ptc.getRegulationMode() == PhaseTapChanger.RegulationMode.CURRENT_LIMITER) {
// Log not supported regulation mode
CgmesReports.phaseTapChangerCurrentLimiterModeNotSupportedReport(context.getReportNode(), cgmesTapChangerId);

// Add a CurrentLimit with the PhaseTapChanger current limiter regulation value to the regulated terminal.
String operationalLimitSetId = context.getNamingStrategy().getCgmesId(ref(terminalId), ref(PhaseTapChanger.RegulationMode.CURRENT_LIMITER.name()), OPERATIONAL_LIMIT_SET);
String operationalLimitSetName = twtName + "_PTC_" + PhaseTapChanger.RegulationMode.CURRENT_LIMITER;
OperationalLimitSetEq.write(operationalLimitSetId, operationalLimitSetName, terminalId, cimNamespace, writer, context);

String className = "CurrentLimit";
String operationalLimitId = context.getNamingStrategy().getCgmesId(ref(operationalLimitSetId), ref(className), PATL, OPERATIONAL_LIMIT_VALUE);
String operationalLimitTypeId = context.getNamingStrategy().getCgmesId(PATL, OPERATIONAL_LIMIT_TYPE);
LoadingLimitEq.write(operationalLimitId, className, "PATL", ptc.getRegulationValue(), operationalLimitTypeId, operationalLimitSetId, cimNamespace, writer, context);

if (!exportedLimitTypes.contains(operationalLimitTypeId)) {
OperationalLimitTypeEq.writePatl(operationalLimitTypeId, cimNamespace, euNamespace, writer, context);
exportedLimitTypes.add(operationalLimitTypeId);
}
}
TapChangerEq.writeControl(cgmesRegulatingControlId, controlName, mode, terminalId, cimNamespace, writer, context);
regulatingControlsWritten.add(cgmesRegulatingControlId);
}
Expand Down Expand Up @@ -1225,7 +1245,7 @@
// Write the permanent limit
String className = loadingLimitClassName(limits);
String operationalLimitId = context.getNamingStrategy().getCgmesId(ref(operationalLimitSetId), ref(className), PATL, OPERATIONAL_LIMIT_VALUE);
LoadingLimitEq.write(operationalLimitId, limits, "PATL", limits.getPermanentLimit(), operationalLimitTypeId, operationalLimitSetId, cimNamespace, writer, context);
LoadingLimitEq.write(operationalLimitId, className, "PATL", limits.getPermanentLimit(), operationalLimitTypeId, operationalLimitSetId, cimNamespace, writer, context);

if (!limits.getTemporaryLimits().isEmpty()) {
for (LoadingLimits.TemporaryLimit temporaryLimit : limits.getTemporaryLimits()) {
Expand All @@ -1241,7 +1261,7 @@
// Write the temporary limit
operationalLimitId = context.getNamingStrategy().getCgmesId(ref(operationalLimitSetId), ref(className), TATL, ref(acceptableDuration), OPERATIONAL_LIMIT_VALUE);
String temporaryLimitName = temporaryLimit.getName().isEmpty() ? "TATL " + temporaryLimit.getAcceptableDuration() : temporaryLimit.getName(); // If the temporary limit name is empty, write TATL and the acceptable duration
LoadingLimitEq.write(operationalLimitId, limits, temporaryLimitName, temporaryLimit.getValue(), operationalLimitTypeId, operationalLimitSetId, cimNamespace, writer, context);
LoadingLimitEq.write(operationalLimitId, className, temporaryLimitName, temporaryLimit.getValue(), operationalLimitTypeId, operationalLimitSetId, cimNamespace, writer, context);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -488,27 +488,31 @@ private static void addRegulatingControlView(TapChanger<?, ?, ?, ?> tc, CgmesTap
} else if (tc instanceof PhaseTapChanger phaseTapChanger
&& CgmesExportUtil.regulatingControlIsDefined(phaseTapChanger)) {
boolean valid;
String unitMultiplier = switch (CgmesExportUtil.getPhaseTapChangerRegulationMode(phaseTapChanger)) {
case RegulatingControlEq.REGULATING_CONTROL_CURRENT_FLOW -> {
boolean regulating;
String unitMultiplier = switch (phaseTapChanger.getRegulationMode()) {
case PhaseTapChanger.RegulationMode.CURRENT_LIMITER -> {
// Unit multiplier is none (multiply by 1), regulation value is a current in Amperes
valid = true;
regulating = false;
yield "none";
}
case RegulatingControlEq.REGULATING_CONTROL_ACTIVE_POWER -> {
case PhaseTapChanger.RegulationMode.ACTIVE_POWER_CONTROL -> {
// Unit multiplier is M, regulation value is an active power flow in MW
valid = true;
regulating = phaseTapChanger.isRegulating();
yield "M";
}
default -> {
valid = false;
regulating = false;
yield "none";
}
};
if (valid) {
rcv = new RegulatingControlView(controlId,
RegulatingControlType.TAP_CHANGER_CONTROL,
true,
phaseTapChanger.isRegulating(),
regulating,
phaseTapChanger.getTargetDeadband(),
phaseTapChanger.getRegulationValue(),
unitMultiplier);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,10 @@
*/
public final class LoadingLimitEq {

public static void write(String id, LoadingLimits loadingLimits, String name, double value,
public static void write(String id, String className, String name, double value,
String operationalLimitTypeId, String operationalLimitSetId, String cimNamespace, XMLStreamWriter writer, CgmesExportContext context) throws XMLStreamException {
String cgmesClass = loadingLimitClassName(loadingLimits);
CgmesExportUtil.writeStartIdName(cgmesClass, id, name, cimNamespace, writer, context);
writer.writeStartElement(cimNamespace, cgmesClass + "." + getLimitValueAttributeName(context));
CgmesExportUtil.writeStartIdName(className, id, name, cimNamespace, writer, context);
writer.writeStartElement(cimNamespace, className + "." + getLimitValueAttributeName(context));
writer.writeCharacters(CgmesExportUtil.format(value));
writer.writeEndElement();
CgmesExportUtil.writeReference("OperationalLimit.OperationalLimitSet", operationalLimitSetId, cimNamespace, writer, context);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ public final class RegulatingControlEq {
public static final String REGULATING_CONTROL_VOLTAGE = "RegulatingControlModeKind.voltage";
public static final String REGULATING_CONTROL_REACTIVE_POWER = "RegulatingControlModeKind.reactivePower";
public static final String REGULATING_CONTROL_ACTIVE_POWER = "RegulatingControlModeKind.activePower";
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.PROPERTY_REGULATING_CONTROL);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,18 @@ public static String getElement(String xmlFile, String className, String rdfId)
return getFirstMatch(xmlFile, pattern);
}

public static String getAttribute(String element, String attributeName) {
String regex = "<cim:" + attributeName + ">(.*?)</cim:" + attributeName + ">";
Pattern pattern = Pattern.compile(regex);
return getFirstMatch(element, pattern);
}

public static String getResource(String element, String attributeName) {
String regex = "<cim:" + attributeName + " rdf:resource=\"(?:#_)?(.*?)\"/>";
Pattern pattern = Pattern.compile(regex);
return getFirstMatch(element, pattern);
}

public static long getElementCount(String xmlFile, String className) {
String regex = "(<cim:" + className + " (rdf:ID=\"_|rdf:about=\"#_).*?\")>";
Pattern pattern = Pattern.compile(regex);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import com.powsybl.commons.test.AbstractSerDeTest;
import com.powsybl.commons.test.PowsyblTestReportResourceBundle;
import com.powsybl.iidm.network.*;
import com.powsybl.iidm.network.test.PhaseShifterTestCaseFactory;
import org.junit.jupiter.api.Test;

import java.io.IOException;
Expand Down Expand Up @@ -64,4 +65,37 @@ void invalidLtcFlagTest() throws IOException {
assertTrue(logs.contains("TapChanger PTC has regulation enabled but has no load tap changing capability. Fixed ltcFlag to true."));
}

@Test
void currentLimiterExportTest() throws IOException {
// IIDM network:
// A PhaseShifter with current limiter regulation mode.
// CGMES network:
// A PhaseShifter with active power regulation mode, off regulation and the current limit saved as an operational limit.
Network network = PhaseShifterTestCaseFactory.createRegulatingWithoutMode();

// PhaseShifter PS1 has a PhaseTapChanger in current limiter regulation mode,
// with a current limit value of 200A at PS1 secondary terminal.
PhaseTapChanger ptc = network.getTwoWindingsTransformer("PS1").getPhaseTapChanger();
assertEquals(PhaseTapChanger.RegulationMode.CURRENT_LIMITER, ptc.getRegulationMode());
assertEquals(200, ptc.getRegulationValue());
assertTrue(ptc.isRegulating());
assertEquals("PS1", ptc.getRegulationTerminal().getConnectable().getId());
assertEquals(2, ptc.getRegulationTerminal().getSide().getNum());
assertTrue(network.getTwoWindingsTransformer("PS1").getOperationalLimitsGroups2().isEmpty());

// PhaseTapChanger is in active power regulation mode and the regulation is disabled.
// A CurentLimit with the value 200A has been created.
String eqFile = writeCgmesProfile(network, "EQ", tmpDir);
String tapChangerControl = getElement(eqFile, "TapChangerControl", "PS1_PTC_RC");
assertEquals("http://iec.ch/TC57/2013/CIM-schema-cim16#RegulatingControlModeKind.activePower", getResource(tapChangerControl, "RegulatingControl.mode"));
String limitSet = getElement(eqFile, "OperationalLimitSet", "PS1_PT_T_2_CURRENT_LIMITER_OLS");
assertEquals("PS1_PT_T_2", getResource(limitSet, "OperationalLimitSet.Terminal"));
String currentLimit = getElement(eqFile, "CurrentLimit", "PS1_PT_T_2_CURRENT_LIMITER_OLS_CurrentLimit_PATL_OLV");
assertEquals("200", getAttribute(currentLimit, "CurrentLimit.value"));

String sshFile = writeCgmesProfile(network, "SSH", tmpDir);
tapChangerControl = getElement(sshFile, "TapChangerControl", "PS1_PTC_RC");
assertEquals("false", getAttribute(tapChangerControl, "RegulatingControl.enabled"));
}

}
Loading