diff --git a/ampl-converter/pom.xml b/ampl-converter/pom.xml index d5b560d0821..bc6d7f7c3a5 100644 --- a/ampl-converter/pom.xml +++ b/ampl-converter/pom.xml @@ -89,5 +89,10 @@ ${project.version} test + + ${project.groupId} + powsybl-iidm-extensions + ${project.version} + diff --git a/ampl-converter/src/main/java/com/powsybl/ampl/converter/AmplConstants.java b/ampl-converter/src/main/java/com/powsybl/ampl/converter/AmplConstants.java index 67f1b53c88b..1a8f7646b2a 100644 --- a/ampl-converter/src/main/java/com/powsybl/ampl/converter/AmplConstants.java +++ b/ampl-converter/src/main/java/com/powsybl/ampl/converter/AmplConstants.java @@ -29,6 +29,7 @@ private AmplConstants() { public static final String MINP = "minP (MW)"; public static final String MAXP = "maxP (MW)"; public static final String V_REGUL = "v regul."; + public static final String V_REGUL_BUS = "v regul. bus"; public static final String ACTIVE_POWER = "P (MW)"; public static final String REACTIVE_POWER = "Q (MVar)"; public static final String MIN_Q_MAX_P = "minQmaxP (MVar)"; diff --git a/ampl-converter/src/main/java/com/powsybl/ampl/converter/AmplNetworkWriter.java b/ampl-converter/src/main/java/com/powsybl/ampl/converter/AmplNetworkWriter.java index 8898ca9a21a..2af4ba42d86 100644 --- a/ampl-converter/src/main/java/com/powsybl/ampl/converter/AmplNetworkWriter.java +++ b/ampl-converter/src/main/java/com/powsybl/ampl/converter/AmplNetworkWriter.java @@ -781,7 +781,7 @@ private void writeHeaders() throws IOException { try (Writer writer = new OutputStreamWriter( dataSource.newOutputStream("_headers", "txt", append), StandardCharsets.UTF_8) ) { - columnsExporter.writeHeaderFile(writer); + writer.write("version " + config.getVersion().getExporterId() + "\n"); } } } diff --git a/ampl-converter/src/main/java/com/powsybl/ampl/converter/util/NetworkUtil.java b/ampl-converter/src/main/java/com/powsybl/ampl/converter/util/NetworkUtil.java new file mode 100644 index 00000000000..d1081fbed80 --- /dev/null +++ b/ampl-converter/src/main/java/com/powsybl/ampl/converter/util/NetworkUtil.java @@ -0,0 +1,32 @@ +/** + * Copyright (c) 2023, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * SPDX-License-Identifier: MPL-2.0 + */ +package com.powsybl.ampl.converter.util; + +import com.powsybl.iidm.network.Bus; +import com.powsybl.iidm.network.Terminal; +import com.powsybl.iidm.network.VoltageLevel; +import com.powsybl.iidm.network.extensions.SlackTerminal; + +/** + * @author Nicolas PIERRE {@literal } + */ +public final class NetworkUtil { + + public static boolean isSlackBus(Bus bus) { + VoltageLevel vl = bus.getVoltageLevel(); + SlackTerminal slackTerminal = vl.getExtension(SlackTerminal.class); + if (slackTerminal != null) { + Terminal terminal = slackTerminal.getTerminal(); + return terminal.getBusView().getBus() == bus; + } + return false; + } + + private NetworkUtil() { + } +} diff --git a/ampl-converter/src/main/java/com/powsybl/ampl/converter/version/AmplColumnsExporter.java b/ampl-converter/src/main/java/com/powsybl/ampl/converter/version/AmplColumnsExporter.java index 1bf6785a3a5..5c307fb4395 100644 --- a/ampl-converter/src/main/java/com/powsybl/ampl/converter/version/AmplColumnsExporter.java +++ b/ampl-converter/src/main/java/com/powsybl/ampl/converter/version/AmplColumnsExporter.java @@ -12,7 +12,6 @@ import com.powsybl.iidm.network.*; import java.io.IOException; -import java.io.Writer; import java.util.List; /** @@ -20,14 +19,6 @@ */ public interface AmplColumnsExporter { - String VERSION_HEADER_NAME = "version"; - - String getExporterId(); - - default void writeHeaderFile(Writer headerFileWriter) throws IOException { - headerFileWriter.write(VERSION_HEADER_NAME + " " + getExporterId() + "\n"); - } - List getRtcColumns(); List getPtcColumns(); diff --git a/ampl-converter/src/main/java/com/powsybl/ampl/converter/version/AmplExportVersion.java b/ampl-converter/src/main/java/com/powsybl/ampl/converter/version/AmplExportVersion.java index 1b218d252f8..17b385463eb 100644 --- a/ampl-converter/src/main/java/com/powsybl/ampl/converter/version/AmplExportVersion.java +++ b/ampl-converter/src/main/java/com/powsybl/ampl/converter/version/AmplExportVersion.java @@ -24,7 +24,8 @@ */ public enum AmplExportVersion { - V1_0("1.0", BasicAmplExporter.getFactory()); + V1_0("1.0", BasicAmplExporter::new), + V1_1("1.1", ExtendedAmplExporter::new); public interface Factory { AmplColumnsExporter create(AmplExportConfig config, Network network, StringToIntMapper mapper, @@ -63,6 +64,6 @@ public static AmplExportVersion fromExporterId(String exporterId) { } public static AmplExportVersion defaultVersion() { - return V1_0; + return V1_1; } } diff --git a/ampl-converter/src/main/java/com/powsybl/ampl/converter/version/BasicAmplExporter.java b/ampl-converter/src/main/java/com/powsybl/ampl/converter/version/BasicAmplExporter.java index b672e682c9a..06267f37b52 100644 --- a/ampl-converter/src/main/java/com/powsybl/ampl/converter/version/BasicAmplExporter.java +++ b/ampl-converter/src/main/java/com/powsybl/ampl/converter/version/BasicAmplExporter.java @@ -13,6 +13,7 @@ import com.powsybl.ampl.converter.AmplUtil; import com.powsybl.commons.io.table.Column; import com.powsybl.commons.io.table.TableFormatter; +import com.powsybl.commons.io.table.TableFormatterHelper; import com.powsybl.commons.util.StringToIntMapper; import com.powsybl.iidm.network.*; import com.powsybl.iidm.network.util.ConnectedComponents; @@ -41,10 +42,6 @@ public class BasicAmplExporter implements AmplColumnsExporter { private final int actionNum; private HashMap hvdcLinesMap; - public static AmplExportVersion.Factory getFactory() { - return BasicAmplExporter::new; - } - public BasicAmplExporter(AmplExportConfig config, Network network, StringToIntMapper mapper, int variantIndex, int faultNum, int actionNum) { @@ -56,11 +53,6 @@ public BasicAmplExporter(AmplExportConfig config, Network network, StringToIntMa this.actionNum = actionNum; } - @Override - public String getExporterId() { - return AmplExportVersion.V1_0.getExporterId(); - } - @Override public List getRtcColumns() { List columns = new ArrayList<>(8); @@ -623,17 +615,28 @@ public void writeBusesColumnsToFormatter(TableFormatter formatter, Bus b) throws double nomV = vl.getNominalV(); double v = b.getV() / nomV; double theta = Math.toRadians(b.getAngle()); - formatter.writeCell(variantIndex) - .writeCell(num) - .writeCell(vlNum) - .writeCell(ccNum) - .writeCell(v) - .writeCell(theta) - .writeCell(b.getP()) - .writeCell(b.getQ()) - .writeCell(faultNum) - .writeCell(actionNum) - .writeCell(id); + TableFormatterHelper formatterHelper = new TableFormatterHelper(formatter); + formatterHelper.addCell(variantIndex) + .addCell(num) + .addCell(vlNum) + .addCell(ccNum) + .addCell(v) + .addCell(theta) + .addCell(b.getP()) + .addCell(b.getQ()) + .addCell(faultNum) + .addCell(actionNum) + .addCell(id); + + // Add cells if necessary + addAdditionalCellsBusesColumns(formatterHelper, b); + + // Write the cells + formatterHelper.write(); + } + + public void addAdditionalCellsBusesColumns(TableFormatterHelper formatterHelper, Bus b) { + // Nothing to do here } @Override @@ -650,17 +653,30 @@ public void writeThreeWindingsTranformersMiddleBusesColumnsToFormatter(TableForm double angle = twt.getProperty("angle") == null ? Double.NaN : Math.toRadians(Double.parseDouble(twt.getProperty("angle"))); - formatter.writeCell(variantIndex) - .writeCell(middleBusNum) - .writeCell(middleVlNum) - .writeCell(middleCcNum) - .writeCell(v) - .writeCell(angle) - .writeCell(0.0) - .writeCell(0.0) - .writeCell(faultNum) - .writeCell(actionNum) - .writeCell(middleBusId); + TableFormatterHelper formatterHelper = new TableFormatterHelper(formatter); + formatterHelper.addCell(variantIndex) + .addCell(middleBusNum) + .addCell(middleVlNum) + .addCell(middleCcNum) + .addCell(v) + .addCell(angle) + .addCell(0.0) + .addCell(0.0) + .addCell(faultNum) + .addCell(actionNum) + .addCell(middleBusId); + + // Add cells if necessary + addAdditionalCellsThreeWindingsTranformersMiddleBusesColumns(formatterHelper, twt, middleCcNum); + + // Write the cells + formatterHelper.write(); + } + + public void addAdditionalCellsThreeWindingsTranformersMiddleBusesColumns(TableFormatterHelper formatterHelper, + ThreeWindingsTransformer twt, + int middleCcNum) { + // Nothing to do here } @Override @@ -678,17 +694,30 @@ public void writeDanglingLineMiddleBusesToFormatter(TableFormatter formatter, Da double nomV = t.getVoltageLevel().getNominalV(); double v = sv.getU() / nomV; double theta = Math.toRadians(sv.getA()); - formatter.writeCell(variantIndex) - .writeCell(middleBusNum) - .writeCell(middleVlNum) - .writeCell(middleCcNum) - .writeCell(v) - .writeCell(theta) - .writeCell(0.0) // 0 MW injected at dangling line internal bus - .writeCell(0.0) // 0 MVar injected at dangling line internal bus - .writeCell(faultNum) - .writeCell(actionNum) - .writeCell(middleBusId); + + TableFormatterHelper formatterHelper = new TableFormatterHelper(formatter); + formatterHelper.addCell(variantIndex) + .addCell(middleBusNum) + .addCell(middleVlNum) + .addCell(middleCcNum) + .addCell(v) + .addCell(theta) + .addCell(0.0) // 0 MW injected at dangling line internal bus + .addCell(0.0) // 0 MVar injected at dangling line internal bus + .addCell(faultNum) + .addCell(actionNum) + .addCell(middleBusId); + + // Add cells if necessary + addAdditionalCellsDanglingLineMiddleBuses(formatterHelper, dl, middleCcNum); + + // Write the cells + formatterHelper.write(); + } + + public void addAdditionalCellsDanglingLineMiddleBuses(TableFormatterHelper formatterHelper, DanglingLine dl, + int middleCcNum) { + // Nothing to do here } @Override @@ -697,17 +726,30 @@ public void writeTieLineMiddleBusesToFormatter(TableFormatter formatter, TieLine String xNodeBusId = AmplUtil.getXnodeBusId(tieLine); int xNodeBusNum = mapper.getInt(AmplSubset.BUS, xNodeBusId); int xNodeVlNum = mapper.getInt(AmplSubset.VOLTAGE_LEVEL, AmplUtil.getXnodeVoltageLevelId(tieLine)); - formatter.writeCell(variantIndex) - .writeCell(xNodeBusNum) - .writeCell(xNodeVlNum) - .writeCell(xNodeCcNum) - .writeCell(Float.NaN) - .writeCell(Double.NaN) - .writeCell(0.0) - .writeCell(0.0) - .writeCell(faultNum) - .writeCell(actionNum) - .writeCell(xNodeBusId); + + TableFormatterHelper formatterHelper = new TableFormatterHelper(formatter); + formatterHelper.addCell(variantIndex) + .addCell(xNodeBusNum) + .addCell(xNodeVlNum) + .addCell(xNodeCcNum) + .addCell(Float.NaN) + .addCell(Double.NaN) + .addCell(0.0) + .addCell(0.0) + .addCell(faultNum) + .addCell(actionNum) + .addCell(xNodeBusId); + + // Add cells if necessary + addAdditionalCellsTieLineMiddleBuses(formatterHelper, tieLine, xNodeCcNum); + + // Write the cells + formatterHelper.write(); + } + + public void addAdditionalCellsTieLineMiddleBuses(TableFormatterHelper formatterHelper, TieLine tieLine, + int xNodeCcNum) { + // Nothing to do here } @Override @@ -1219,29 +1261,40 @@ public void writeGeneratorToFormatter(TableFormatter formatter, Generator gen) t double maxP = gen.getMaxP(); double vb = gen.getRegulatingTerminal().getVoltageLevel().getNominalV(); - formatter.writeCell(variantIndex) - .writeCell(num) - .writeCell(busNum) - .writeCell(conBusNum != -1 ? conBusNum : busNum) - .writeCell(vlNum) - .writeCell(minP) - .writeCell(maxP) - .writeCell(gen.getReactiveLimits().getMinQ(maxP)) - .writeCell(gen.getReactiveLimits().getMinQ(0)) - .writeCell(gen.getReactiveLimits().getMinQ(minP)) - .writeCell(gen.getReactiveLimits().getMaxQ(maxP)) - .writeCell(gen.getReactiveLimits().getMaxQ(0)) - .writeCell(gen.getReactiveLimits().getMaxQ(minP)) - .writeCell(gen.isVoltageRegulatorOn()) - .writeCell(gen.getTargetV() / vb) - .writeCell(gen.getTargetP()) - .writeCell(gen.getTargetQ()) - .writeCell(faultNum) - .writeCell(actionNum) - .writeCell(id) - .writeCell(gen.getNameOrId()) - .writeCell(t.getP()) - .writeCell(t.getQ()); + TableFormatterHelper formatterHelper = new TableFormatterHelper(formatter); + formatterHelper.addCell(variantIndex) + .addCell(num) + .addCell(busNum) + .addCell(conBusNum != -1 ? conBusNum : busNum) + .addCell(vlNum) + .addCell(minP) + .addCell(maxP) + .addCell(gen.getReactiveLimits().getMinQ(maxP)) + .addCell(gen.getReactiveLimits().getMinQ(0)) + .addCell(gen.getReactiveLimits().getMinQ(minP)) + .addCell(gen.getReactiveLimits().getMaxQ(maxP)) + .addCell(gen.getReactiveLimits().getMaxQ(0)) + .addCell(gen.getReactiveLimits().getMaxQ(minP)) + .addCell(gen.isVoltageRegulatorOn()) + .addCell(gen.getTargetV() / vb) + .addCell(gen.getTargetP()) + .addCell(gen.getTargetQ()) + .addCell(faultNum) + .addCell(actionNum) + .addCell(id) + .addCell(gen.getNameOrId()) + .addCell(t.getP()) + .addCell(t.getQ()); + + // Add cells if necessary + addAdditionalCellsGenerator(formatterHelper, gen); + + // Write the cells + formatterHelper.write(); + } + + public void addAdditionalCellsGenerator(TableFormatterHelper formatterHelper, Generator gen) { + // Nothing to do here } @Override @@ -1295,23 +1348,35 @@ public void writeStaticVarCompensatorToFormatter(TableFormatter formatter, double zb = vb * vb / AmplConstants.SB; // Base impedance int vlNum = mapper.getInt(AmplSubset.VOLTAGE_LEVEL, t.getVoltageLevel().getId()); - formatter.writeCell(variantIndex) - .writeCell(num) - .writeCell(busNum) - .writeCell(conBusNum) - .writeCell(vlNum) - .writeCell(svc.getBmin() * zb) - .writeCell(svc.getBmax() * zb) - .writeCell(svc.getRegulationMode().equals(StaticVarCompensator.RegulationMode.VOLTAGE)) - .writeCell(vlSet / vb) - .writeCell(svc.getReactivePowerSetpoint()) - .writeCell(faultNum) - .writeCell(actionNum) - .writeCell(id) - .writeCell(svc.getNameOrId()) - .writeCell(t.getP()) - .writeCell(t.getQ()); + TableFormatterHelper formatterHelper = new TableFormatterHelper(formatter); + formatterHelper.addCell(variantIndex) + .addCell(num) + .addCell(busNum) + .addCell(conBusNum) + .addCell(vlNum) + .addCell(svc.getBmin() * zb) + .addCell(svc.getBmax() * zb) + .addCell(svc.getRegulationMode().equals(StaticVarCompensator.RegulationMode.VOLTAGE)) + .addCell(vlSet / vb) + .addCell(svc.getReactivePowerSetpoint()) + .addCell(faultNum) + .addCell(actionNum) + .addCell(id) + .addCell(svc.getNameOrId()) + .addCell(t.getP()) + .addCell(t.getQ()); + + // Add cells if necessary + addAdditionalCellsStaticVarCompensator(formatterHelper, svc); + + // Write the cells + formatterHelper.write(); + } + + public void addAdditionalCellsStaticVarCompensator(TableFormatterHelper formatterHelper, + StaticVarCompensator svc) { + // Nothing to do here } @Override @@ -1323,18 +1388,31 @@ public void writeDanglingLineVoltageLevelToFormatter(TableFormatter formatter, double nomV = vl.getNominalV(); double minV = vl.getLowVoltageLimit() / nomV; double maxV = vl.getHighVoltageLimit() / nomV; - formatter.writeCell(variantIndex) - .writeCell(num) - .writeCell("") - .writeCell(0) - .writeCell(nomV) - .writeCell(minV) - .writeCell(maxV) - .writeCell(faultNum) - .writeCell(actionNum) - .writeCell(vl.getSubstation().flatMap(Substation::getCountry).map(Enum::toString).orElse("")) - .writeCell(dl.getId() + "_voltageLevel") - .writeCell(""); + + TableFormatterHelper formatterHelper = new TableFormatterHelper(formatter); + formatterHelper.addCell(variantIndex) + .addCell(num) + .addCell("") + .addCell(0) + .addCell(nomV) + .addCell(minV) + .addCell(maxV) + .addCell(faultNum) + .addCell(actionNum) + .addCell(vl.getSubstation().flatMap(Substation::getCountry).map(Enum::toString).orElse("")) + .addCell(dl.getId() + "_voltageLevel") + .addCell(""); + + // Add cells if necessary + addAdditionalCellsDanglingLineVoltageLevel(formatterHelper, dl); + + // Write the cells + formatterHelper.write(); + } + + public void addAdditionalCellsDanglingLineVoltageLevel(TableFormatterHelper formatterHelper, + DanglingLine dl) { + // Nothing to do here } @Override @@ -1342,24 +1420,37 @@ public void writeThreeWindingsTransformerVoltageLevelToFormatter(TableFormatter ThreeWindingsTransformer twt) throws IOException { String vlId = AmplUtil.getThreeWindingsTransformerMiddleVoltageLevelId(twt); int num = mapper.getInt(AmplSubset.VOLTAGE_LEVEL, vlId); - formatter.writeCell(variantIndex) - .writeCell(num) - .writeCell("") - .writeCell(0) - .writeCell(twt.getRatedU0()) - .writeCell(Float.NaN) - .writeCell(Float.NaN) - .writeCell(faultNum) - .writeCell(actionNum) - .writeCell(twt.getLeg1() + + TableFormatterHelper formatterHelper = new TableFormatterHelper(formatter); + formatterHelper.addCell(variantIndex) + .addCell(num) + .addCell("") + .addCell(0) + .addCell(twt.getRatedU0()) + .addCell(Float.NaN) + .addCell(Float.NaN) + .addCell(faultNum) + .addCell(actionNum) + .addCell(twt.getLeg1() .getTerminal() .getVoltageLevel() .getSubstation() .flatMap(Substation::getCountry) .map(Enum::toString) .orElse("")) - .writeCell(vlId) - .writeCell(""); + .addCell(vlId) + .addCell(""); + + // Add cells if necessary + addAdditionalCellsThreeWindingsTransformerVoltageLevel(formatterHelper, twt); + + // Write the cells + formatterHelper.write(); + } + + public void addAdditionalCellsThreeWindingsTransformerVoltageLevel(TableFormatterHelper formatterHelper, + ThreeWindingsTransformer twt) { + // Nothing to do here } private void writeRatioTapChanger(TableFormatter formatter, String id, double zb2, double reactance, @@ -1369,17 +1460,30 @@ private void writeRatioTapChanger(TableFormatter formatter, String id, double zb for (int position = rtc.getLowTapPosition(); position <= rtc.getHighTapPosition(); position++) { RatioTapChangerStep step = rtc.getStep(position); double x = reactance * (1 + step.getX() / 100) / zb2; - formatter.writeCell(variantIndex) - .writeCell(num) - .writeCell(position - rtc.getLowTapPosition() + 1) - .writeCell(step.getRho()) - .writeCell(x) - .writeCell(0.0) - .writeCell(faultNum) - .writeCell(actionNum); + TableFormatterHelper formatterHelper = new TableFormatterHelper(formatter); + formatterHelper.addCell(variantIndex) + .addCell(num) + .addCell(position - rtc.getLowTapPosition() + 1) + .addCell(step.getRho()) + .addCell(x) + .addCell(0.0) + .addCell(faultNum) + .addCell(actionNum); + + // Add cells if necessary + addAdditionalCellsRatioTapChangerStep(formatterHelper, id, zb2, reactance, rtc); + + // Write the cells + formatterHelper.write(); } } + public void addAdditionalCellsRatioTapChangerStep(TableFormatterHelper formatterHelper, + String id, double zb2, double reactance, + RatioTapChanger rtc) { + // Nothing to do here + } + private void writePhaseTapChanger(TableFormatter formatter, String id, double zb2, double reactance, PhaseTapChanger ptc) throws IOException { int num = mapper.getInt(AmplSubset.TAP_CHANGER_TABLE, id); @@ -1387,17 +1491,30 @@ private void writePhaseTapChanger(TableFormatter formatter, String id, double zb for (int position = ptc.getLowTapPosition(); position <= ptc.getHighTapPosition(); position++) { PhaseTapChangerStep step = ptc.getStep(position); double x = reactance * (1 + step.getX() / 100) / zb2; - formatter.writeCell(variantIndex) - .writeCell(num) - .writeCell(position - ptc.getLowTapPosition() + 1) - .writeCell(step.getRho()) - .writeCell(x) - .writeCell(Math.toRadians(step.getAlpha())) - .writeCell(faultNum) - .writeCell(actionNum); + TableFormatterHelper formatterHelper = new TableFormatterHelper(formatter); + formatterHelper.addCell(variantIndex) + .addCell(num) + .addCell(position - ptc.getLowTapPosition() + 1) + .addCell(step.getRho()) + .addCell(x) + .addCell(Math.toRadians(step.getAlpha())) + .addCell(faultNum) + .addCell(actionNum); + + // Add cells if necessary + addAdditionalCellsPhaseTapChangerStep(formatterHelper, id, zb2, reactance, ptc); + + // Write the cells + formatterHelper.write(); } } + public void addAdditionalCellsPhaseTapChangerStep(TableFormatterHelper formatterHelper, + String id, double zb2, double reactance, + PhaseTapChanger ptc) { + // Nothing to do here + } + private void writeTemporaryCurrentLimits(CurrentLimits limits, TableFormatter formatter, String branchId, boolean side1, String sideId) throws IOException { int branchNum = mapper.getInt(AmplSubset.BRANCH, branchId); @@ -1448,4 +1565,28 @@ private static double getPermanentLimit(CurrentLimits limits) { } return Double.NaN; } + + public AmplExportConfig getConfig() { + return config; + } + + public Network getNetwork() { + return network; + } + + public StringToIntMapper getMapper() { + return mapper; + } + + public int getVariantIndex() { + return variantIndex; + } + + public int getFaultNum() { + return faultNum; + } + + public int getActionNum() { + return actionNum; + } } diff --git a/ampl-converter/src/main/java/com/powsybl/ampl/converter/version/ExtendedAmplExporter.java b/ampl-converter/src/main/java/com/powsybl/ampl/converter/version/ExtendedAmplExporter.java new file mode 100644 index 00000000000..52ed1042170 --- /dev/null +++ b/ampl-converter/src/main/java/com/powsybl/ampl/converter/version/ExtendedAmplExporter.java @@ -0,0 +1,219 @@ +/** + * Copyright (c) 2023, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * SPDX-License-Identifier: MPL-2.0 + */ +package com.powsybl.ampl.converter.version; + +import com.powsybl.ampl.converter.AmplConstants; +import com.powsybl.ampl.converter.AmplExportConfig; +import com.powsybl.ampl.converter.AmplSubset; +import com.powsybl.ampl.converter.util.NetworkUtil; +import com.powsybl.commons.io.table.Column; +import com.powsybl.commons.io.table.TableFormatter; +import com.powsybl.commons.io.table.TableFormatterHelper; +import com.powsybl.commons.util.StringToIntMapper; +import com.powsybl.iidm.network.*; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import static com.powsybl.ampl.converter.AmplConstants.*; + +/** + * 1st extension of BasicAmplExporter, associated with AMPL version 1.1 (exporter id). + * The extension adds: + * - A slack bus boolean in the bus table. + * - R, G and B characteristics in tap tables. + * - The regulated bus in generator and static var compensator tables. + * + * @author Nicolas PIERRE {@literal } + * @author Pierre ARVY {@literal } + */ +public class ExtendedAmplExporter extends BasicAmplExporter { + + private static final int SLACK_BUS_COLUMN_INDEX = 8; + private static final int TAP_CHANGER_R_COLUMN_INDEX = 4; + private static final int TAP_CHANGER_G_COLUMN_INDEX = 6; + private static final int TAP_CHANGER_B_COLUMN_INDEX = 7; + private static final int GENERATOR_V_REGUL_BUS_COLUMN_INDEX = 14; + private static final int STATIC_VAR_COMPENSATOR_V_REGUL_BUS_COLUMN_INDEX = 8; + + public ExtendedAmplExporter(AmplExportConfig config, + Network network, + StringToIntMapper mapper, + int variantIndex, int faultNum, int actionNum) { + super(config, network, mapper, variantIndex, faultNum, actionNum); + } + + private record ImpedanceAndAdmittance(double r, double x, double g, double b) { } + + @Override + public List getBusesColumns() { + List busesColumns = new ArrayList<>(super.getBusesColumns()); + // add slack bus column + busesColumns.add(SLACK_BUS_COLUMN_INDEX, new Column("slack bus")); + return busesColumns; + } + + @Override + public List getTapChangerTableColumns() { + List tapChangerTableColumns = new ArrayList<>(super.getTapChangerTableColumns()); + // add r, g and b columns + tapChangerTableColumns.add(TAP_CHANGER_R_COLUMN_INDEX, new Column("r (pu)")); + tapChangerTableColumns.add(TAP_CHANGER_G_COLUMN_INDEX, new Column("g (pu)")); + tapChangerTableColumns.add(TAP_CHANGER_B_COLUMN_INDEX, new Column("b (pu)")); + return tapChangerTableColumns; + } + + @Override + public List getGeneratorsColumns() { + List generatorsColumns = new ArrayList<>(super.getGeneratorsColumns()); + // add column for voltage regulated bus + generatorsColumns.add(GENERATOR_V_REGUL_BUS_COLUMN_INDEX, new Column(V_REGUL_BUS)); + return generatorsColumns; + } + + @Override + public List getStaticVarCompensatorColumns() { + List svcColumns = new ArrayList<>(super.getStaticVarCompensatorColumns()); + // add column for voltage regulated bus + svcColumns.add(STATIC_VAR_COMPENSATOR_V_REGUL_BUS_COLUMN_INDEX, new Column(V_REGUL_BUS)); + return svcColumns; + } + + @Override + public void addAdditionalCellsBusesColumns(TableFormatterHelper formatterHelper, Bus b) { + formatterHelper.addCell(NetworkUtil.isSlackBus(b), SLACK_BUS_COLUMN_INDEX); + } + + @Override + public void addAdditionalCellsThreeWindingsTranformersMiddleBusesColumns(TableFormatterHelper formatterHelper, + ThreeWindingsTransformer twt, + int middleCcNum) { + formatterHelper.addCell(false, SLACK_BUS_COLUMN_INDEX); + } + + @Override + public void addAdditionalCellsDanglingLineMiddleBuses(TableFormatterHelper formatterHelper, DanglingLine dl, + int middleCcNum) { + formatterHelper.addCell(false, SLACK_BUS_COLUMN_INDEX); + } + + @Override + public void addAdditionalCellsTieLineMiddleBuses(TableFormatterHelper formatterHelper, TieLine tieLine, + int xNodeCcNum) { + formatterHelper.addCell(false, SLACK_BUS_COLUMN_INDEX); + } + + @Override + public void writeTwoWindingsTransformerTapChangerTableToFormatter(TableFormatter formatter, + TwoWindingsTransformer twt) throws IOException { + Terminal t2 = twt.getTerminal2(); + double vb2 = t2.getVoltageLevel().getNominalV(); + double zb2 = vb2 * vb2 / AmplConstants.SB; + ImpedanceAndAdmittance transformer = new ImpedanceAndAdmittance(twt.getR(), twt.getX(), + twt.getG(), twt.getB()); + RatioTapChanger rtc = twt.getRatioTapChanger(); + if (rtc != null) { + String id = twt.getId() + RATIO_TABLE_SUFFIX; + writeRatioTapChanger(formatter, id, zb2, transformer, rtc); + } + + PhaseTapChanger ptc = twt.getPhaseTapChanger(); + if (ptc != null) { + String id = twt.getId() + PHASE_TABLE_SUFFIX; + writePhaseTapChanger(formatter, id, zb2, transformer, ptc); + } + } + + @Override + public void writeThreeWindingsTransformerTapChangerTableToFormatter(TableFormatter formatter, + ThreeWindingsTransformer twt) throws IOException { + int legNumber = 0; + for (ThreeWindingsTransformer.Leg leg : twt.getLegs()) { + legNumber++; + RatioTapChanger rtc = leg.getRatioTapChanger(); + double vb = twt.getRatedU0(); + double zb = vb * vb / AmplConstants.SB; + ImpedanceAndAdmittance transformer = new ImpedanceAndAdmittance(leg.getR(), leg.getX(), + leg.getG(), leg.getB()); + if (rtc != null) { + String id = twt.getId() + "_leg" + legNumber + RATIO_TABLE_SUFFIX; + writeRatioTapChanger(formatter, id, zb, transformer, rtc); + } + PhaseTapChanger ptc = leg.getPhaseTapChanger(); + if (ptc != null) { + String id = twt.getId() + "_leg" + legNumber + PHASE_TABLE_SUFFIX; + writePhaseTapChanger(formatter, id, zb, transformer, ptc); + } + } + } + + private void writeRatioTapChanger(TableFormatter formatter, String id, double zb2, + ImpedanceAndAdmittance transformerZandY, + RatioTapChanger rtc) throws IOException { + int num = getMapper().getInt(AmplSubset.TAP_CHANGER_TABLE, id); + + for (int position = rtc.getLowTapPosition(); position <= rtc.getHighTapPosition(); position++) { + RatioTapChangerStep step = rtc.getStep(position); + ImpedanceAndAdmittance stepCharacteristics = new ImpedanceAndAdmittance(step.getR(), step.getX(), step.getG(), step.getB()); + writeTapChanger(formatter, new TapChangerParametersForWriter(num, position, rtc.getLowTapPosition(), zb2, transformerZandY, stepCharacteristics, step.getRho(), 0)); + } + } + + private void writePhaseTapChanger(TableFormatter formatter, String id, double zb2, + ImpedanceAndAdmittance transformerZandY, + PhaseTapChanger ptc) throws IOException { + int num = getMapper().getInt(AmplSubset.TAP_CHANGER_TABLE, id); + + for (int position = ptc.getLowTapPosition(); position <= ptc.getHighTapPosition(); position++) { + PhaseTapChangerStep step = ptc.getStep(position); + ImpedanceAndAdmittance stepCharacteristics = new ImpedanceAndAdmittance(step.getR(), step.getX(), step.getG(), step.getB()); + writeTapChanger(formatter, new TapChangerParametersForWriter(num, position, ptc.getLowTapPosition(), zb2, transformerZandY, stepCharacteristics, step.getRho(), Math.toRadians(step.getAlpha()))); + } + } + + private record TapChangerParametersForWriter(int num, int stepPosition, int lowTapPosition, double zb2, + ImpedanceAndAdmittance transformer, ImpedanceAndAdmittance step, double rho, double alpha) { } + + private void writeTapChanger(TableFormatter formatter, TapChangerParametersForWriter parametersForWriter) throws IOException { + double rNorm = parametersForWriter.transformer.r * (1 + parametersForWriter.step.r / 100) / parametersForWriter.zb2; + double xNorm = parametersForWriter.transformer.x * (1 + parametersForWriter.step.x / 100) / parametersForWriter.zb2; + double gNorm = parametersForWriter.transformer.g * (1 + parametersForWriter.step.g / 100) * parametersForWriter.zb2; + double bNorm = parametersForWriter.transformer.b * (1 + parametersForWriter.step.b / 100) * parametersForWriter.zb2; + formatter.writeCell(getVariantIndex()) + .writeCell(parametersForWriter.num) + .writeCell(parametersForWriter.stepPosition - parametersForWriter.lowTapPosition + 1) + .writeCell(parametersForWriter.rho) + .writeCell(rNorm) + .writeCell(xNorm) + .writeCell(gNorm) + .writeCell(bNorm) + .writeCell(parametersForWriter.alpha) + .writeCell(getFaultNum()) + .writeCell(getActionNum()); + } + + @Override + public void addAdditionalCellsGenerator(TableFormatterHelper formatterHelper, Generator gen) { + int regulatingBusNum = gen.isVoltageRegulatorOn() ? + getMapper().getInt(AmplSubset.BUS, gen.getRegulatingTerminal().getBusView().getBus().getId()) : -1; + formatterHelper.addCell(regulatingBusNum, GENERATOR_V_REGUL_BUS_COLUMN_INDEX); + } + + @Override + public void addAdditionalCellsStaticVarCompensator(TableFormatterHelper formatterHelper, + StaticVarCompensator svc) { + boolean voltageRegulation = svc.getRegulationMode().equals(StaticVarCompensator.RegulationMode.VOLTAGE); + int regulatingBusNum = voltageRegulation ? + getMapper().getInt(AmplSubset.BUS, svc.getRegulatingTerminal().getBusView().getBus().getId()) : -1; + + // Cell to add + formatterHelper.addCell(regulatingBusNum, STATIC_VAR_COMPENSATOR_V_REGUL_BUS_COLUMN_INDEX); + } + +} diff --git a/ampl-converter/src/test/java/com/powsybl/ampl/converter/AmplNetworkWriterTest.java b/ampl-converter/src/test/java/com/powsybl/ampl/converter/AmplNetworkWriterTest.java index eb58f7b7838..5ed81140981 100644 --- a/ampl-converter/src/test/java/com/powsybl/ampl/converter/AmplNetworkWriterTest.java +++ b/ampl-converter/src/test/java/com/powsybl/ampl/converter/AmplNetworkWriterTest.java @@ -7,12 +7,12 @@ */ package com.powsybl.ampl.converter; -import com.powsybl.ampl.converter.version.AmplExportVersion; import com.powsybl.commons.test.AbstractSerDeTest; import com.powsybl.commons.datasource.DataSource; import com.powsybl.commons.datasource.MemDataSource; import com.powsybl.iidm.network.*; import com.powsybl.iidm.network.test.*; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import java.io.ByteArrayInputStream; @@ -28,12 +28,22 @@ */ class AmplNetworkWriterTest extends AbstractSerDeTest { + Properties properties; + private void assertEqualsToRef(MemDataSource dataSource, String suffix, String refFileName) throws IOException { try (InputStream actual = new ByteArrayInputStream(dataSource.getData(suffix, "txt"))) { assertTxtEquals(getClass().getResourceAsStream("/" + refFileName), actual); } } + @Override + @BeforeEach + public void setUp() throws IOException { + super.setUp(); + properties = new Properties(); + properties.put("iidm.export.ampl.export-version", "1.0"); + } + @Test void test() { AmplExporter exporter = new AmplExporter(); @@ -47,9 +57,7 @@ void writeEurostag() throws IOException { Network network = EurostagTutorialExample1Factory.createWithMoreGenerators(); MemDataSource dataSource = new MemDataSource(); - AmplExporter exporter = new AmplExporter(); - AmplExportConfig amplExportConfig = new AmplExportConfig(AmplExportConfig.ExportScope.ALL, false, AmplExportConfig.ExportActionType.CURATIVE, false, false, AmplExportVersion.defaultVersion(), true); - exporter.export(network, amplExportConfig, dataSource); + export(network, properties, dataSource); assertEqualsToRef(dataSource, "_network_substations", "inputs/eurostag-tutorial-example1-substations.txt"); assertEqualsToRef(dataSource, "_network_buses", "inputs/eurostag-tutorial-example1-buses.txt"); @@ -64,12 +72,10 @@ void writeEurostag() throws IOException { @Test void writeNetworkWithExtension() throws IOException { Network network = Network.create("sim1", "test"); - network.addExtension(FooNetworkExtension.class, new FooNetworkExtension()); MemDataSource dataSource = new MemDataSource(); - AmplExporter exporter = new AmplExporter(); - exporter.export(network, new Properties(), dataSource); + export(network, properties, dataSource); assertEqualsToRef(dataSource, "foo-network-extension", "inputs/foo-network-extension.txt"); } @@ -79,8 +85,7 @@ void writeShunt() throws IOException { Network network = EurostagTutorialExample1Factory.createWithMultipleConnectedComponents(); MemDataSource dataSource = new MemDataSource(); - AmplExporter exporter = new AmplExporter(); - exporter.export(network, new Properties(), dataSource); + export(network, properties, dataSource); assertEqualsToRef(dataSource, "_network_shunts", "inputs/eurostag-tutorial-example1-shunts.txt"); } @@ -92,8 +97,7 @@ void writeShunt2() throws IOException { sc.setSectionCount(2); MemDataSource dataSource = new MemDataSource(); - AmplExporter exporter = new AmplExporter(); - exporter.export(network, new Properties(), dataSource); + export(network, properties, dataSource); assertEqualsToRef(dataSource, "_network_shunts", "inputs/shunt-test-case-shunts.txt"); } @@ -103,7 +107,7 @@ void writeLcc() throws IOException { Network network = HvdcTestNetwork.createLcc(); MemDataSource dataSource = new MemDataSource(); - export(network, new Properties(), dataSource); + export(network, properties, dataSource); assertEqualsToRef(dataSource, "_network_hvdc", "inputs/hvdc-lcc-test-case.txt"); assertEqualsToRef(dataSource, "_network_lcc_converter_stations", "inputs/lcc-test-case.txt"); @@ -115,7 +119,7 @@ void writePhaseTapChanger() throws IOException { Network network = PhaseShifterTestCaseFactory.create(); MemDataSource dataSource = new MemDataSource(); - export(network, new Properties(), dataSource); + export(network, properties, dataSource); assertEqualsToRef(dataSource, "_network_ptc", "inputs/ptc-test-case.txt"); } @@ -125,7 +129,7 @@ void writeSVC() throws IOException { Network network = SvcTestCaseFactory.createWithMoreSVCs(); MemDataSource dataSource = new MemDataSource(); - export(network, new Properties(), dataSource); + export(network, properties, dataSource); assertEqualsToRef(dataSource, "_network_static_var_compensators", "inputs/svc-test-case.txt"); } @@ -135,7 +139,7 @@ void writeBattery() throws IOException { Network network = BatteryNetworkFactory.create(); MemDataSource dataSource = new MemDataSource(); - export(network, new Properties(), dataSource); + export(network, properties, dataSource); assertEqualsToRef(dataSource, "_network_batteries", "inputs/battery-test-batteries.txt"); } @@ -158,7 +162,7 @@ void writeThreeWindingsTransformer() throws IOException { .add(); MemDataSource dataSource = new MemDataSource(); - export(network, new Properties(), dataSource); + export(network, properties, dataSource); assertEqualsToRef(dataSource, "_network_branches", "inputs/three-windings-transformers-branches.txt"); assertEqualsToRef(dataSource, "_network_buses", "inputs/three-windings-transformers-buses.txt"); @@ -173,7 +177,7 @@ void writeVsc() throws IOException { Network network = HvdcTestNetwork.createVsc(); MemDataSource dataSource = new MemDataSource(); - export(network, new Properties(), dataSource); + export(network, properties, dataSource); assertEqualsToRef(dataSource, "_network_hvdc", "inputs/hvdc-vsc-test-case.txt"); assertEqualsToRef(dataSource, "_network_vsc_converter_stations", "inputs/vsc-test-case.txt"); @@ -185,7 +189,7 @@ void writeCurrentLimits() throws IOException { Network network = EurostagTutorialExample1Factory.createWithCurrentLimits(); MemDataSource dataSource = new MemDataSource(); - export(network, new Properties(), dataSource); + export(network, properties, dataSource); assertEqualsToRef(dataSource, "_network_limits", "inputs/current-limits-test-case.txt"); } @@ -201,7 +205,6 @@ void writeTieLine() throws IOException { .add(); } - Properties properties = new Properties(); properties.put("iidm.export.ampl.with-xnodes", "true"); MemDataSource dataSource = new MemDataSource(); @@ -218,7 +221,7 @@ void writeDanglingLines() throws IOException { Network network = DanglingLineNetworkFactory.create(); MemDataSource dataSource = new MemDataSource(); - export(network, new Properties(), dataSource); + export(network, properties, dataSource); assertEqualsToRef(dataSource, "_network_branches", "inputs/dangling-line-branches.txt"); assertEqualsToRef(dataSource, "_network_buses", "inputs/dangling-line-buses.txt"); @@ -233,7 +236,7 @@ void writeExtensions() throws IOException { HvdcLine l = network.getHvdcLine("L"); l.addExtension(FooExtension.class, new FooExtension()); MemDataSource dataSource = new MemDataSource(); - export(network, new Properties(), dataSource); + export(network, properties, dataSource); assertEqualsToRef(dataSource, "foo-extension", "inputs/foo-extension.txt"); } @@ -247,16 +250,15 @@ private void export(Network network, Properties properties, DataSource dataSourc void writeHeaders() throws IOException { Network network = Network.create("dummy_network", "test"); MemDataSource dataSource = new MemDataSource(); - export(network, new Properties(), dataSource); + export(network, properties, dataSource); assertEqualsToRef(dataSource, "_headers", "inputs/headers.txt"); } @Test - void writeHeadersWithVersion() throws IOException { + void writeHeadersWithVersion10() throws IOException { Network network = Network.create("dummy_network", "test"); MemDataSource dataSource = new MemDataSource(); - Properties properties = new Properties(); properties.put("iidm.export.ampl.export-version", "1.0"); export(network, properties, dataSource); @@ -268,12 +270,22 @@ void writeHeadersWithUnknownVersion() { Network network = Network.create("dummy_network", "test"); MemDataSource dataSource = new MemDataSource(); - Properties properties = new Properties(); properties.put("iidm.export.ampl.export-version", "V1_0"); Exception e = assertThrows(IllegalArgumentException.class, () -> export(network, properties, dataSource)); - assertTrue(e.getMessage().contains("Value V1_0 of parameter iidm.export.ampl.export-version is not contained in possible values [1.0]")); + assertTrue(e.getMessage().contains("Value V1_0 of parameter iidm.export.ampl.export-version is not contained in possible values [1.0")); + + } + @Test + void writeHeadersWithVersion11() throws IOException { + Network network = Network.create("dummy_network", "test"); + MemDataSource dataSource = new MemDataSource(); + + properties.put("iidm.export.ampl.export-version", "1.1"); + + export(network, properties, dataSource); + assertEqualsToRef(dataSource, "_headers", "inputs/extended_exporter/headers.txt"); } @Test @@ -282,7 +294,7 @@ void writeLineWithDifferentNominalVoltageAtEnds() throws IOException { network.getVoltageLevel("VL2").setNominalV(400); MemDataSource dataSource = new MemDataSource(); - export(network, new Properties(), dataSource); + export(network, properties, dataSource); assertEqualsToRef(dataSource, "_network_branches", "inputs/line-with-different-nominal-voltage-at-ends-test-case.txt"); } @@ -295,7 +307,7 @@ void writeZeroImpedanceLineWithDifferentNominalVoltageAtEnds() throws IOExceptio .setX(0); MemDataSource dataSource = new MemDataSource(); - export(network, new Properties(), dataSource); + export(network, properties, dataSource); assertEqualsToRef(dataSource, "_network_branches", "inputs/zero-impedance-line-with-different-nominal-voltage-at-ends-test-case.txt"); } diff --git a/ampl-converter/src/test/java/com/powsybl/ampl/converter/ExtendedAmplExporterTest.java b/ampl-converter/src/test/java/com/powsybl/ampl/converter/ExtendedAmplExporterTest.java new file mode 100644 index 00000000000..505dbe22017 --- /dev/null +++ b/ampl-converter/src/test/java/com/powsybl/ampl/converter/ExtendedAmplExporterTest.java @@ -0,0 +1,201 @@ +/** + * Copyright (c) 2023, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * SPDX-License-Identifier: MPL-2.0 + */ +package com.powsybl.ampl.converter; + +import com.powsybl.commons.datasource.MemDataSource; +import com.powsybl.commons.test.AbstractSerDeTest; +import com.powsybl.iidm.network.*; +import com.powsybl.iidm.network.extensions.SlackTerminalAdder; +import com.powsybl.iidm.network.test.DanglingLineNetworkFactory; +import com.powsybl.iidm.network.test.EurostagTutorialExample1Factory; +import com.powsybl.iidm.network.test.SvcTestCaseFactory; +import com.powsybl.iidm.network.test.ThreeWindingsTransformerNetworkFactory; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.Properties; + +import static com.powsybl.commons.test.ComparisonUtils.assertTxtEquals; + +/** + * @author Nicolas PIERRE {@literal } + * @author Pierre ARVY {@literal } + */ +class ExtendedAmplExporterTest extends AbstractSerDeTest { + + MemDataSource dataSource; + AmplExporter exporter; + + private void assertEqualsToRef(MemDataSource dataSource, String suffix, String refFileName) throws IOException { + try (InputStream actual = new ByteArrayInputStream(dataSource.getData(suffix, "txt"))) { + assertTxtEquals(getClass().getResourceAsStream("/" + refFileName), actual); + } + } + + @Override + @BeforeEach + public void setUp() throws IOException { + super.setUp(); + dataSource = new MemDataSource(); + exporter = new AmplExporter(); + } + + @Test + void testNoModifiedExports() throws IOException { + Network network = EurostagTutorialExample1Factory.createWithMoreGenerators(); + + exporter.export(network, new Properties(), dataSource); + + assertEqualsToRef(dataSource, "_network_substations", "inputs/eurostag-tutorial-example1-substations.txt"); + assertEqualsToRef(dataSource, "_network_rtc", "inputs/eurostag-tutorial-example1-rtc.txt"); + assertEqualsToRef(dataSource, "_network_ptc", "inputs/eurostag-tutorial-example1-ptc.txt"); + assertEqualsToRef(dataSource, "_network_loads", "inputs/eurostag-tutorial-example1-loads.txt"); + assertEqualsToRef(dataSource, "_network_limits", "inputs/eurostag-tutorial-example1-limits.txt"); + } + + @Test + void testSlackBusExport() throws IOException { + Network network = EurostagTutorialExample1Factory.create(); + VoltageLevel vlGen = network.getVoltageLevel("VLLOAD"); + Bus bus = vlGen.getBusBreakerView().getBus("NLOAD"); + SlackTerminalAdder adder = vlGen.newExtension(SlackTerminalAdder.class); + adder.withTerminal(bus.getConnectedTerminals().iterator().next()).add(); + + exporter.export(network, new Properties(), dataSource); + + assertEqualsToRef(dataSource, "_network_buses", + "inputs/extended_exporter/eurostag-tutorial-example1-buses.txt"); + } + + @Test + void testSlackBusValue3wtMiddleBusExport() throws IOException { + Network network = ThreeWindingsTransformerNetworkFactory.createWithCurrentLimits(); + + exporter.export(network, new Properties(), dataSource); + + // verify slack bus has been added to buses file + assertEqualsToRef(dataSource, "_network_buses", + "inputs/extended_exporter/three-windings-transformers-buses.txt"); + } + + @Test + void testSlackBusValueDanglingLineMiddleBusExport() throws IOException { + Network network = DanglingLineNetworkFactory.create(); + + exporter.export(network, new Properties(), dataSource); + + // verify slack bus has been added to buses file + assertEqualsToRef(dataSource, "_network_buses", + "inputs/extended_exporter/dangling-line-buses.txt"); + } + + @Test + void testSlackBusValueTieLineMiddleBusExport() throws IOException { + Network network = EurostagTutorialExample1Factory.createWithTieLine(); + + Properties properties = new Properties(); + properties.put("iidm.export.ampl.with-xnodes", "true"); + exporter.export(network, properties, dataSource); + + // verify slack bus has been added to buses file + assertEqualsToRef(dataSource, "_network_buses", + "inputs/extended_exporter/eurostag-tutorial-example1-buses-tl.txt"); + } + + @Test + void testNewTapTwoWindingsTransformerExport() throws IOException { + Network network = EurostagTutorialExample1Factory.create(); + + TwoWindingsTransformer transformer = network.getTwoWindingsTransformers().iterator().next(); + transformer.newRatioTapChanger(); + transformer.newPhaseTapChanger(); + exporter.export(network, new Properties(), dataSource); + // verify r, g and b values have been added to tap changer file + assertEqualsToRef(dataSource, "_network_tct", + "inputs/extended_exporter/eurostag-tutorial-example1-tct.txt"); + } + + @Test + void testNewTapThreeWindingsTransformerExport() throws IOException { + Network network = ThreeWindingsTransformerNetworkFactory.createWithCurrentLimits(); + network.getThreeWindingsTransformer("3WT").getLeg1() + .newPhaseTapChanger() + .beginStep() + .setRho(1) + .setR(0.1) + .setX(1.) + .setB(0.) + .setG(0.) + .setAlpha(0) + .endStep() + .setTapPosition(0) + .setLowTapPosition(0) + .add(); + + exporter.export(network, new Properties(), dataSource); + + // verify r, g and b values have been added to tap changer file + assertEqualsToRef(dataSource, "_network_tct", + "inputs/extended_exporter/three-windings-transformers-tct.txt"); + } + + @Test + void testRegulatingBusIdExportGenerators() throws IOException { + Network network = EurostagTutorialExample1Factory.createWithMoreGenerators(); + network.getGenerator("GEN").setVoltageRegulatorOn(false); + network.getVoltageLevel("VLGEN").newGenerator() + .setId("GEN3") + .setBus("NGEN") + .setConnectableBus("NGEN") + .setMinP(-9999.99) + .setMaxP(9999.99) + .setVoltageRegulatorOn(true) + .setRegulatingTerminal(network.getLoad("LOAD").getTerminal()) + .setTargetV(152.5) + .setTargetP(607.0) + .setTargetQ(301.0) + .add(); + + exporter.export(network, new Properties(), dataSource); + + assertEqualsToRef(dataSource, "_network_generators", + "inputs/extended_exporter/eurostag-tutorial-example1-generators-regulating-bus.txt"); + } + + @Test + void testRegulatingBusIdExportSvc() throws IOException { + Network network = SvcTestCaseFactory.createWithMoreSVCs(); + network.getStaticVarCompensator("SVC2").setRegulationMode(StaticVarCompensator.RegulationMode.OFF); + network.getVoltageLevel("VL1").newStaticVarCompensator() + .setId("SVC1") + .setConnectableBus("B1") + .setBus("B1") + .setBmin(0.0002) + .setBmax(0.0008) + .setRegulationMode(StaticVarCompensator.RegulationMode.VOLTAGE) + .setVoltageSetpoint(390) + .setRegulatingTerminal(network.getLoad("L2").getTerminal()) + .add(); + + exporter.export(network, new Properties(), dataSource); + + assertEqualsToRef(dataSource, "_network_static_var_compensators", + "inputs/extended_exporter/svc-test-case-regulating-bus.txt"); + } + + @Test + void testVersion() throws IOException { + Network network = Network.create("dummy_network", "test"); + exporter.export(network, new Properties(), dataSource); + + assertEqualsToRef(dataSource, "_headers", "inputs/extended_exporter/headers.txt"); + } +} diff --git a/ampl-converter/src/test/resources/inputs/extended_exporter/dangling-line-buses.txt b/ampl-converter/src/test/resources/inputs/extended_exporter/dangling-line-buses.txt new file mode 100644 index 00000000000..6ee9de1a5d2 --- /dev/null +++ b/ampl-converter/src/test/resources/inputs/extended_exporter/dangling-line-buses.txt @@ -0,0 +1,4 @@ +#Buses (dangling-line/InitialState) +#"variant" "num" "substation" "cc" "v (pu)" "theta (rad)" "p (MW)" "q (MVar)" "slack bus" "fault" "curative" "id" +1 1 1 0 -99999.0 -99999.0 0.00000 0.00000 false 0 0 "VL_0" +1 2 2 0 -99999.0 -99999.0 0.00000 0.00000 false 0 0 "DL" diff --git a/ampl-converter/src/test/resources/inputs/extended_exporter/eurostag-tutorial-example1-buses-tl.txt b/ampl-converter/src/test/resources/inputs/extended_exporter/eurostag-tutorial-example1-buses-tl.txt new file mode 100644 index 00000000000..831f0411822 --- /dev/null +++ b/ampl-converter/src/test/resources/inputs/extended_exporter/eurostag-tutorial-example1-buses-tl.txt @@ -0,0 +1,8 @@ +#Buses (sim1/InitialState) +#"variant" "num" "substation" "cc" "v (pu)" "theta (rad)" "p (MW)" "q (MVar)" "slack bus" "fault" "curative" "id" +1 1 1 0 1.02083 0.0405959 -605.558 -225.283 false 0 0 "VLGEN_0" +1 2 2 0 1.05827 0.00000 0.00000 0.00000 false 0 0 "VLHV1_0" +1 3 3 0 1.02619 -0.0611975 0.00000 0.00000 false 0 0 "VLHV2_0" +1 4 4 0 0.983857 -0.167804 600.000 200.000 false 0 0 "VLLOAD_0" +1 5 5 0 -99999.0 -99999.0 0.00000 0.00000 false 0 0 "XNODE1" +1 6 6 0 -99999.0 -99999.0 0.00000 0.00000 false 0 0 "XNODE2" diff --git a/ampl-converter/src/test/resources/inputs/extended_exporter/eurostag-tutorial-example1-buses.txt b/ampl-converter/src/test/resources/inputs/extended_exporter/eurostag-tutorial-example1-buses.txt new file mode 100644 index 00000000000..4d907ee5d04 --- /dev/null +++ b/ampl-converter/src/test/resources/inputs/extended_exporter/eurostag-tutorial-example1-buses.txt @@ -0,0 +1,6 @@ +#Buses (sim1/InitialState) +#"variant" "num" "substation" "cc" "v (pu)" "theta (rad)" "p (MW)" "q (MVar)" "slack bus" "fault" "curative" "id" +1 1 1 0 -99999.0 -99999.0 0.00000 0.00000 false 0 0 "VLGEN_0" +1 2 2 0 -99999.0 -99999.0 0.00000 0.00000 false 0 0 "VLHV1_0" +1 3 3 0 -99999.0 -99999.0 0.00000 0.00000 false 0 0 "VLHV2_0" +1 4 4 0 -99999.0 -99999.0 0.00000 0.00000 true 0 0 "VLLOAD_0" diff --git a/ampl-converter/src/test/resources/inputs/extended_exporter/eurostag-tutorial-example1-generators-regulating-bus.txt b/ampl-converter/src/test/resources/inputs/extended_exporter/eurostag-tutorial-example1-generators-regulating-bus.txt new file mode 100644 index 00000000000..e39619353d9 --- /dev/null +++ b/ampl-converter/src/test/resources/inputs/extended_exporter/eurostag-tutorial-example1-generators-regulating-bus.txt @@ -0,0 +1,5 @@ +#Generators (sim1/InitialState) +#"variant" "num" "bus" "con. bus" "substation" "minP (MW)" "maxP (MW)" "minQmaxP (MVar)" "minQ0 (MVar)" "minQminP (MVar)" "maxQmaxP (MVar)" "maxQ0 (MVar)" "maxQminP (MVar)" "v regul." "v regul. bus" "targetV (pu)" "targetP (MW)" "targetQ (MVar)" "fault" "curative" "id" "description" "P (MW)" "Q (MVar)" +1 1 1 1 1 -9999.99 9999.99 -9999.99 -9999.99 -9999.99 9999.99 9999.99 9999.99 false -1 1.02083 607.000 301.000 0 0 "GEN" "GEN" -99999.0 -99999.0 +1 2 1 1 1 -9999.99 9999.99 4.00000 6.00000 6.00000 5.00000 7.00000 7.00000 true 1 1.02083 607.000 301.000 0 0 "GEN2" "GEN2" -99999.0 -99999.0 +1 3 1 1 1 -9999.99 9999.99 -1.79769e+308 -1.79769e+308 -1.79769e+308 1.79769e+308 1.79769e+308 1.79769e+308 true 4 1.01667 607.000 301.000 0 0 "GEN3" "GEN3" -99999.0 -99999.0 diff --git a/ampl-converter/src/test/resources/inputs/extended_exporter/eurostag-tutorial-example1-tct.txt b/ampl-converter/src/test/resources/inputs/extended_exporter/eurostag-tutorial-example1-tct.txt new file mode 100644 index 00000000000..e6bc54e3d8f --- /dev/null +++ b/ampl-converter/src/test/resources/inputs/extended_exporter/eurostag-tutorial-example1-tct.txt @@ -0,0 +1,5 @@ +#Tap changer table (sim1/InitialState) +#"variant" "num" "tap" "var ratio" "r (pu)" "x (pu)" "g (pu)" "b (pu)" "angle (rad)" "fault" "curative" +1 1 1 0.850567 0.000210000 0.0179988 0.00000 0.00000 0.00000 0 0 +1 1 2 1.00067 0.000210000 0.0179988 0.00000 0.00000 0.00000 0 0 +1 1 3 1.15077 0.000210000 0.0179988 0.00000 0.00000 0.00000 0 0 diff --git a/ampl-converter/src/test/resources/inputs/extended_exporter/headers.txt b/ampl-converter/src/test/resources/inputs/extended_exporter/headers.txt new file mode 100644 index 00000000000..3987f9c4e85 --- /dev/null +++ b/ampl-converter/src/test/resources/inputs/extended_exporter/headers.txt @@ -0,0 +1 @@ +version 1.1 diff --git a/ampl-converter/src/test/resources/inputs/extended_exporter/svc-test-case-regulating-bus.txt b/ampl-converter/src/test/resources/inputs/extended_exporter/svc-test-case-regulating-bus.txt new file mode 100644 index 00000000000..6c474e1d57f --- /dev/null +++ b/ampl-converter/src/test/resources/inputs/extended_exporter/svc-test-case-regulating-bus.txt @@ -0,0 +1,5 @@ +#Static VAR compensators (svcTestCase/InitialState) +#"variant" "num" "bus" "con. bus" "substation" "minB (pu)" "maxB (pu)" "v regul." "v regul. bus" "targetV (pu)" "targetQ (MVar)" "fault" "curative" "id" "description" "P (MW)" "Q (MVar)" +1 1 2 2 2 0.288800 1.15520 false -1 1.02632 -99999.0 0 0 "SVC2" "SVC2" -99999.0 -99999.0 +1 2 2 2 2 0.288800 1.15520 true 2 1.02632 350.000 0 0 "SVC3" "SVC3" -99999.0 -99999.0 +1 3 1 1 1 0.288800 1.15520 true 2 1.02632 -99999.0 0 0 "SVC1" "SVC1" -99999.0 -99999.0 diff --git a/ampl-converter/src/test/resources/inputs/extended_exporter/three-windings-transformers-buses.txt b/ampl-converter/src/test/resources/inputs/extended_exporter/three-windings-transformers-buses.txt new file mode 100644 index 00000000000..40da6768d11 --- /dev/null +++ b/ampl-converter/src/test/resources/inputs/extended_exporter/three-windings-transformers-buses.txt @@ -0,0 +1,6 @@ +#Buses (three-windings-transformer/InitialState) +#"variant" "num" "substation" "cc" "v (pu)" "theta (rad)" "p (MW)" "q (MVar)" "slack bus" "fault" "curative" "id" +1 1 1 0 1.01200 -0.167901 0.00000 0.00000 false 0 0 "VL_132_0" +1 2 2 0 1.05700 -0.265988 11.2000 7.50000 false 0 0 "VL_33_0" +1 3 3 0 1.07100 -0.265988 0.00000 -10.6000 false 0 0 "VL_11_0" +1 4 4 0 -99999.0 -99999.0 0.00000 0.00000 false 0 0 "3WT" diff --git a/ampl-converter/src/test/resources/inputs/extended_exporter/three-windings-transformers-tct.txt b/ampl-converter/src/test/resources/inputs/extended_exporter/three-windings-transformers-tct.txt new file mode 100644 index 00000000000..cb190a811e2 --- /dev/null +++ b/ampl-converter/src/test/resources/inputs/extended_exporter/three-windings-transformers-tct.txt @@ -0,0 +1,9 @@ +#Tap changer table (three-windings-transformer/InitialState) +#"variant" "num" "tap" "var ratio" "r (pu)" "x (pu)" "g (pu)" "b (pu)" "angle (rad)" "fault" "curative" +1 3 1 1.00000 0.100100 0.0101000 1.00000 0.100000 0.00000 0 0 +1 1 1 0.900000 0.00631126 0.000625613 0.00000 0.00000 0.00000 0 0 +1 1 2 1.00000 0.00631806 0.000625681 0.00000 0.00000 0.00000 0 0 +1 1 3 1.10000 0.00632487 0.000625749 0.00000 0.00000 0.00000 0 0 +1 2 1 0.900000 0.000695201 6.94520e-05 0.00000 0.00000 0.00000 0 0 +1 2 2 1.00000 0.000695285 6.94528e-05 0.00000 0.00000 0.00000 0 0 +1 2 3 1.10000 0.000695369 6.94537e-05 0.00000 0.00000 0.00000 0 0 diff --git a/commons/src/main/java/com/powsybl/commons/io/table/TableFormatterHelper.java b/commons/src/main/java/com/powsybl/commons/io/table/TableFormatterHelper.java new file mode 100644 index 00000000000..c3cda7bd5f5 --- /dev/null +++ b/commons/src/main/java/com/powsybl/commons/io/table/TableFormatterHelper.java @@ -0,0 +1,134 @@ +/* + * Copyright (c) 2024, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * SPDX-License-Identifier: MPL-2.0 + */ +package com.powsybl.commons.io.table; + +import java.io.IOException; +import java.util.LinkedList; +import java.util.List; + +/** + * @author Nicolas Rol {@literal } + */ +public class TableFormatterHelper { + + private final TableFormatter tableFormatter; + private final List objectsToWrite = new LinkedList<>(); + + public TableFormatterHelper(TableFormatter tableFormatter) { + this.tableFormatter = tableFormatter; + } + + public TableFormatterHelper addCell(Object object) { + objectsToWrite.add(object); + return this; + } + + public TableFormatterHelper addCell(Object object, int position) { + objectsToWrite.add(position, object); + return this; + } + + public TableFormatterHelper addEmptyCell() { + objectsToWrite.add(new EmptyCells(1)); + return this; + } + + public TableFormatterHelper addEmptyCell(int position) { + objectsToWrite.add(position, new EmptyCells(1)); + return this; + } + + public TableFormatterHelper addEmptyCells(int count) { + objectsToWrite.add(new EmptyCells(count)); + return this; + } + + public TableFormatterHelper addEmptyCells(int count, int position) { + objectsToWrite.add(position, new EmptyCells(count)); + return this; + } + + public TableFormatterHelper addEmptyLine() { + objectsToWrite.add(new EmptyLines(1)); + return this; + } + + public TableFormatterHelper addEmptyLine(int position) { + objectsToWrite.add(position, new EmptyLines(1)); + return this; + } + + public TableFormatterHelper addEmptyLines(int count) { + objectsToWrite.add(new EmptyLines(count)); + return this; + } + + public TableFormatterHelper addEmptyLines(int count, int position) { + objectsToWrite.add(position, new EmptyLines(count)); + return this; + } + + public TableFormatterHelper addComment(String comment) { + objectsToWrite.add(new Comment(comment)); + return this; + } + + public TableFormatterHelper addComment(String comment, int position) { + objectsToWrite.add(position, new Comment(comment)); + return this; + } + + public TableFormatter write() throws IOException { + for (Object object : objectsToWrite) { + if (object instanceof String s) { + tableFormatter.writeCell(s); + } else if (object instanceof Character c) { + tableFormatter.writeCell(c); + } else if (object instanceof Integer i) { + tableFormatter.writeCell(i); + } else if (object instanceof Float f) { + tableFormatter.writeCell(f); + } else if (object instanceof Double d) { + tableFormatter.writeCell(d); + } else if (object instanceof Boolean b) { + tableFormatter.writeCell(b); + } else if (object instanceof Comment comment) { + tableFormatter.writeComment(comment.commentToWrite); + } else if (object instanceof EmptyCells emptyCells) { + tableFormatter.writeEmptyCells(emptyCells.numberOfCells); + } else if (object instanceof EmptyLines emptyLines) { + tableFormatter.writeEmptyLines(emptyLines.numberOfLines); + } + } + return tableFormatter; + } + + private static class Comment { + String commentToWrite; + + Comment(String commentToWrite) { + this.commentToWrite = commentToWrite; + } + } + + private static class EmptyCells { + int numberOfCells; + + EmptyCells(int numberOfCells) { + this.numberOfCells = numberOfCells; + } + } + + private static class EmptyLines { + int numberOfLines; + + EmptyLines(int numberOfLines) { + this.numberOfLines = numberOfLines; + } + } +}