diff --git a/EU2Mod_MB/src/main/java/itesla/converter/ModelicaModel.java b/EU2Mod_MB/src/main/java/itesla/converter/ModelicaModel.java index 0af07f2b..ce5fae3a 100644 --- a/EU2Mod_MB/src/main/java/itesla/converter/ModelicaModel.java +++ b/EU2Mod_MB/src/main/java/itesla/converter/ModelicaModel.java @@ -549,9 +549,13 @@ public void Connection() { connRight = model.nameModelica + "_" + Blocks[Link[i][1] - 1].GraphicalNumber.toString() + ".u" + Link[i][2].toString(); } } - Blocks[Link[i][1] - 1].UsedInputPins.set(Link[i][2] - 1, true); - conn = conn + connLeft + ", " + connRight + ");"; - outputConnection.add(conn); + if ((Link[i][2]) <= Blocks[Link[i][1] - 1].UsedInputPins.size()) { + Blocks[Link[i][1] - 1].UsedInputPins.set(Link[i][2] - 1, true); + conn = conn + connLeft + ", " + connRight + ");"; + outputConnection.add(conn); + } else { + System.err.println("Connection - file: " + pathEu + ", id: " + Blocks[Link[i][1] - 1].idEu + ", inputPins: " + Blocks[Link[i][1] - 1].UsedInputPins + ", inputPins size: " + Blocks[Link[i][1] - 1].UsedInputPins.size() + ", but the index to-be set is: " + Link[i][2]); + } } } @@ -601,8 +605,12 @@ public void InputConnection() { } } conn = " connect(" + connLeft + ", " + connRight + ");"; - Blocks[i].UsedInputPins.set(indConnRight - 1, true); - outputInputConnection.add(conn); + if (indConnRight <= Blocks[i].UsedInputPins.size()) { + Blocks[i].UsedInputPins.set(indConnRight - 1, true); + outputInputConnection.add(conn); + } else { + System.err.println("InputConnection - file: " + pathEu + ", id: " + Blocks[i].idEu + ", inputPins: " + Blocks[i].UsedInputPins + ", inputPins size: " + Blocks[i].UsedInputPins.size() + ", but the index to-be set is: " + (indConnRight - 1)); + } } } } diff --git a/eurostag-ech-export/src/main/java/eu/itesla_project/iidm/eurostag/export/EchUtil.java b/eurostag-ech-export/src/main/java/eu/itesla_project/iidm/eurostag/export/EchUtil.java index dd14c006..56cb658c 100644 --- a/eurostag-ech-export/src/main/java/eu/itesla_project/iidm/eurostag/export/EchUtil.java +++ b/eurostag-ech-export/src/main/java/eu/itesla_project/iidm/eurostag/export/EchUtil.java @@ -11,6 +11,7 @@ import java.util.Collections; import java.util.HashSet; +import java.util.Objects; import java.util.Set; import java.util.stream.StreamSupport; @@ -183,4 +184,53 @@ public static boolean isInMainCc(Branch branch, boolean noswitch) { return (ConnectedComponents.getCcNum(EchUtil.getBus(branch.getTerminal1(), noswitch)) == Component.MAIN_NUM) && (ConnectedComponents.getCcNum(EchUtil.getBus(branch.getTerminal2(), noswitch)) == Component.MAIN_NUM); } + + /** + * given an iIDM HVDC line , returns its DC voltage to be used with Eurostag + * Multiplying the line's nominalV by 2 corresponds to the fact that iIDM refers to the cable-ground voltage + * while Eurostag regulations to the cable-cable voltage + */ + public static float getHvdcLineDcVoltage(HvdcLine line) { + Objects.requireNonNull(line); + return line.getNominalV() * 2.0f; + } + + public static boolean isPMode(HvdcConverterStation vscConv, HvdcLine hvdcLine) { + Objects.requireNonNull(vscConv); + Objects.requireNonNull(hvdcLine); + HvdcConverterStation side1Conv = hvdcLine.getConverterStation1(); + HvdcConverterStation side2Conv = hvdcLine.getConverterStation2(); + if ((hvdcLine.getConvertersMode().equals(HvdcLine.ConvertersMode.SIDE_1_RECTIFIER_SIDE_2_INVERTER)) + && (vscConv.getId().equals(side1Conv.getId()))) { + return true; + } + if ((hvdcLine.getConvertersMode().equals(HvdcLine.ConvertersMode.SIDE_1_INVERTER_SIDE_2_RECTIFIER)) + && (vscConv.getId().equals(side2Conv.getId()))) { + return true; + } + return false; + } + + public static HvdcConverterStation getPStation(HvdcLine hvdcLine) { + Objects.requireNonNull(hvdcLine); + if (hvdcLine.getConvertersMode().equals(HvdcLine.ConvertersMode.SIDE_1_RECTIFIER_SIDE_2_INVERTER)) { + return hvdcLine.getConverterStation1(); + } + if (hvdcLine.getConvertersMode().equals(HvdcLine.ConvertersMode.SIDE_1_INVERTER_SIDE_2_RECTIFIER)) { + return hvdcLine.getConverterStation2(); + } + return null; + } + + public static HvdcConverterStation getVStation(HvdcLine hvdcLine) { + Objects.requireNonNull(hvdcLine); + if (hvdcLine.getConvertersMode().equals(HvdcLine.ConvertersMode.SIDE_1_RECTIFIER_SIDE_2_INVERTER)) { + return hvdcLine.getConverterStation2(); + } + if (hvdcLine.getConvertersMode().equals(HvdcLine.ConvertersMode.SIDE_1_INVERTER_SIDE_2_RECTIFIER)) { + return hvdcLine.getConverterStation1(); + } + return null; + } + } diff --git a/eurostag-ech-export/src/main/java/eu/itesla_project/iidm/eurostag/export/EurostagDictionary.java b/eurostag-ech-export/src/main/java/eu/itesla_project/iidm/eurostag/export/EurostagDictionary.java index 866a4b9c..8d87a0d6 100644 --- a/eurostag-ech-export/src/main/java/eu/itesla_project/iidm/eurostag/export/EurostagDictionary.java +++ b/eurostag-ech-export/src/main/java/eu/itesla_project/iidm/eurostag/export/EurostagDictionary.java @@ -13,6 +13,8 @@ import eu.itesla_project.eurostag.network.EsgBranchName; import com.powsybl.iidm.network.*; import com.powsybl.iidm.network.util.Identifiables; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.io.BufferedReader; import java.io.BufferedWriter; @@ -31,6 +33,8 @@ */ public final class EurostagDictionary { + private static final Logger LOGGER = LoggerFactory.getLogger(EurostagDictionary.class); + private static final EurostagNamingStrategy NAMING_STRATEGY = new DicoEurostagNamingStrategyFactory().create(); private final BiMap iidmId2esgId; @@ -54,16 +58,19 @@ public static EurostagDictionary create(Network network, BranchParallelIndexes p Set generatorIds = Identifiables.sort(network.getGenerators()).stream().map(Generator::getId).collect(Collectors.toSet()); Set shuntIds = Identifiables.sort(network.getShunts()).stream().map(ShuntCompensator::getId).collect(Collectors.toSet()); Set svcIds = Identifiables.sort(network.getStaticVarCompensators()).stream().map(StaticVarCompensator::getId).collect(Collectors.toSet()); + Set converterStationsIds = Identifiables.sort(network.getVscConverterStations()).stream().map(VscConverterStation::getId).collect(Collectors.toSet()); NAMING_STRATEGY.fillDictionary(dictionary, EurostagNamingStrategy.NameType.NODE, busIds); NAMING_STRATEGY.fillDictionary(dictionary, EurostagNamingStrategy.NameType.GENERATOR, generatorIds); NAMING_STRATEGY.fillDictionary(dictionary, EurostagNamingStrategy.NameType.LOAD, loadIds); NAMING_STRATEGY.fillDictionary(dictionary, EurostagNamingStrategy.NameType.BANK, shuntIds); NAMING_STRATEGY.fillDictionary(dictionary, EurostagNamingStrategy.NameType.SVC, svcIds); + NAMING_STRATEGY.fillDictionary(dictionary, EurostagNamingStrategy.NameType.VSC, converterStationsIds); for (DanglingLine dl : Identifiables.sort(network.getDanglingLines())) { // skip if not in the main connected component if (config.isExportMainCCOnly() && !EchUtil.isInMainCc(dl, config.isNoSwitch())) { + LOGGER.trace("dangling line not mapped, not in main component: {}", dl.getId()); continue; } ConnectionBus bus1 = ConnectionBus.fromTerminal(dl.getTerminal(), config, fakeNodes); @@ -79,6 +86,7 @@ public static EurostagDictionary create(Network network, BranchParallelIndexes p Bus bus2 = EchUtil.getBus2(vl, sw.getId(), config); // skip switches not in the main connected component if (config.isExportMainCCOnly() && (!EchUtil.isInMainCc(bus1) || !EchUtil.isInMainCc(bus2))) { + LOGGER.trace("switch not mapped, not in main component: {}", sw.getId()); continue; } dictionary.addIfNotExist(sw.getId(), @@ -91,6 +99,7 @@ public static EurostagDictionary create(Network network, BranchParallelIndexes p for (Line l : Identifiables.sort(network.getLines())) { // skip lines not in the main connected component if (config.isExportMainCCOnly() && !EchUtil.isInMainCc(l, config.isNoSwitch())) { + LOGGER.trace("line not mapped, not in main component: {}", l.getId()); continue; } ConnectionBus bus1 = ConnectionBus.fromTerminal(l.getTerminal1(), config, fakeNodes); @@ -104,6 +113,7 @@ public static EurostagDictionary create(Network network, BranchParallelIndexes p for (TwoWindingsTransformer twt : Identifiables.sort(network.getTwoWindingsTransformers())) { // skip transformers not in the main connected component if (config.isExportMainCCOnly() && !EchUtil.isInMainCc(twt, config.isNoSwitch())) { + LOGGER.trace("two windings transformer not mapped, not in main component: {}", twt.getId()); continue; } ConnectionBus bus1 = ConnectionBus.fromTerminal(twt.getTerminal1(), config, fakeNodes); @@ -114,6 +124,7 @@ public static EurostagDictionary create(Network network, BranchParallelIndexes p } for (ThreeWindingsTransformer twt : Identifiables.sort(network.getThreeWindingsTransformers())) { + LOGGER.error("NOT YET IMPLEMENTED"); throw new AssertionError("TODO"); } @@ -133,7 +144,9 @@ private EurostagDictionary(Map iidmId2esgId, EurostagEchExportCo public void add(String iidmId, String esgId) { if (iidmId2esgId.containsKey(iidmId)) { - throw new RuntimeException("IIDM id '" + iidmId + "' already exists in the dictionary"); + String errorMsg = "IIDM id '" + iidmId + "' already exists in the dictionary"; + LOGGER.error(errorMsg); + throw new RuntimeException(errorMsg); } iidmId2esgId.put(iidmId, esgId); } @@ -141,8 +154,10 @@ public void add(String iidmId, String esgId) { public void addIfNotExist(String iidmId, String esgId) { if (!iidmId2esgId.containsKey(iidmId)) { if (iidmId2esgId.inverse().containsKey(esgId)) { - throw new RuntimeException("Esg id '" + esgId + "' is already associated to IIDM id '" - + iidmId2esgId.inverse().get(esgId) + "' impossible to associate it to IIDM id '" + iidmId + "'"); + String errorMsg = "Esg id '" + esgId + "' is already associated to IIDM id '" + + iidmId2esgId.inverse().get(esgId) + "' impossible to associate it to IIDM id '" + iidmId + "'"; + LOGGER.error(errorMsg); + throw new RuntimeException(errorMsg); } iidmId2esgId.put(iidmId, esgId); } @@ -150,14 +165,18 @@ public void addIfNotExist(String iidmId, String esgId) { public String getEsgId(String iidmId) { if (!iidmId2esgId.containsKey(iidmId)) { - throw new RuntimeException("IIDM id '" + iidmId + "' + not found in the dictionary"); + String errorMsg = "IIDM id '" + iidmId + "' + not found in the dictionary"; + LOGGER.error(errorMsg); + throw new RuntimeException(errorMsg); } return iidmId2esgId.get(iidmId); } public String getIidmId(String esgId) { if (!iidmId2esgId.containsValue(esgId)) { - throw new RuntimeException("ESG id '" + esgId + "' + not found in the dictionary"); + String errorMsg = "ESG id '" + esgId + "' + not found in the dictionary"; + LOGGER.error(errorMsg); + throw new RuntimeException(errorMsg); } return iidmId2esgId.inverse().get(esgId); } @@ -187,6 +206,7 @@ public void load(Path file) { } } } catch (IOException e) { + LOGGER.error(e.getMessage(), e); throw new RuntimeException(e); } } @@ -200,6 +220,7 @@ public void dump(Path file) { } } } catch (IOException e) { + LOGGER.error(e.getMessage(), e); throw new RuntimeException(e); } } diff --git a/eurostag-ech-export/src/main/java/eu/itesla_project/iidm/eurostag/export/EurostagEchExport.java b/eurostag-ech-export/src/main/java/eu/itesla_project/iidm/eurostag/export/EurostagEchExport.java index 1b583894..f511a494 100644 --- a/eurostag-ech-export/src/main/java/eu/itesla_project/iidm/eurostag/export/EurostagEchExport.java +++ b/eurostag-ech-export/src/main/java/eu/itesla_project/iidm/eurostag/export/EurostagEchExport.java @@ -26,7 +26,7 @@ /** * @author Geoffroy Jamgotchian */ -public class EurostagEchExport { +public class EurostagEchExport implements EurostagEchExporter { private static final Logger LOGGER = LoggerFactory.getLogger(EurostagEchExport.class); @@ -38,16 +38,16 @@ public class EurostagEchExport { /** * epsilon value for susceptance */ - public static final float B_EPSILON = 0.00001f; + public static final float B_EPSILON = 0.000001f; private static final String XNODE_V_PROPERTY = "xnode_v"; private static final String XNODE_ANGLE_PROPERTY = "xnode_angle"; - private final Network network; - private final EurostagEchExportConfig config; - private final BranchParallelIndexes parallelIndexes; - private final EurostagDictionary dictionary; - private final EurostagFakeNodes fakeNodes; + protected final Network network; + protected final EurostagEchExportConfig config; + protected final BranchParallelIndexes parallelIndexes; + protected final EurostagDictionary dictionary; + protected final EurostagFakeNodes fakeNodes; public EurostagEchExport(Network network, EurostagEchExportConfig config, BranchParallelIndexes parallelIndexes, EurostagDictionary dictionary, EurostagFakeNodes fakeNodes) { this.network = Objects.requireNonNull(network); @@ -74,6 +74,10 @@ private void createAreas(EsgNetwork esgNetwork) { for (Country c : network.getCountries()) { esgNetwork.addArea(new EsgArea(new Esg2charName(c.toString()), EsgArea.Type.AC)); } + + if (network.getHvdcLineCount() > 0) { + esgNetwork.addArea(new EsgArea(new Esg2charName("DC"), EsgArea.Type.DC)); + } } private EsgNode createNode(String busId, String countryIsoCode, float nominalV, float v, float angle, boolean slackBus) { @@ -194,14 +198,53 @@ private void createLines(EsgNetwork esgNetwork, EsgGeneralParameters parameters) LOGGER.warn("not in main component, skipping Line: {}", l.getId()); continue; } - + // It is better to model branches as -normal- lines because it is impossible to open dissymmetrical branches and to do short-circuit on them + // Therefore, normal lines are created: + // - If the G and B are the same on each side of the line, even if the G are not 0 + // - If the B are not the same but the G are 0 + // The code could be extended to handle the case where the B are not the same and the G are not the same ConnectionBus bus1 = ConnectionBus.fromTerminal(l.getTerminal1(), config, fakeNodes); ConnectionBus bus2 = ConnectionBus.fromTerminal(l.getTerminal2(), config, fakeNodes); - // if the admittance are the same in the both side of PI line model - if (Math.abs(l.getG1() - l.getG2()) < G_EPSILON && Math.abs(l.getB1() - l.getB2()) < B_EPSILON) { - //...create a simple line + if (Math.abs(l.getG1() - l.getG2()) < G_EPSILON + && (Math.abs(l.getB1() - l.getB2()) < B_EPSILON + || (Math.abs(l.getG1()) < G_EPSILON && Math.abs(l.getG2()) < G_EPSILON))) { + ConnectionBus bNode = null; + float b; + float diffB = 0f; + float g = (l.getG1() + l.getG2()) / 2.0f; + float vNom = 0f; + if (l.getB1() < l.getB2() - B_EPSILON) { + bNode = bus2; + b = l.getB1(); + diffB = l.getB2() - l.getB1(); + vNom = l.getTerminal2().getVoltageLevel().getNominalV(); + } else if (l.getB2() < l.getB1() - B_EPSILON) { + bNode = bus1; + b = l.getB2(); + diffB = l.getB1() - l.getB2(); + vNom = l.getTerminal1().getVoltageLevel().getNominalV(); + } else { + b = (l.getB1() + l.getB2()) / 2.0f; + } + esgNetwork.addLine(createLine(l.getId(), bus1, bus2, l.getTerminal1().getVoltageLevel().getNominalV(), - l.getR(), l.getX(), l.getG1(), l.getB1(), parameters)); + l.getR(), l.getX(), g, b, parameters)); + + if (bNode != null) { + //create a dummy shunt attached to bNode + String fictionalShuntId = "FKSH" + l.getId(); + addToDictionary(fictionalShuntId, dictionary, EurostagNamingStrategy.NameType.BANK); + + int ieleba = 1; + float plosba = 0.f; + float rcapba = vNom * vNom * diffB; + int imaxba = 1; + EsgCapacitorOrReactorBank.RegulatingMode xregba = EsgCapacitorOrReactorBank.RegulatingMode.NOT_REGULATING; + + esgNetwork.addCapacitorsOrReactorBanks(new EsgCapacitorOrReactorBank(new Esg8charName(dictionary.getEsgId(fictionalShuntId)), + new Esg8charName(dictionary.getEsgId(bNode.getId())), + ieleba, plosba, rcapba, imaxba, xregba)); + } } else { EsgBranchConnectionStatus status = getStatus(bus1, bus2); if (status.equals(EsgBranchConnectionStatus.CLOSED_AT_BOTH_SIDE)) { @@ -233,16 +276,13 @@ private void createLines(EsgNetwork esgNetwork, EsgGeneralParameters parameters) private EsgDetailedTwoWindingTransformer.Tap createTap(TwoWindingsTransformer twt, int iplo, float rho, float dr, float dx, float dephas, float rate, EsgGeneralParameters parameters) { float nomiU2 = twt.getTerminal2().getVoltageLevel().getNominalV(); - float rhoI = twt.getRatedU2() / twt.getRatedU1() * rho; - float uno1 = nomiU2 / rhoI; + float uno1 = nomiU2 / rho; float uno2 = nomiU2; - float r = twt.getR() * (1 + dr / 100.0f); - float x = twt.getX() * (1 + dx / 100.0f); //...mTrans.getR() = Get the nominal series resistance specified in Ω at the secondary voltage side. float zb2 = (float) (Math.pow(nomiU2, 2) / parameters.getSnref()); - float rpu2 = r / zb2; //...total line resistance [p.u.](Base snref) - float xpu2 = x / zb2; //...total line reactance [p.u.](Base snref) + float rpu2 = dr / zb2; //...total line resistance [p.u.](Base snref) + float xpu2 = dx / zb2; //...total line reactance [p.u.](Base snref) //...leakage impedance [%] (base rate) float ucc; @@ -286,6 +326,74 @@ private void createAdditionalBank(EsgNetwork esgNetwork, TwoWindingsTransformer } + private float getRtcRho1(TwoWindingsTransformer twt, int p) { + float rho1 = twt.getRatedU2() / twt.getRatedU1(); + if (twt.getRatioTapChanger() != null) { + rho1 *= twt.getRatioTapChanger().getStep(p).getRho(); + } + if (twt.getPhaseTapChanger() != null) { + rho1 *= twt.getPhaseTapChanger().getCurrentStep().getRho(); + } + return rho1; + } + + private float getPtcRho1(TwoWindingsTransformer twt, int p) { + float rho1 = twt.getRatedU2() / twt.getRatedU1(); + if (twt.getRatioTapChanger() != null) { + rho1 *= twt.getRatioTapChanger().getCurrentStep().getRho(); + } + if (twt.getPhaseTapChanger() != null) { + rho1 *= twt.getPhaseTapChanger().getStep(p).getRho(); + } + return rho1; + } + + private float getValue(float initialValue, float rtcStepValue, float ptcStepValue) { + return initialValue * (1 + rtcStepValue / 100) * (1 + ptcStepValue / 100); + } + + private float getRtcR(TwoWindingsTransformer twt, int p) { + return getValue(twt.getR(), + twt.getRatioTapChanger() != null ? twt.getRatioTapChanger().getStep(p).getR() : 0, + twt.getPhaseTapChanger() != null ? twt.getPhaseTapChanger().getCurrentStep().getR() : 0); + } + + private float getPtcR(TwoWindingsTransformer twt, int p) { + return getValue(twt.getR(), + twt.getRatioTapChanger() != null ? twt.getRatioTapChanger().getCurrentStep().getR() : 0, + twt.getPhaseTapChanger() != null ? twt.getPhaseTapChanger().getStep(p).getR() : 0); + } + + private float getRtcX(TwoWindingsTransformer twt, int p) { + return getValue(twt.getX(), + twt.getRatioTapChanger() != null ? twt.getRatioTapChanger().getStep(p).getX() : 0, + twt.getPhaseTapChanger() != null ? twt.getPhaseTapChanger().getCurrentStep().getX() : 0); + } + + private float getPtcX(TwoWindingsTransformer twt, int p) { + return getValue(twt.getX(), + twt.getRatioTapChanger() != null ? twt.getRatioTapChanger().getCurrentStep().getX() : 0, + twt.getPhaseTapChanger() != null ? twt.getPhaseTapChanger().getStep(p).getX() : 0); + } + + private float getR(TwoWindingsTransformer twt) { + return getValue(twt.getR(), + twt.getRatioTapChanger() != null ? twt.getRatioTapChanger().getCurrentStep().getR() : 0, + twt.getPhaseTapChanger() != null ? twt.getPhaseTapChanger().getCurrentStep().getR() : 0); + } + + private float getG1(TwoWindingsTransformer twt) { + return getValue(config.isSpecificCompatibility() ? twt.getG() / 2 : twt.getG(), + twt.getRatioTapChanger() != null ? twt.getRatioTapChanger().getCurrentStep().getG() : 0, + twt.getPhaseTapChanger() != null ? twt.getPhaseTapChanger().getCurrentStep().getG() : 0); + } + + private float getB1(TwoWindingsTransformer twt) { + return getValue(config.isSpecificCompatibility() ? twt.getB() / 2 : twt.getB(), + twt.getRatioTapChanger() != null ? twt.getRatioTapChanger().getCurrentStep().getB() : 0, + twt.getPhaseTapChanger() != null ? twt.getPhaseTapChanger().getCurrentStep().getB() : 0); + } + private void createTransformers(EsgNetwork esgNetwork, EsgGeneralParameters parameters) { Set additionalBanksIds = new HashSet<>(); @@ -329,13 +437,16 @@ private void createTransformers(EsgNetwork esgNetwork, EsgGeneralParameters para EsgDetailedTwoWindingTransformer.RegulatingMode regulatingMode = EsgDetailedTwoWindingTransformer.RegulatingMode.NOT_REGULATING; Esg8charName zbusr = null; //...regulated node name (if empty, no tap change) float voltr = Float.NaN; - int ktpnom; //...nominal tap number is not available in IIDM. Take th median plot by default - int ktap8; //...initial tap position (tap number) (Ex: 10) + int ktpnom = 1; //...nominal tap number is not available in IIDM. Take th median plot by default + int ktap8 = 1; //...initial tap position (tap number) (Ex: 10) List taps = new ArrayList<>(); RatioTapChanger rtc = twt.getRatioTapChanger(); PhaseTapChanger ptc = twt.getPhaseTapChanger(); - if (rtc != null && ptc == null) { + if ((rtc != null && ptc == null) || (rtc != null && ptc != null && rtc.isRegulating() && !ptc.isRegulating())) { + if (rtc != null && ptc != null) { + LOGGER.warn("both ptc and rtc exist on two winding transformer {}. Only the rtc is kept because it is regulating.", twt.getId()); + } if (rtc.isRegulating()) { ConnectionBus regulatingBus = ConnectionBus.fromTerminal(rtc.getRegulationTerminal(), config, null); if (regulatingBus.getId() != null) { @@ -348,10 +459,14 @@ private void createTransformers(EsgNetwork esgNetwork, EsgGeneralParameters para ktpnom = rtc.getStepCount() / 2 + 1; for (int p = rtc.getLowTapPosition(); p <= rtc.getHighTapPosition(); p++) { int iplo = p - rtc.getLowTapPosition() + 1; - taps.add(createTap(twt, iplo, rtc.getStep(p).getRho(), rtc.getStep(p).getR(), rtc.getStep(p).getX(), 0f, rate, parameters)); + taps.add(createTap(twt, iplo, getRtcRho1(twt, p), getRtcR(twt, p), getRtcX(twt, p), 0f, rate, parameters)); + } + + } else if (ptc != null || rtc != null) { + if (rtc != null && ptc != null) { + LOGGER.warn("both ptc and rtc exist on two winding transformer {}. Only the ptc is kept.", twt.getId()); } - } else if (ptc != null && rtc == null) { if (ptc.getRegulationMode() == PhaseTapChanger.RegulationMode.CURRENT_LIMITER && ptc.isRegulating()) { String regulbus = EchUtil.getBus(ptc.getRegulationTerminal(), config).getId(); if (regulbus.equals(bus1.getId())) { @@ -368,32 +483,25 @@ private void createTransformers(EsgNetwork esgNetwork, EsgGeneralParameters para ktpnom = ptc.getStepCount() / 2 + 1; for (int p = ptc.getLowTapPosition(); p <= ptc.getHighTapPosition(); p++) { int iplo = p - ptc.getLowTapPosition() + 1; - taps.add(createTap(twt, iplo, ptc.getStep(p).getRho(), ptc.getStep(p).getR(), ptc.getStep(p).getX(), ptc.getStep(p).getAlpha(), rate, parameters)); + taps.add(createTap(twt, iplo, getPtcRho1(twt, p), getPtcR(twt, p), getPtcX(twt, p), ptc.getStep(p).getAlpha(), rate, parameters)); } } else if (rtc == null && ptc == null) { - ktap8 = 1; - ktpnom = 1; - taps.add(createTap(twt, 1, 1f, 0f, 0f, 0f, rate, parameters)); - } else { - throw new RuntimeException("Transformer " + twt.getId() + " with voltage and phase tap changer not yet supported"); + taps.add(createTap(twt, 1, twt.getRatedU2() / twt.getRatedU1(), twt.getR(), twt.getX(), 0f, rate, parameters)); } // trick to handle the fact that Eurostag model allows only the impedance to change and not the resistance. // As an approximation, the resistance is fixed to the value it has for the initial step, // but discrepancies will occur if the step is changed. if ((ptc != null) || (rtc != null)) { - float dr = (rtc != null) ? rtc.getStep(rtc.getTapPosition()).getR() : ptc.getStep(ptc.getTapPosition()).getR(); - float tapAdjustedR = twt.getR() * (1 + dr / 100.0f); + float tapAdjustedR = getR(twt); float rpu2Adjusted = (tapAdjustedR * parameters.getSnref()) / nomiU2 / nomiU2; pcu = rpu2Adjusted * rate * 100f / parameters.getSnref(); - float dg = (rtc != null) ? rtc.getStep(rtc.getTapPosition()).getG() : ptc.getStep(ptc.getTapPosition()).getG(); - float tapAdjustedG = twt.getG() * (1 + dg / 100.0f); + float tapAdjustedG = getG1(twt); float gpu2Adjusted = (tapAdjustedG / parameters.getSnref()) * nomiU2 * nomiU2; pfer = 10000f * ((float) Math.sqrt(gpu2Adjusted) / rate) * (parameters.getSnref() / 100f); - float db = (rtc != null) ? rtc.getStep(rtc.getTapPosition()).getB() : ptc.getStep(ptc.getTapPosition()).getB(); - float tapAdjustedB = twt.getB() * (1 + db / 100.0f); + float tapAdjustedB = getB1(twt); float bpu2Adjusted = (tapAdjustedB / parameters.getSnref()) * nomiU2 * nomiU2; modgb = (float) Math.sqrt(Math.pow(gpu2Adjusted, 2.f) + Math.pow(bpu2Adjusted, 2.f)); cmagn = 10000 * (modgb / rate) * (parameters.getSnref() / 100f); @@ -495,14 +603,21 @@ private void createGenerators(EsgNetwork esgNetwork) { boolean isQminQmaxInverted = g.getReactiveLimits().getMinQ(pgen) > g.getReactiveLimits().getMaxQ(pgen); if (isQminQmaxInverted) { LOGGER.warn("inverted qmin {} and qmax {} values for generator {}", g.getReactiveLimits().getMinQ(pgen), g.getReactiveLimits().getMaxQ(pgen), g.getId()); + qgen = -g.getTerminal().getQ(); + } + boolean isVoltageRegulatorOn = g.isVoltageRegulatorOn(); + // Exception for out of bound regulating generators + if (config.isSpecificCompatibility() && (g.getTargetP() < 0.0001) && (g.getMinP() > 0.0001)) { + isVoltageRegulatorOn = false; + LOGGER.warn("out of bound regulating generator {}, targetP {}, minP {} : turn off its voltage regulation", g.getId(), g.getTargetP(), g.getMinP()); } // in case qmin and qmax are inverted, take out the unit from the voltage regulation if it has a target Q // and open widely the Q interval float qgmin = (config.isNoGeneratorMinMaxQ() || isQminQmaxInverted) ? -9999 : g.getReactiveLimits().getMinQ(pgen); float qgmax = (config.isNoGeneratorMinMaxQ() || isQminQmaxInverted) ? 9999 : g.getReactiveLimits().getMaxQ(pgen); EsgRegulatingMode mode = (isQminQmaxInverted && !Float.isNaN(qgen)) ? EsgRegulatingMode.NOT_REGULATING : - (g.isVoltageRegulatorOn() && g.getTargetV() >= 0.1 ? EsgRegulatingMode.REGULATING : EsgRegulatingMode.NOT_REGULATING); - float vregge = (isQminQmaxInverted && !Float.isNaN(qgen)) ? Float.NaN : (g.isVoltageRegulatorOn() ? g.getTargetV() : Float.NaN); + (isVoltageRegulatorOn && g.getTargetV() >= 0.1 ? EsgRegulatingMode.REGULATING : EsgRegulatingMode.NOT_REGULATING); + float vregge = (isQminQmaxInverted && !Float.isNaN(qgen)) ? Float.NaN : (isVoltageRegulatorOn ? g.getTargetV() : Float.NaN); float qgensh = 1.f; //fails, when noSwitch is true !! @@ -562,7 +677,7 @@ private void createStaticVarCompensators(EsgNetwork esgNetwork) { if (!config.isSvcAsFixedInjectionInLF()) { binit = svc.getReactivePowerSetPoint(); } else { - binit = -svc.getTerminal().getQ(); + binit = svc.getTerminal().getQ(); Bus svcBus = EchUtil.getBus(svc.getTerminal(), config); if ((svcBus != null) && (Math.abs(svcBus.getV()) > 0.0f)) { binit = binit * (float) Math.pow(vlNomVoltage / svcBus.getV(), 2); @@ -577,6 +692,172 @@ private void createStaticVarCompensators(EsgNetwork esgNetwork) { } } + //add a new couple (iidmId, esgId). EsgId is built from iidmId using a simple cut-name mapping strategy + private String addToDictionary(String iidmId, EurostagDictionary dictionary, EurostagNamingStrategy.NameType nameType) { + if (dictionary.iidmIdExists(iidmId)) { + throw new RuntimeException("iidmId " + iidmId + " already exists in dictionary"); + } + String esgId = iidmId.length() > nameType.getLength() ? iidmId.substring(0, nameType.getLength()) + : Strings.padEnd(iidmId, nameType.getLength(), ' '); + int counter = 0; + while (dictionary.esgIdExists(esgId)) { + String counterStr = Integer.toString(counter++); + if (counterStr.length() > nameType.getLength()) { + throw new RuntimeException("Renaming fatal error " + iidmId + " -> " + esgId); + } + esgId = esgId.substring(0, nameType.getLength() - counterStr.length()) + counterStr; + } + dictionary.add(iidmId, esgId); + return esgId; + } + + protected float zeroIfNanOrValue(float value) { + return Float.isNaN(value) ? 0 : value; + } + + protected EsgACDCVscConverter createACDCVscConverter(VscConverterStation vscConv, HvdcLine hline, Esg8charName vscConvDcName) { + Objects.requireNonNull(vscConv); + Objects.requireNonNull(hline, "no hvdc line connected to VscConverterStation " + vscConv.getId()); + boolean isPmode = EchUtil.isPMode(vscConv, hline); + Esg8charName znamsvc = new Esg8charName(dictionary.getEsgId(vscConv.getId())); // converter station ID + Esg8charName receivingNodeDcName = new Esg8charName("GROUND"); // receiving DC node name; always GROUND + Bus vscConvBus = EchUtil.getBus(vscConv.getTerminal(), config); + if (vscConvBus == null) { + throw new RuntimeException("VSCConverter " + vscConv.getId() + " not connected to a bus and not connectable"); + } + Esg8charName acNode = dictionary.iidmIdExists(vscConvBus.getId()) ? new Esg8charName(dictionary.getEsgId(vscConvBus.getId())) + : null; + if (acNode == null) { + throw new RuntimeException("VSCConverter " + vscConv.getId() + " : acNode mapping not found"); + } + EsgACDCVscConverter.ConverterState xstate = EsgACDCVscConverter.ConverterState.ON; // converter state ' ' ON; 'S' OFF + EsgACDCVscConverter.DCControlMode xregl = isPmode ? EsgACDCVscConverter.DCControlMode.AC_ACTIVE_POWER : EsgACDCVscConverter.DCControlMode.DC_VOLTAGE; // DC control mode 'P' AC_ACTIVE_POWER; 'V' DC_VOLTAGE + //AC control mode assumed to be "AC reactive power"(Q) + EsgACDCVscConverter.ACControlMode xoper = EsgACDCVscConverter.ACControlMode.AC_REACTIVE_POWER; // AC control mode 'V' AC_VOLTAGE; 'Q' AC_REACTIVE_POWER; 'A' AC_POWER_FACTOR + float rrdc = 0; // resistance [Ohms] + float rxdc = 16; // reactance [Ohms] + + float activeSetPoint = zeroIfNanOrValue(hline.getActivePowerSetpoint()); // AC active power setpoint [MW]. Only if DC control mode is 'P' + //subtracts losses on the P side (even if the station in context is V) + float pac = activeSetPoint - Math.abs(activeSetPoint * EchUtil.getPStation(hline).getLossFactor() / 100.0f); + pac = isPmode ? pac : -pac; //change sign in case of V mode side + // multiplying the line's nominalV by 2 corresponds to the fact that iIDM refers to the cable-ground voltage + // while Eurostag regulations to the cable-cable voltage + float pvd = EchUtil.getHvdcLineDcVoltage(hline); // DC voltage setpoint [MW]. Only if DC control mode is 'V' + float pre = -vscConv.getReactivePowerSetpoint(); // AC reactive power setpoint [Mvar]. Only if AC control mode is 'Q' + if ((Float.isNaN(pre)) || (vscConv.isVoltageRegulatorOn())) { + float terminalQ = vscConv.getTerminal().getQ(); + if (Float.isNaN(terminalQ)) { + pre = zeroIfNanOrValue(pre); + } else { + pre = terminalQ; + } + } + float pco = Float.NaN; // AC power factor setpoint. Only if AC control mode is 'A' + float qvscsh = 1; // Reactive sharing cofficient [%]. Only if AC control mode is 'V' + float pvscmin = -hline.getMaxP(); // Minimum AC active power [MW] + float pvscmax = hline.getMaxP(); // Maximum AC active power [MW] + float qvscmin = vscConv.getReactiveLimits().getMinQ(0); // Minimum reactive power injected on AC node [kV] + float qvscmax = vscConv.getReactiveLimits().getMaxQ(0); // Maximum reactive power injected on AC node [kV] + // iIDM vscConv.getLossFactor() is in % of the MW. As it is, not suitable for vsb0, which is fixed in MW + // for now, set vsb0, vsb1,vsb2 to 0 + float vsb0 = 0; // Losses coefficient Beta0 [MW] + float vsb1 = 0; // Losses coefficient Beta1 [kW] + float vsb2 = 0; // Losses coefficient Beta2 [Ohms] + + Bus connectedBus = vscConv.getTerminal().getBusBreakerView().getConnectableBus(); + if (connectedBus == null) { + connectedBus = vscConv.getTerminal().getBusView().getConnectableBus(); + if (connectedBus == null) { + throw new RuntimeException("VSCConverter " + vscConv.getId() + " : connected bus not found!"); + } + } + float mvm = connectedBus.getV() / connectedBus.getVoltageLevel().getNominalV(); // Initial AC modulated voltage magnitude [p.u.] + float mva = connectedBus.getAngle(); // Initial AC modulated voltage angle [deg] + float pva = connectedBus.getV(); // AC voltage setpoint [kV]. Only if AC control mode is 'V' + + return new EsgACDCVscConverter( + znamsvc, + vscConvDcName, + receivingNodeDcName, + acNode, + xstate, + xregl, + xoper, + rrdc, + rxdc, + pac, + pvd, + pva, + pre, + pco, + qvscsh, + pvscmin, + pvscmax, + qvscmin, + qvscmax, + vsb0, + vsb1, + vsb2, + mvm, + mva); + } + + protected float computeLosses(HvdcLine hvdcLine, HvdcConverterStation convStation, float activeSetPoint) { + float cableLossesEnd = EchUtil.isPMode(convStation, hvdcLine) ? 0.0f : 1.0f; + float ploss = (float) (Math.abs(activeSetPoint * convStation.getLossFactor() / 100.0f) + cableLossesEnd * (hvdcLine.getR() - 0.25f) * Math.pow(activeSetPoint / hvdcLine.getNominalV(), 2)); //Eurostag model requires a fixed resistance of 1 ohm at 640 kV quivalent to 0.25 ohm at 320 kV + return ploss; + } + + protected float computeLosses(HvdcLine hvdcLine, HvdcConverterStation convStation) { + float activeSetPoint = zeroIfNanOrValue(hvdcLine.getActivePowerSetpoint()); + return computeLosses(hvdcLine, convStation, activeSetPoint); + } + + private EsgLoad createConverterStationAdditionalLoad(HvdcLine hvdcLine, HvdcConverterStation convStation) { + float ploss = computeLosses(hvdcLine, convStation); + ConnectionBus rectConvBus = ConnectionBus.fromTerminal(convStation.getTerminal(), config, fakeNodes); + String fictionalLoadId = "fict_" + convStation.getId(); + addToDictionary(fictionalLoadId, dictionary, EurostagNamingStrategy.NameType.LOAD); + return createLoad(rectConvBus, fictionalLoadId, ploss, 0); + } + + private void createACDCVscConverters(EsgNetwork esgNetwork) { + //creates 2 DC nodes, for each hvdc line (one node per converter station) + for (HvdcLine hvdcLine : Identifiables.sort(network.getHvdcLines())) { + // skip lines with converter stations not in the main connected component + if (config.isExportMainCCOnly() && (!EchUtil.isInMainCc(hvdcLine.getConverterStation1(), config.isNoSwitch()) || !EchUtil.isInMainCc(hvdcLine.getConverterStation2(), config.isNoSwitch()))) { + LOGGER.warn("skipped HVDC line {}: at least one converter station is not in main component", hvdcLine.getId()); + continue; + } + HvdcConverterStation convStation1 = hvdcLine.getConverterStation1(); + HvdcConverterStation convStation2 = hvdcLine.getConverterStation2(); + + //create two dc nodes, one for each conv. station + Esg8charName hvdcNodeName1 = new Esg8charName(addToDictionary("DC_" + convStation1.getId(), dictionary, EurostagNamingStrategy.NameType.NODE)); + Esg8charName hvdcNodeName2 = new Esg8charName(addToDictionary("DC_" + convStation2.getId(), dictionary, EurostagNamingStrategy.NameType.NODE)); + float dcVoltage = EchUtil.getHvdcLineDcVoltage(hvdcLine); + esgNetwork.addDCNode(new EsgDCNode(new Esg2charName("DC"), hvdcNodeName1, dcVoltage, 1)); + esgNetwork.addDCNode(new EsgDCNode(new Esg2charName("DC"), hvdcNodeName2, dcVoltage, 1)); + + //create a dc link, representing the hvdc line + //Eurostag model requires a resistance of 1 ohm (not hvdcLine.getR()) + float r = 1.0f; + esgNetwork.addDCLink(new EsgDCLink(hvdcNodeName1, hvdcNodeName2, '1', r, EsgDCLink.LinkStatus.ON)); + + //create the two converter stations + EsgACDCVscConverter esgConv1 = createACDCVscConverter(network.getVscConverterStation(convStation1.getId()), hvdcLine, hvdcNodeName1); + EsgACDCVscConverter esgConv2 = createACDCVscConverter(network.getVscConverterStation(convStation2.getId()), hvdcLine, hvdcNodeName2); + esgNetwork.addACDCVscConverter(esgConv1); + esgNetwork.addACDCVscConverter(esgConv2); + + //Create one load on the node to which converters stations are connected + esgNetwork.addLoad(createConverterStationAdditionalLoad(hvdcLine, convStation1)); + esgNetwork.addLoad(createConverterStationAdditionalLoad(hvdcLine, convStation2)); + } + } + + @Override public EsgNetwork createNetwork(EsgGeneralParameters parameters) { EsgNetwork esgNetwork = new EsgNetwork(); @@ -605,6 +886,9 @@ public EsgNetwork createNetwork(EsgGeneralParameters parameters) { // static VAR compensators createStaticVarCompensators(esgNetwork); + // ACDC VSC Converters + createACDCVscConverters(esgNetwork); + // nodes createNodes(esgNetwork); diff --git a/eurostag-ech-export/src/main/java/eu/itesla_project/iidm/eurostag/export/EurostagEchExporter.java b/eurostag-ech-export/src/main/java/eu/itesla_project/iidm/eurostag/export/EurostagEchExporter.java new file mode 100644 index 00000000..c7024429 --- /dev/null +++ b/eurostag-ech-export/src/main/java/eu/itesla_project/iidm/eurostag/export/EurostagEchExporter.java @@ -0,0 +1,23 @@ +/** + * Copyright (c) 2017, 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/. + */ +package eu.itesla_project.iidm.eurostag.export; + +import eu.itesla_project.eurostag.network.EsgGeneralParameters; +import eu.itesla_project.eurostag.network.EsgNetwork; +import eu.itesla_project.eurostag.network.EsgSpecialParameters; + +import java.io.IOException; +import java.nio.file.Path; + +/** + * @author Christian Biasuzzi + */ +public interface EurostagEchExporter { + EsgNetwork createNetwork(EsgGeneralParameters parameters); + + void write(Path file, EsgGeneralParameters parameters, EsgSpecialParameters specialParameters) throws IOException; +} diff --git a/eurostag-ech-export/src/main/java/eu/itesla_project/iidm/eurostag/export/EurostagEchExporterFactory.java b/eurostag-ech-export/src/main/java/eu/itesla_project/iidm/eurostag/export/EurostagEchExporterFactory.java new file mode 100644 index 00000000..732eb330 --- /dev/null +++ b/eurostag-ech-export/src/main/java/eu/itesla_project/iidm/eurostag/export/EurostagEchExporterFactory.java @@ -0,0 +1,16 @@ +/** + * Copyright (c) 2017, 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/. + */ +package eu.itesla_project.iidm.eurostag.export; + +import com.powsybl.iidm.network.Network; + +/** + * @author Christian Biasuzzi + */ +public interface EurostagEchExporterFactory { + EurostagEchExporter createEchExporter(Network network, EurostagEchExportConfig exportConfig, BranchParallelIndexes parallelIndexes, EurostagDictionary dictionary, EurostagFakeNodes fakeNodes); +} diff --git a/eurostag-ech-export/src/main/java/eu/itesla_project/iidm/eurostag/export/EurostagEchExporterFactoryImpl.java b/eurostag-ech-export/src/main/java/eu/itesla_project/iidm/eurostag/export/EurostagEchExporterFactoryImpl.java new file mode 100644 index 00000000..967a5e4a --- /dev/null +++ b/eurostag-ech-export/src/main/java/eu/itesla_project/iidm/eurostag/export/EurostagEchExporterFactoryImpl.java @@ -0,0 +1,20 @@ +/** + * Copyright (c) 2017, 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/. + */ +package eu.itesla_project.iidm.eurostag.export; + +import com.powsybl.iidm.network.Network; + +/** + * @author Christian Biasuzzi + */ +public class EurostagEchExporterFactoryImpl implements EurostagEchExporterFactory { + + @Override + public EurostagEchExporter createEchExporter(Network network, EurostagEchExportConfig exportConfig, BranchParallelIndexes parallelIndexes, EurostagDictionary dictionary, EurostagFakeNodes fakeNodes) { + return new EurostagEchExport(network, exportConfig, parallelIndexes, dictionary, fakeNodes); + } +} diff --git a/eurostag-ech-export/src/main/java/eu/itesla_project/iidm/eurostag/export/EurostagNamingStrategy.java b/eurostag-ech-export/src/main/java/eu/itesla_project/iidm/eurostag/export/EurostagNamingStrategy.java index 5ed3a393..579a82c0 100644 --- a/eurostag-ech-export/src/main/java/eu/itesla_project/iidm/eurostag/export/EurostagNamingStrategy.java +++ b/eurostag-ech-export/src/main/java/eu/itesla_project/iidm/eurostag/export/EurostagNamingStrategy.java @@ -20,7 +20,8 @@ enum NameType { GENERATOR(8), LOAD(8), BANK(8), - SVC(8); + SVC(8), + VSC(8); int length; diff --git a/eurostag-ech-export/src/test/java/eu/itesla_project/iidm/eurostag/export/CheckEurostagEchExportConfigTest.java b/eurostag-ech-export/src/test/java/eu/itesla_project/iidm/eurostag/export/CheckEurostagEchExporterConfigTest.java similarity index 77% rename from eurostag-ech-export/src/test/java/eu/itesla_project/iidm/eurostag/export/CheckEurostagEchExportConfigTest.java rename to eurostag-ech-export/src/test/java/eu/itesla_project/iidm/eurostag/export/CheckEurostagEchExporterConfigTest.java index 1ab2f2b7..795d69ef 100644 --- a/eurostag-ech-export/src/test/java/eu/itesla_project/iidm/eurostag/export/CheckEurostagEchExportConfigTest.java +++ b/eurostag-ech-export/src/test/java/eu/itesla_project/iidm/eurostag/export/CheckEurostagEchExporterConfigTest.java @@ -22,7 +22,7 @@ /** * @author Christian Biasuzzi */ -public class CheckEurostagEchExportConfigTest { +public class CheckEurostagEchExporterConfigTest { InMemoryPlatformConfig platformConfig; FileSystem fileSystem; @@ -39,10 +39,11 @@ public void tearDown() throws Exception { fileSystem.close(); } - private EurostagEchExportConfig getConfigFromFile(FileSystem fileSystem, boolean specificCompatibility) { + private EurostagEchExportConfig getConfigFromFile(FileSystem fileSystem, boolean specificCompatibility, boolean mainCcOnly) { InMemoryPlatformConfig platformConfig = new InMemoryPlatformConfig(fileSystem); MapModuleConfig moduleConfig = platformConfig.createModuleConfig("eurostag-ech-export"); moduleConfig.setStringProperty("svcAsFixedInjectionInLF", "false"); + moduleConfig.setStringProperty("exportMainCCOnly", Boolean.toString(mainCcOnly)); moduleConfig = platformConfig.createModuleConfig("load-flow-default-parameters"); moduleConfig.setStringProperty("specificCompatibility", Boolean.toString(specificCompatibility)); @@ -59,15 +60,26 @@ public void testConfig() throws IOException { @Test public void testConfigFromFile() throws IOException { - EurostagEchExportConfig config = getConfigFromFile(fileSystem, false); + EurostagEchExportConfig config = getConfigFromFile(fileSystem, false, false); assertEquals(false, config.isSvcAsFixedInjectionInLF()); } @Test public void testConfigSpecificCompatibility() throws IOException { - EurostagEchExportConfig config = getConfigFromFile(fileSystem, true); + EurostagEchExportConfig config = getConfigFromFile(fileSystem, true, false); assertEquals(true, config.isSvcAsFixedInjectionInLF()); } + @Test + public void testConfigExportNoMainCC() throws IOException { + EurostagEchExportConfig config = getConfigFromFile(fileSystem, true, false); + assertEquals(false, config.isExportMainCCOnly()); + } + + @Test + public void testConfigExportMainCC() throws IOException { + EurostagEchExportConfig config = getConfigFromFile(fileSystem, true, true); + assertEquals(true, config.isExportMainCCOnly()); + } } diff --git a/eurostag-ech-export/src/test/java/eu/itesla_project/iidm/eurostag/export/EchUtilsTest.java b/eurostag-ech-export/src/test/java/eu/itesla_project/iidm/eurostag/export/EchUtilsTest.java new file mode 100644 index 00000000..adb48431 --- /dev/null +++ b/eurostag-ech-export/src/test/java/eu/itesla_project/iidm/eurostag/export/EchUtilsTest.java @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2018, 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/. + */ +package eu.itesla_project.iidm.eurostag.export; + +import com.google.common.collect.Lists; +import com.google.common.jimfs.Configuration; +import com.google.common.jimfs.Jimfs; +import com.powsybl.commons.config.InMemoryPlatformConfig; +import com.powsybl.commons.config.MapModuleConfig; +import com.powsybl.iidm.network.HvdcLine; +import com.powsybl.iidm.network.Network; +import com.powsybl.iidm.network.VscConverterStation; +import com.powsybl.iidm.network.test.EurostagTutorialExample1Factory; +import com.powsybl.iidm.network.test.HvdcTestNetwork; +import org.junit.Before; +import org.junit.Test; + +import java.io.IOException; +import java.nio.file.FileSystem; + +import static org.junit.Assert.*; + + +/** + * @author Christian Biasuzzi + */ +public class EchUtilsTest { + + + private final float delta = 0.0f; + private Network network; + private Network networkHvdc; + + @Before + public void setUp() throws Exception { + network = EurostagTutorialExample1Factory.create(); + networkHvdc = HvdcTestNetwork.createVsc(); + } + + private EurostagEchExportConfig getConfig(boolean noSwitch, boolean exportMainCCOnly) { + FileSystem fileSystem = Jimfs.newFileSystem(Configuration.unix()); + InMemoryPlatformConfig platformConfig = new InMemoryPlatformConfig(fileSystem); + MapModuleConfig moduleConfig = platformConfig.createModuleConfig("eurostag-ech-export"); + moduleConfig.setStringProperty("noSwitch", Boolean.toString(noSwitch)); + moduleConfig.setStringProperty("exportMainCCOnly", Boolean.toString(exportMainCCOnly)); + return EurostagEchExportConfig.load(platformConfig); + } + + private EurostagEchExportConfig getConfig(boolean noSwitch) { + return getConfig(noSwitch, false); + } + + @Test + public void testGetHvdcLineDcVoltage() { + networkHvdc.getHvdcLineStream().forEach(line -> assertEquals(line.getNominalV() * 2.0f, EchUtil.getHvdcLineDcVoltage(line), delta)); + } + + @Test + public void testGetHvdcLineDcVoltageNull() { + try { + EchUtil.getHvdcLineDcVoltage(null); + fail("Expected not null parameter"); + } catch (NullPointerException e) { + } + + } + + @Test + public void testIsImMainCcBus() { + Lists.newArrayList(true, false).stream().forEach(exportMainCCOnly -> { + EchUtil.getBuses(networkHvdc, getConfig(false, exportMainCCOnly)).forEach(bus -> { + assertEquals(EchUtil.isInMainCc(bus), true); + }); + }); + } + + @Test + public void testIsImMainCcGen() { + Lists.newArrayList(true, false).stream().forEach(exportMainCCOnly -> { + network.getGeneratorStream().forEach(gen -> { + assertEquals(EchUtil.isInMainCc(gen, exportMainCCOnly), true); + }); + }); + } + + @Test + public void testIsImMainCcLine() { + Lists.newArrayList(true, false).stream().forEach(exportMainCCOnly -> { + network.getLines().forEach(line -> { + assertEquals(EchUtil.isInMainCc(line, exportMainCCOnly), true); + }); + }); + } + + @Test + public void testIsPmode() throws IOException { + HvdcLine hline = networkHvdc.getHvdcLine("L"); + assertTrue(EchUtil.isPMode(networkHvdc.getVscConverterStation("C2"), hline)); + assertFalse(EchUtil.isPMode(networkHvdc.getVscConverterStation("C1"), hline)); + } + + @Test + public void testGetPVstation() throws IOException { + HvdcLine hline = networkHvdc.getHvdcLine("L"); + assertTrue(EchUtil.getPStation(hline).getId().equals(networkHvdc.getVscConverterStation("C2").getId())); + assertTrue(EchUtil.getVStation(hline).getId().equals(networkHvdc.getVscConverterStation("C1").getId())); + } + +} \ No newline at end of file diff --git a/eurostag-ech-export/src/test/java/eu/itesla_project/iidm/eurostag/export/EurostagEchExportTest.java b/eurostag-ech-export/src/test/java/eu/itesla_project/iidm/eurostag/export/EurostagEchExportTest.java index c3d2ea0d..6655bcda 100644 --- a/eurostag-ech-export/src/test/java/eu/itesla_project/iidm/eurostag/export/EurostagEchExportTest.java +++ b/eurostag-ech-export/src/test/java/eu/itesla_project/iidm/eurostag/export/EurostagEchExportTest.java @@ -7,11 +7,12 @@ package eu.itesla_project.iidm.eurostag.export; import com.google.common.io.CharStreams; -import eu.itesla_project.eurostag.network.EsgGeneralParameters; -import eu.itesla_project.eurostag.network.EsgSpecialParameters; -import com.powsybl.iidm.network.Network; +import com.powsybl.iidm.network.*; import com.powsybl.iidm.network.test.EurostagTutorialExample1Factory; +import com.powsybl.iidm.network.test.HvdcTestNetwork; import com.powsybl.iidm.network.test.SvcTestCaseFactory; +import eu.itesla_project.eurostag.network.EsgGeneralParameters; +import eu.itesla_project.eurostag.network.EsgSpecialParameters; import org.joda.time.LocalDate; import org.junit.Test; @@ -27,15 +28,20 @@ */ public class EurostagEchExportTest { - private void test(Network network, String reference, LocalDate editDate, EsgSpecialParameters specialParameters) throws IOException { + private void test(Network network, String reference, LocalDate editDate, EsgSpecialParameters specialParameters, EurostagEchExportConfig config) throws IOException { StringWriter writer = new StringWriter(); EsgGeneralParameters parameters = new EsgGeneralParameters(); parameters.setEditDate(editDate); - new EurostagEchExport(network).write(writer, parameters, specialParameters); + new EurostagEchExport(network, config).write(writer, parameters, specialParameters); writer.close(); assertEquals(CharStreams.toString(new InputStreamReader(getClass().getResourceAsStream(reference), StandardCharsets.UTF_8)), writer.toString()); } + private void test(Network network, String reference, LocalDate editDate, EsgSpecialParameters specialParameters) throws IOException { + test(network, reference, editDate, specialParameters, new EurostagEchExportConfig()); + } + + @Test public void test() throws IOException { Network network = EurostagTutorialExample1Factory.create(); @@ -55,4 +61,123 @@ public void testSVC() throws IOException { EsgSpecialParameters specialParameters = new EsgSpecialParameters(); test(network, "/eurostag-svc-test.ech", LocalDate.parse("2016-01-01"), specialParameters); } + + @Test + public void testHVDC() throws IOException { + Network network = HvdcTestNetwork.createVsc(); + network.getVoltageLevelStream().findFirst().orElse(null) + .newGenerator().setId("G1") + .setConnectableBus("B1") + .setBus("B1") + .setVoltageRegulatorOn(true) + .setTargetP(100.0F) + .setTargetV(400.0F) + .setMinP(50.0F) + .setMaxP(150.0F) + .add(); + EsgSpecialParameters specialParameters = new EsgSpecialParameters(); + test(network, "/eurostag-hvdc-test.ech", LocalDate.parse("2016-01-01"), specialParameters); + } + + private void addLine(Network network, VoltageLevel vlhv1, VoltageLevel vlhv2, String idLine, float g1, float g2, float b1, float b2) { + Bus nhv1 = vlhv1.getBusBreakerView().newBus() + .setId("N1" + idLine) + .add(); + Bus nhv2 = vlhv2.getBusBreakerView().newBus() + .setId("N2" + idLine) + .add(); + network.newLine() + .setId(idLine) + .setVoltageLevel1(vlhv1.getId()) + .setBus1(nhv1.getId()) + .setConnectableBus1(nhv1.getId()) + .setVoltageLevel2(vlhv2.getId()) + .setBus2(nhv2.getId()) + .setConnectableBus2(nhv2.getId()) + .setR(3) + .setX(33) + .setG1(g1) + .setB1(b1) + .setG2(g2) + .setB2(b2) + .add(); + } + + @Test + public void testLines() throws IOException { + Network network = EurostagTutorialExample1Factory.create(); + VoltageLevel vlhv1 = network.getSubstation("P1").newVoltageLevel() + .setId("VL1") + .setNominalV(380) + .setTopologyKind(TopologyKind.BUS_BREAKER) + .add(); + VoltageLevel vlhv2 = network.getSubstation("P2").newVoltageLevel() + .setId("VL2") + .setNominalV(380) + .setTopologyKind(TopologyKind.BUS_BREAKER) + .add(); + + float bTest1 = EurostagEchExport.B_EPSILON * 2f; + + //G and B are the same on each side, G are 0 + addLine(network, vlhv1, vlhv2, "L1", 0f, 0f, 0f, 0f); + addLine(network, vlhv1, vlhv2, "L2", 0f, 0f, bTest1, bTest1); + //B are not the same, G are 0 + addLine(network, vlhv1, vlhv2, "L3", 0f, 0f, bTest1, 0); // dummy shunt expected in the .ech + addLine(network, vlhv1, vlhv2, "L4", 0f, 0f, 0, bTest1); // dummy shunt expected in the .ech + addLine(network, vlhv1, vlhv2, "L5", 0f, 0f, EurostagEchExport.B_EPSILON / 2f, 0); + addLine(network, vlhv1, vlhv2, "L6", 0f, 0f, 0, EurostagEchExport.B_EPSILON / 2f); + //B are not the same, G are not 0 + addLine(network, vlhv1, vlhv2, "L7", 1f, 0f, bTest1, 0); + addLine(network, vlhv1, vlhv2, "L8", 0f, 1f, bTest1, 0); + + EsgSpecialParameters specialParameters = new EsgSpecialParameters(); + test(network, "/eurostag-tutorial-example1_lines.ech", LocalDate.parse("2016-03-01"), specialParameters); + } + + + @Test + public void testCptRpt() throws IOException { + Network network = EurostagTutorialExample1Factory.create(); + TwoWindingsTransformer nhv2Nload = network.getTwoWindingsTransformer("NHV2_NLOAD"); + + //adds a phase tap changer to the NHV2_NLOAD TwoWindingTransformer (it has already a ratio tap changer) + //so that both tap changers adjust rho + nhv2Nload.newPhaseTapChanger() + .setTapPosition(0) + .setRegulationTerminal(nhv2Nload.getTerminal2()) + .setRegulationMode(PhaseTapChanger.RegulationMode.FIXED_TAP) + .setRegulationValue(200) + .beginStep() + .setAlpha(-20f) + .setRho(0.96f) + .setR(0f) + .setX(0f) + .setG(0f) + .setB(0f) + .endStep() + .add(); + + EsgSpecialParameters specialParameters = new EsgSpecialParameters(); + test(network, "/eurostag-tutorial-example1_cptrpt.ech", LocalDate.parse("2016-03-01"), specialParameters); + } + + @Test + public void testVoltageRegulationException() throws IOException { + Network network = EurostagTutorialExample1Factory.create(); + + //voltage regulator exception: if specificCompatibility && (g.getTargetP() < 0.0001) && (g.getMinP() > 0.0001) + // turn off g's voltage regulation + Generator g = network.getGenerator("GEN"); + g.setMinP(300f); + g.setTargetP(0); + + EsgSpecialParameters specialParameters = new EsgSpecialParameters(); + EurostagEchExportConfig exTemp = new EurostagEchExportConfig(); + EurostagEchExportConfig exportConfig= new EurostagEchExportConfig(exTemp.isNoGeneratorMinMaxQ(), exTemp.isNoSwitch(), exTemp.getForbiddenCharactersString(), + exTemp.getForbiddenCharactersReplacement(), exTemp.isSvcAsFixedInjectionInLF(), true, exTemp.isExportMainCCOnly()); + + test(network, "/eurostag-tutorial-example1_vre.ech", LocalDate.parse("2016-03-01"), specialParameters, exportConfig); + } + } \ No newline at end of file diff --git a/eurostag-ech-export/src/test/java/eu/itesla_project/iidm/eurostag/export/EurostagNamingStrategyTest.java b/eurostag-ech-export/src/test/java/eu/itesla_project/iidm/eurostag/export/EurostagNamingStrategyTest.java index 71bb5535..c0554b3f 100644 --- a/eurostag-ech-export/src/test/java/eu/itesla_project/iidm/eurostag/export/EurostagNamingStrategyTest.java +++ b/eurostag-ech-export/src/test/java/eu/itesla_project/iidm/eurostag/export/EurostagNamingStrategyTest.java @@ -63,6 +63,13 @@ private Network createMockNetwork(List genNames) { .thenReturn(Collections.emptyList()); Mockito.when(net.getThreeWindingsTransformers()) .thenReturn(Collections.emptyList()); + Mockito.when(net.getVscConverterStations()) + .thenReturn(Collections.emptyList()); + Mockito.when(net.getHvdcLines()) + .thenReturn(Collections.emptyList()); + Mockito.when(net.getHvdcConverterStations()) + .thenReturn(Collections.emptyList()); + List genList = genNames.stream().map(x -> { Generator g = Mockito.mock(Generator.class); diff --git a/eurostag-ech-export/src/test/resources/eurostag-hvdc-test.ech b/eurostag-ech-export/src/test/resources/eurostag-hvdc-test.ech new file mode 100644 index 00000000..6145c9e4 --- /dev/null +++ b/eurostag-ech-export/src/test/resources/eurostag-hvdc-test.ech @@ -0,0 +1,44 @@ +HEADER 01/01/16 5.1 + +B + +9 1 0 0 1 1 20 0.005 4 100. 1 1 + +SP INPVPQ 2 +SP THMAX 0.100000 +SP EMAXF 0.100000 +SP ZMIN 0.000200 +SP RAMIN 0.800000 +SP RAMAX 1.200000 +SP TOLPLO 0.001000 + +GC hvdctest/InitialState + +AA FA +AA FR +DA DC + +1FAFAKENOD1 380. 1. 0. 0. 0. +1FAFAKENOD2 380. 1. 0. 0. 0. +1FRB1 400. 1. 0. 0. 0. +5 B1 0. +1FRVL2_0 400. 1. 0. 0. 0. +1FRVL2_1 400. 1. 0. 0. 0. + +6 VL2_0 VL2_1 1 0. 0. + +CH fict_C1 Y B1 0. 0. 0.3983 0. 0. 0. 0. 0. +CH fict_C2 Y VL2_1 0. 0. 0.0308 0. 0. 0. 0. 0. + +G G1 Y B1 50. 100. 150. -2147483 21474836 V 400. B1 1. 0. 0. + +DC N DC_C1 DC 800. 1. +DC N DC_C2 DC 800. 1. + +DC L DC_C1 DC_C2 1 1. + +DC V C1 DC_C1 GROUND B1 V Q 0. 16. -279.969 800. 50. 1. +DC V -300. 300. 0. 10. 0. 0. 0. +DC V C2 DC_C2 GROUND VL2_1 P Q 0. 16. 279.9692 800. -123. 1. +DC V -300. 300. 0. 10. 0. 0. 0. + diff --git a/eurostag-ech-export/src/test/resources/eurostag-tutorial-example1_cptrpt.ech b/eurostag-ech-export/src/test/resources/eurostag-tutorial-example1_cptrpt.ech new file mode 100644 index 00000000..bb8344ec --- /dev/null +++ b/eurostag-ech-export/src/test/resources/eurostag-tutorial-example1_cptrpt.ech @@ -0,0 +1,43 @@ +HEADER 01/03/16 5.1 + +B + +9 1 0 0 1 1 20 0.005 4 100. 1 1 + +SP INPVPQ 2 +SP THMAX 0.100000 +SP EMAXF 0.100000 +SP ZMIN 0.000200 +SP RAMIN 0.800000 +SP RAMAX 1.200000 +SP TOLPLO 0.001000 + +GC sim1/InitialState + +AA FA +AA FR + +1FAFAKENOD1 380. 1. 0. 0. 0. +1FAFAKENOD2 380. 1. 0. 0. 0. +1FRNGEN 24. 1. 0. 0. 0. +5 NGEN 0. +1FRNHV1 380. 1. 0. 0. 0. +1FRNHV2 380. 1. 0. 0. 0. +1FRNLOAD 150. 1. 0. 0. 0. + +3 NHV1 NHV2 1 0.002078 0.022853 0. 0.278692 100. 0. 0. +3 NHV1 NHV2 2 0.002078 0.022853 0. 0.278692 100. 0. 0. + +48NGEN NHV1 1 100.0.018462 0. 0. 1. 0. 0. +48 1 1 N +48 1 22.8 380. 0.769231 0. +48NHV2 NLOAD 1 100. 0.021 0. 0. 1. 0. 0. +48 2 2 NLOAD 158. V +48 1 465.066 150. 1.8 0. +48 2 395.3061 150. 1.8 0. +48 3 343.7444 150. 1.8 0. + +CH LOAD Y NLOAD 0. 0. 600. 0. 0. 200. 0. 0. + +G GEN Y NGEN -9999.99 607. 9999.99 -9999.99 301. 9999.99 V 24.5 NGEN 1. 0. 0. + diff --git a/eurostag-ech-export/src/test/resources/eurostag-tutorial-example1_lines.ech b/eurostag-ech-export/src/test/resources/eurostag-tutorial-example1_lines.ech new file mode 100644 index 00000000..db9097b7 --- /dev/null +++ b/eurostag-ech-export/src/test/resources/eurostag-tutorial-example1_lines.ech @@ -0,0 +1,73 @@ +HEADER 01/03/16 5.1 + +B + +9 1 0 0 1 1 20 0.005 4 100. 1 1 + +SP INPVPQ 2 +SP THMAX 0.100000 +SP EMAXF 0.100000 +SP ZMIN 0.000200 +SP RAMIN 0.800000 +SP RAMAX 1.200000 +SP TOLPLO 0.001000 + +GC sim1/InitialState + +AA FA +AA FR + +1FAFAKENOD1 380. 1. 0. 0. 0. +1FAFAKENOD2 380. 1. 0. 0. 0. +1FRN1L1 380. 1. 0. 0. 0. +1FRN1L2 380. 1. 0. 0. 0. +1FRN1L3 380. 1. 0. 0. 0. +1FRN1L4 380. 1. 0. 0. 0. +1FRN1L5 380. 1. 0. 0. 0. +1FRN1L6 380. 1. 0. 0. 0. +1FRN1L7 380. 1. 0. 0. 0. +1FRN1L8 380. 1. 0. 0. 0. +1FRN2L1 380. 1. 0. 0. 0. +1FRN2L2 380. 1. 0. 0. 0. +1FRN2L3 380. 1. 0. 0. 0. +1FRN2L4 380. 1. 0. 0. 0. +1FRN2L5 380. 1. 0. 0. 0. +1FRN2L6 380. 1. 0. 0. 0. +1FRN2L7 380. 1. 0. 0. 0. +1FRN2L8 380. 1. 0. 0. 0. +1FRNGEN 24. 1. 0. 0. 0. +5 NGEN 0. +1FRNHV1 380. 1. 0. 0. 0. +1FRNHV2 380. 1. 0. 0. 0. +1FRNLOAD 150. 1. 0. 0. 0. + +3 N1L1 N2L1 1 0.002078 0.022853 0. 0. 100. 0. 0. +3 N1L2 N2L2 1 0.002078 0.022853 0. 0.002888 100. 0. 0. +3 N1L3 N2L3 1 0.002078 0.022853 0. 0. 100. 0. 0. +3 N1L4 N2L4 1 0.002078 0.022853 0. 0. 100. 0. 0. +3 N1L5 N2L5 1 0.002078 0.022853 0. 0.000361 100. 0. 0. +3 N1L6 N2L6 1 0.002078 0.022853 0. 0.000361 100. 0. 0. +3 NHV1 NHV2 1 0.002078 0.022853 0. 0.278692 100. 0. 0. +3 NHV1 NHV2 2 0.002078 0.022853 0. 0.278692 100. 0. 0. + +P N1L7 N2L7 1 0.002078 0.022853 1444. 0.002888 100. +P 0.002078 0.022853 0. 0. +P N1L8 N2L8 1 0.002078 0.022853 0. 0.002888 100. +P 0.002078 0.022853 1444. 0. + +48NGEN NHV1 1 100.0.018462 0. 0. 1. 0. 0. +48 1 1 N +48 1 22.8 380. 0.769231 0. +48NHV2 NLOAD 1 100. 0.021 0. 0. 1. 0. 0. +48 2 2 NLOAD 158. V +48 1 446.4633 150. 1.8 0. +48 2 379.4938 150. 1.8 0. +48 3 329.9946 150. 1.8 0. + +CH LOAD Y NLOAD 0. 0. 600. 0. 0. 200. 0. 0. + +G GEN Y NGEN -9999.99 607. 9999.99 -9999.99 301. 9999.99 V 24.5 NGEN 1. 0. 0. + +C FKSHL3 N1L3 1 0. 0.2888 1 N 0. 0. 0. +C FKSHL4 N2L4 1 0. 0.2888 1 N 0. 0. 0. + diff --git a/eurostag-ech-export/src/test/resources/eurostag-tutorial-example1_vre.ech b/eurostag-ech-export/src/test/resources/eurostag-tutorial-example1_vre.ech new file mode 100644 index 00000000..72873323 --- /dev/null +++ b/eurostag-ech-export/src/test/resources/eurostag-tutorial-example1_vre.ech @@ -0,0 +1,43 @@ +HEADER 01/03/16 5.1 + +B + +9 1 0 0 1 1 20 0.005 4 100. 1 1 + +SP INPVPQ 2 +SP THMAX 0.100000 +SP EMAXF 0.100000 +SP ZMIN 0.000200 +SP RAMIN 0.800000 +SP RAMAX 1.200000 +SP TOLPLO 0.001000 + +GC sim1/InitialState + +AA FA +AA FR + +1FAFAKENOD1 380. 1. 0. 0. 0. +1FAFAKENOD2 380. 1. 0. 0. 0. +1FRNGEN 24. 1. 0. 0. 0. +5 NGEN 0. +1FRNHV1 380. 1. 0. 0. 0. +1FRNHV2 380. 1. 0. 0. 0. +1FRNLOAD 150. 1. 0. 0. 0. + +3 NHV1 NHV2 1 0.002078 0.022853 0. 0.278692 100. 0. 0. +3 NHV1 NHV2 2 0.002078 0.022853 0. 0.278692 100. 0. 0. + +48NGEN NHV1 1 100.0.018462 0. 0. 1. 0. 0. +48 1 1 N +48 1 22.8 380. 0.769231 0. +48NHV2 NLOAD 1 100. 0.021 0. 0. 1. 0. 0. +48 2 2 NLOAD 158. V +48 1 446.4633 150. 1.8 0. +48 2 379.4938 150. 1.8 0. +48 3 329.9946 150. 1.8 0. + +CH LOAD Y NLOAD 0. 0. 600. 0. 0. 200. 0. 0. + +G GEN Y NGEN 300. 0. 9999.99 -9999.99 301. 9999.99 N NGEN 1. 0. 0. + diff --git a/eurostag-integration/src/main/java/eu/itesla_project/eurostag/EurostagExportTool.java b/eurostag-integration/src/main/java/eu/itesla_project/eurostag/EurostagExportTool.java index ef9e9307..24c85a5d 100644 --- a/eurostag-integration/src/main/java/eu/itesla_project/eurostag/EurostagExportTool.java +++ b/eurostag-integration/src/main/java/eu/itesla_project/eurostag/EurostagExportTool.java @@ -88,7 +88,9 @@ public void run(CommandLine line, ToolRunningContext context) throws Exception { } else { parameters.setStartMode(eurostagConfig.isLfWarmStart() ? EsgGeneralParameters.StartMode.WARM_START : EsgGeneralParameters.StartMode.FLAT_START); } - EsgNetwork networkEch = new EurostagEchExport(network, exportConfig, parallelIndexes, dictionary, fakeNodes).createNetwork(parameters); + + EurostagEchExporterFactory echExportFactory = defaultConfig.newFactoryImpl(EurostagEchExporterFactory.class, EurostagEchExporterFactoryImpl.class); + EsgNetwork networkEch = echExportFactory.createEchExporter(network, exportConfig, parallelIndexes, dictionary, fakeNodes).createNetwork(parameters); new EurostagNetworkModifier().hvLoadModelling(networkEch); new EsgWriter(networkEch, parameters, specialParameters).write(writer, network.getId() + "/" + network.getStateManager().getWorkingStateId()); } @@ -111,7 +113,7 @@ public void run(CommandLine line, ToolRunningContext context) throws Exception { // export limits try (OutputStream os = Files.newOutputStream(outputDir.resolve(LIMITS_ZIP_FILE_NAME))) { - EurostagImpactAnalysis.writeLimits(network, dictionary, os); + EurostagImpactAnalysis.writeLimits(network, dictionary, os, exportConfig); } } diff --git a/eurostag-integration/src/main/java/eu/itesla_project/eurostag/EurostagImpactAnalysis.java b/eurostag-integration/src/main/java/eu/itesla_project/eurostag/EurostagImpactAnalysis.java index 33fb0802..52b74460 100644 --- a/eurostag-integration/src/main/java/eu/itesla_project/eurostag/EurostagImpactAnalysis.java +++ b/eurostag-integration/src/main/java/eu/itesla_project/eurostag/EurostagImpactAnalysis.java @@ -25,6 +25,7 @@ import com.powsybl.simulation.*; import com.powsybl.simulation.securityindexes.SecurityIndex; import com.powsybl.simulation.securityindexes.SecurityIndexParser; +import eu.itesla_project.iidm.eurostag.export.EurostagEchExportConfig; import org.apache.commons.configuration.ConfigurationException; import org.apache.commons.configuration.HierarchicalINIConfiguration; import org.apache.commons.configuration.SubnodeConfiguration; @@ -43,6 +44,7 @@ import java.nio.file.Path; import java.util.*; import java.util.concurrent.CompletableFuture; +import java.util.stream.Collectors; import static com.powsybl.computation.FilePostProcessor.FILE_GZIP; import static com.powsybl.computation.FilePreProcessor.ARCHIVE_UNZIP; @@ -125,6 +127,8 @@ public class EurostagImpactAnalysis implements ImpactAnalysis, EurostagConstants private final EurostagConfig config; + private final EurostagEchExportConfig exportConfig; + private final Command allCmd; private final List allContingencies = new ArrayList<>(); @@ -137,11 +141,11 @@ public class EurostagImpactAnalysis implements ImpactAnalysis, EurostagConstants public EurostagImpactAnalysis(Network network, ComputationManager computationManager, int priority, ContingenciesProvider contingenciesProvider) { - this(network, computationManager, priority, contingenciesProvider, EurostagConfig.load()); + this(network, computationManager, priority, contingenciesProvider, EurostagConfig.load(), EurostagEchExportConfig.load()); } public EurostagImpactAnalysis(Network network, ComputationManager computationManager, int priority, - ContingenciesProvider contingenciesProvider, EurostagConfig config) { + ContingenciesProvider contingenciesProvider, EurostagConfig config, EurostagEchExportConfig exportConfig) { Objects.requireNonNull(network, "network is null"); Objects.requireNonNull(computationManager, "computation manager is null"); Objects.requireNonNull(contingenciesProvider, "contingencies provider is null"); @@ -151,6 +155,7 @@ public EurostagImpactAnalysis(Network network, ComputationManager computationMan this.priority = priority; this.contingenciesProvider = contingenciesProvider; this.config = config; + this.exportConfig = exportConfig; allCmd = createCommand(ALL_SCENARIOS_ZIP_FILE_NAME, WP43_ALL_CONFIGS_ZIP_FILE_NAME); subsetCmd = createCommand(PARTIAL_SCENARIOS_ZIP_FILE_NAME, WP43_PARTIAL_CONFIGS_ZIP_FILE_NAME); } @@ -202,11 +207,13 @@ public String getVersion() { //needed by wp43 integration //contains lines, only - private static void dumpLinesDictionary(Network network, EurostagDictionary dictionary, Path dir) throws IOException { + private static void dumpLinesDictionary(Network network, EurostagDictionary dictionary, Path dir, EurostagEchExportConfig exportConfig) throws IOException { try (BufferedWriter os = Files.newBufferedWriter(dir.resolve("dict_lines.csv"), StandardCharsets.UTF_8)) { - for (Identifiable obj : Identifiables.sort(Iterables.concat(network.getLines(), - network.getTwoWindingsTransformers(), - network.getDanglingLines()))) { + for (Identifiable obj : Identifiables.sort(Iterables.concat( + network.getLineStream().filter(line -> !(exportConfig.isExportMainCCOnly() && !EchUtil.isInMainCc(line, exportConfig.isNoSwitch()))).collect(Collectors.toList()), + network.getTwoWindingsTransformerStream().filter(twt -> !(exportConfig.isExportMainCCOnly() && !EchUtil.isInMainCc(twt, exportConfig.isNoSwitch()))).collect(Collectors.toList()), + network.getDanglingLineStream().filter(dl -> !(exportConfig.isExportMainCCOnly() && !EchUtil.isInMainCc(dl, exportConfig.isNoSwitch()))).collect(Collectors.toList()) + ))) { os.write(obj.getId() + ";" + dictionary.getEsgId(obj.getId())); os.newLine(); } @@ -218,11 +225,13 @@ private static void dumpLinesDictionary(Network network, EurostagDictionary dict //needed by wp43 integration //contains buses, only - private static void dumpBusesDictionary(Network network, EurostagDictionary dictionary, Path dir) throws IOException { + private static void dumpBusesDictionary(Network network, EurostagDictionary dictionary, Path dir, EurostagEchExportConfig exportConfig) throws IOException { try (BufferedWriter os = Files.newBufferedWriter(dir.resolve("dict_buses.csv"), StandardCharsets.UTF_8)) { for (Bus bus : Identifiables.sort(EchUtil.getBuses(network, dictionary.getConfig()))) { - os.write(bus.getId() + ";" + dictionary.getEsgId(bus.getId())); - os.newLine(); + if (!(exportConfig.isExportMainCCOnly() && (!EchUtil.isInMainCc(bus)))) { + os.write(bus.getId() + ";" + dictionary.getEsgId(bus.getId())); + os.newLine(); + } } } } @@ -251,19 +260,28 @@ private static void dumpLimits(EurostagDictionary dictionary, BufferedWriter wri writer.newLine(); } - private static void writeLimits(Network network, EurostagDictionary dictionary, Domain domain, OutputStream os) throws IOException { + protected static void writeLimits(Network network, EurostagDictionary dictionary, Domain domain, OutputStream os, EurostagEchExportConfig exportConfig) throws IOException { GenericArchive archive = domain.getArchiveFactory().create(GenericArchive.class); try (FileSystem fileSystem = ShrinkWrapFileSystems.newFileSystem(archive)) { Path rootDir = fileSystem.getPath("/"); // dump first current limits for each of the branches try (BufferedWriter writer = Files.newBufferedWriter(rootDir.resolve(CURRENT_LIMITS_CSV), StandardCharsets.UTF_8)) { for (Line l : network.getLines()) { + if (exportConfig.isExportMainCCOnly() && !EchUtil.isInMainCc(l, exportConfig.isNoSwitch())) { + continue; + } dumpLimits(dictionary, writer, l); } for (TwoWindingsTransformer twt : network.getTwoWindingsTransformers()) { + if (exportConfig.isExportMainCCOnly() && !EchUtil.isInMainCc(twt, exportConfig.isNoSwitch())) { + continue; + } dumpLimits(dictionary, writer, twt); } for (DanglingLine dl : network.getDanglingLines()) { + if (exportConfig.isExportMainCCOnly() && !EchUtil.isInMainCc(dl, exportConfig.isNoSwitch())) { + continue; + } dumpLimits(dictionary, writer, dl.getId(), dl.getCurrentLimits(), null, @@ -273,6 +291,9 @@ private static void writeLimits(Network network, EurostagDictionary dictionary, } try (BufferedWriter writer = Files.newBufferedWriter(rootDir.resolve(VOLTAGE_LIMITS_CSV), StandardCharsets.UTF_8)) { for (Bus b : EchUtil.getBuses(network, dictionary.getConfig())) { + if (exportConfig.isExportMainCCOnly() && !(EchUtil.isInMainCc(b))) { + continue; + } VoltageLevel vl = b.getVoltageLevel(); if (!Float.isNaN(vl.getLowVoltageLimit()) && !Float.isNaN(vl.getHighVoltageLimit())) { writer.write(dictionary.getEsgId(b.getId())); @@ -288,20 +309,17 @@ private static void writeLimits(Network network, EurostagDictionary dictionary, } //dump lines dictionary, for WP43 integration - dumpLinesDictionary(network, dictionary, rootDir); + dumpLinesDictionary(network, dictionary, rootDir, exportConfig); //dump buses dictionary, for WP43 integration - dumpBusesDictionary(network, dictionary, rootDir); + dumpBusesDictionary(network, dictionary, rootDir, exportConfig); } archive.as(ZipExporter.class).exportTo(os); } - private void writeLimits(Domain domain, OutputStream os) throws IOException { - writeLimits(network, dictionary, domain, os); - } - static void writeLimits(Network network, EurostagDictionary dictionary, OutputStream os) throws IOException { - writeLimits(network, dictionary, ShrinkWrap.createDomain(), os); + protected static void writeLimits(Network network, EurostagDictionary dictionary, OutputStream os, EurostagEchExportConfig exportConfig) throws IOException { + writeLimits(network, dictionary, ShrinkWrap.createDomain(), os, exportConfig); } private void writeScenarios(Domain domain, List contingencies, OutputStream os) throws IOException { @@ -425,7 +443,7 @@ public void init(SimulationParameters parameters, Map context) t writeAllWp43Configs(domain, os); } try (OutputStream os = computationManager.newCommonFile(LIMITS_ZIP_FILE_NAME)) { - writeLimits(domain, os); + writeLimits(network, dictionary, domain, os, exportConfig); } } } @@ -457,7 +475,7 @@ private Command before(SimulationState state, Set contingencyIds, Path w Files.write(workingDir.resolve(DDB_DICT_GENS_CSV), ((EurostagState) state).getDictGensCsv()); try (OutputStream os = Files.newOutputStream(workingDir.resolve(LIMITS_ZIP_FILE_NAME))) { - writeLimits(domain.get(), os); + writeLimits(network, dictionary, domain.get(), os, exportConfig); } } diff --git a/eurostag-integration/src/main/java/eu/itesla_project/eurostag/EurostagStabilization.java b/eurostag-integration/src/main/java/eu/itesla_project/eurostag/EurostagStabilization.java index fd61c020..e0b1612d 100644 --- a/eurostag-integration/src/main/java/eu/itesla_project/eurostag/EurostagStabilization.java +++ b/eurostag-integration/src/main/java/eu/itesla_project/eurostag/EurostagStabilization.java @@ -102,6 +102,8 @@ public class EurostagStabilization implements Stabilization, EurostagConstants { private SimulationParameters parameters; + private final EurostagEchExporterFactory eurostagEchExporterFactory; + public EurostagStabilization(Network network, ComputationManager computationManager, int priority) { this(network, computationManager, priority, EurostagConfig.load()); } @@ -115,6 +117,7 @@ public EurostagStabilization(Network network, ComputationManager computationMana this.priority = priority; ComponentDefaultConfig defaultConfig = ComponentDefaultConfig.load(); this.ddbClient = defaultConfig.newFactoryImpl(DynamicDatabaseClientFactory.class).create(config.isDdbCaching()); + this.eurostagEchExporterFactory = defaultConfig.newFactoryImpl(EurostagEchExporterFactory.class, EurostagEchExporterFactoryImpl.class); this.config = config; LOGGER.info(config.toString()); @@ -194,7 +197,7 @@ private void writeEch(Path workingDir) throws IOException { } else { parameters.setStartMode(config.isLfWarmStart() ? EsgGeneralParameters.StartMode.WARM_START : EsgGeneralParameters.StartMode.FLAT_START); } - EsgNetwork networkEch = new EurostagEchExport(network, exportConfig, parallelIndexes, dictionary, fakeNodes).createNetwork(parameters); + EsgNetwork networkEch = eurostagEchExporterFactory.createEchExporter(network, exportConfig, parallelIndexes, dictionary, fakeNodes).createNetwork(parameters); networkModifier.hvLoadModelling(networkEch); new EsgWriter(networkEch, parameters, specialParameters).write(writer, network.getId() + "/" + network.getStateManager().getWorkingStateId()); } diff --git a/eurostag-network/src/main/java/eu/itesla_project/eurostag/network/EsgACDCVscConverter.java b/eurostag-network/src/main/java/eu/itesla_project/eurostag/network/EsgACDCVscConverter.java new file mode 100644 index 00000000..9e5687a4 --- /dev/null +++ b/eurostag-network/src/main/java/eu/itesla_project/eurostag/network/EsgACDCVscConverter.java @@ -0,0 +1,203 @@ +/** + * Copyright (c) 2017, 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/. + */ +package eu.itesla_project.eurostag.network; + +import java.util.Objects; + +/** + * @author Christian Biasuzzi + */ +public class EsgACDCVscConverter { + + public enum ConverterState { + ON, + OFF + } + + public enum DCControlMode { + AC_ACTIVE_POWER, + DC_VOLTAGE + } + + public enum ACControlMode { + AC_VOLTAGE, + AC_REACTIVE_POWER, + AC_POWER_FACTOR + } + + private final Esg8charName znconv; // converter name + private final Esg8charName dcNode1; // sending DC node name + private final Esg8charName dcNode2; // receiving DC node name + private final Esg8charName acNode; // AC node name + private final ConverterState xstate; // converter state ' ' ON; 'S' OFF + private final DCControlMode xregl; // DC control mode 'P' AC_ACTIVE_POWER; 'V' DC_VOLTAGE + private final ACControlMode xoper; // AC control mode 'V' AC_VOLTAGE; 'Q' AC_REACTIVE_POWER; 'A' AC_POWER_FACTOR + private final float rrdc; // resistance [Ohms] + private final float rxdc; // reactance [Ohms] + private final float pac; // AC active power setpoint [MW]. Only if DC control mode is 'P' + private final float pvd; // DC voltage setpoint [MW]. Only if DC control mode is 'V' + private final float pva; // AC voltage setpoint [kV]. Only if AC control mode is 'V' + private final float pre; // AC reactive power setpoint [Mvar]. Only if AC control mode is 'Q' + private final float pco; // AC power factor setpoint. Only if AC control mode is 'A' + private final float qvscsh; // Reactive sharing cofficient [%]. Only if AC control mode is 'V' + private final float pvscmin; // Minimum AC active power [MW] + private final float pvscmax; // Maximum AC active power [MW] + private final float qvscmin; // Minimum reactive power injected on AC node [kV] + private final float qvscmax; // Maximum reactive power injected on AC node [kV] + private final float vsb0; // Losses coefficient Beta0 [MW] + private final float vsb1; // Losses coefficient Beta1 [kW] + private final float vsb2; // Losses coefficient Beta2 [Ohms] + private final float mvm; // Initial AC modulated voltage magnitude [p.u.] + private final float mva; // Initial AC modulated voltage angle [deg] + + public EsgACDCVscConverter(Esg8charName znconv, + Esg8charName dcNode1, + Esg8charName dcNode2, + Esg8charName acNode, + ConverterState xstate, + DCControlMode xregl, + ACControlMode xoper, + float rrdc, + float rxdc, + float pac, + float pvd, + float pva, + float pre, + float pco, + float qvscsh, + float pvscmin, + float pvscmax, + float qvscmin, + float qvscmax, + float vsb0, + float vsb1, + float vsb2, + float mvm, + float mva) { + this.znconv = Objects.requireNonNull(znconv); + this.dcNode1 = Objects.requireNonNull(dcNode1); + this.dcNode2 = Objects.requireNonNull(dcNode2); + this.acNode = Objects.requireNonNull(acNode); + this.xstate = Objects.requireNonNull(xstate); + this.xregl = Objects.requireNonNull(xregl); + this.xoper = Objects.requireNonNull(xoper); + this.rrdc = rrdc; + this.rxdc = rxdc; + this.pac = pac; + this.pvd = pvd; + this.pva = pva; + this.pre = pre; + this.pco = pco; + this.qvscsh = qvscsh; + this.pvscmin = pvscmin; + this.pvscmax = pvscmax; + this.qvscmin = qvscmin; + this.qvscmax = qvscmax; + this.vsb0 = vsb0; + this.vsb1 = vsb1; + this.vsb2 = vsb2; + this.mvm = mvm; + this.mva = mva; + } + + + public Esg8charName getZnconv() { + return znconv; + } + + public Esg8charName getDcNode1() { + return dcNode1; + } + + public Esg8charName getDcNode2() { + return dcNode2; + } + + public Esg8charName getAcNode() { + return acNode; + } + + public ConverterState getXstate() { + return xstate; + } + + public DCControlMode getXregl() { + return xregl; + } + + public ACControlMode getXoper() { + return xoper; + } + + public float getRrdc() { + return rrdc; + } + + public float getRxdc() { + return rxdc; + } + + public float getPac() { + return pac; + } + + public float getPvd() { + return pvd; + } + + public float getPva() { + return pva; + } + + public float getPre() { + return pre; + } + + public float getPco() { + return pco; + } + + public float getQvscsh() { + return qvscsh; + } + + public float getPvscmin() { + return pvscmin; + } + + public float getPvscmax() { + return pvscmax; + } + + public float getQvscmin() { + return qvscmin; + } + + public float getQvscmax() { + return qvscmax; + } + + public float getVsb0() { + return vsb0; + } + + public float getVsb1() { + return vsb1; + } + + public float getVsb2() { + return vsb2; + } + + public float getMvm() { + return mvm; + } + + public float getMva() { + return mva; + } +} diff --git a/eurostag-network/src/main/java/eu/itesla_project/eurostag/network/EsgDCLink.java b/eurostag-network/src/main/java/eu/itesla_project/eurostag/network/EsgDCLink.java new file mode 100644 index 00000000..97e948a9 --- /dev/null +++ b/eurostag-network/src/main/java/eu/itesla_project/eurostag/network/EsgDCLink.java @@ -0,0 +1,60 @@ +/** + * Copyright (c) 2017, 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/. + */ +package eu.itesla_project.eurostag.network; + +import java.util.Objects; + +/** + * @author Christian Biasuzzi + */ +public class EsgDCLink { + + public enum LinkStatus { + ON, + OFF + } + + private final Esg8charName node1Name; // sending node name + private final Esg8charName node2Name; // receiving node name + private final char xpp; // parallel index + private final float rldc; // total link resistance [p.u.] + private final LinkStatus linkStatus; // ' ' ON; 'S' OFF + + public EsgDCLink(Esg8charName node1Name, Esg8charName node2Name, char xpp, float rldc, LinkStatus linkStatus) { + this.node1Name = Objects.requireNonNull(node1Name); + this.node2Name = Objects.requireNonNull(node2Name); + this.linkStatus = Objects.requireNonNull(linkStatus); + this.xpp = xpp; + this.rldc = rldc; + } + + public Esg8charName getNode1Name() { + return node1Name; + } + + public Esg8charName getNode2Name() { + return node2Name; + } + + public char getXpp() { + return xpp; + } + + public float getRldc() { + return rldc; + } + + public LinkStatus getLinkStatus() { + return linkStatus; + } + + @Override + public String toString() { + return node1Name + "-" + node2Name + "-" + xpp + ""; + } + +} diff --git a/eurostag-network/src/main/java/eu/itesla_project/eurostag/network/EsgDCNode.java b/eurostag-network/src/main/java/eu/itesla_project/eurostag/network/EsgDCNode.java new file mode 100644 index 00000000..fe881b4a --- /dev/null +++ b/eurostag-network/src/main/java/eu/itesla_project/eurostag/network/EsgDCNode.java @@ -0,0 +1,44 @@ +/** + * Copyright (c) 2017, 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/. + */ +package eu.itesla_project.eurostag.network; + +import java.util.Objects; + +/** + * @author Christian Biasuzzi + */ +public class EsgDCNode { + + private final Esg2charName area; // zone identifier + private final Esg8charName name; // node name + private final float vbase; // base voltage [kV] + private final float vinit; // Initial voltage [p.u.] + + public EsgDCNode(Esg2charName area, Esg8charName name, float vbase, float vinit) { + this.area = Objects.requireNonNull(area); + this.name = Objects.requireNonNull(name); + this.vbase = vbase; + this.vinit = vinit; + } + + public Esg2charName getArea() { + return area; + } + + public Esg8charName getName() { + return name; + } + + public float getVbase() { + return vbase; + } + + public float getVinit() { + return vinit; + } + +} diff --git a/eurostag-network/src/main/java/eu/itesla_project/eurostag/network/EsgNetwork.java b/eurostag-network/src/main/java/eu/itesla_project/eurostag/network/EsgNetwork.java index 256c34ec..940a775c 100644 --- a/eurostag-network/src/main/java/eu/itesla_project/eurostag/network/EsgNetwork.java +++ b/eurostag-network/src/main/java/eu/itesla_project/eurostag/network/EsgNetwork.java @@ -35,6 +35,9 @@ public class EsgNetwork { private final Map loads = new LinkedHashMap<>(); private final Map capacitorsOrReactorBanks = new LinkedHashMap<>(); private final Map staticVarCompensators = new LinkedHashMap<>(); + private final Map dcNodes = new LinkedHashMap<>(); + private final Map dcLinks = new LinkedHashMap<>(); + private final Map vscConverters = new LinkedHashMap<>(); private void checkBranchName(EsgBranchName name) { if (getNode(name.getNode1Name().toString()) == null) { @@ -398,4 +401,71 @@ public void addStaticVarCompensator(EsgStaticVarCompensator svc) { } staticVarCompensators.put(svc.getZnamsvc().toString(), svc); } + + public Collection getDCNodes() { + return dcNodes.values(); + } + + public EsgDCNode getDCNode(String name) { + return dcNodes.get(name); + } + + public void addDCNode(EsgDCNode node) { + if (dcNodes.containsKey(node.getName().toString())) { + throw new IllegalArgumentException("DC node '" + node.getName() + "' already exists"); + } + dcNodes.put(node.getName().toString(), node); + } + + public void removeDCNode(String node) { + if (!dcNodes.containsKey(node)) { + throw new IllegalArgumentException("DC node '" + node + "' doesn't exists"); + } + dcNodes.remove(node); + } + + public Collection getDCLinks() { + return dcLinks.values(); + } + + public EsgDCLink getDCLink(String dcLinkStr) { + return dcLinks.get(dcLinkStr); + } + + public void addDCLink(EsgDCLink dclink) { + if (dcLinks.containsKey(dclink.toString())) { + throw new IllegalArgumentException("DC Link '" + dcLinks.toString() + "' already exists"); + } + dcLinks.put(dclink.toString(), dclink); + } + + public void removeDCLink(String dcLinkStr) { + if (!lines.containsKey(dcLinkStr)) { + throw new IllegalArgumentException("DC Link '" + dcLinkStr + "' doesn't exists"); + } + dcLinks.remove(dcLinkStr); + } + + public Collection getACDCVscConverters() { + return vscConverters.values(); + } + + public EsgACDCVscConverter getACDCVscConverter(String name) { + return vscConverters.get(name); + } + + public void addACDCVscConverter(EsgACDCVscConverter vscConverter) { + if (vscConverters.containsKey(vscConverter.getZnconv().toString())) { + throw new IllegalArgumentException("ACDCVscConverter '" + vscConverter.getZnconv() + "' already exists"); + } + vscConverters.put(vscConverter.getZnconv().toString(), vscConverter); + } + + public void removeACDCVscConverter(String name) { + if (!vscConverters.containsKey(name)) { + throw new IllegalArgumentException("ACDCVscConverter '" + name + "' doesn't exists"); + } + vscConverters.remove(name); + } + } diff --git a/eurostag-network/src/main/java/eu/itesla_project/eurostag/network/io/EsgWriter.java b/eurostag-network/src/main/java/eu/itesla_project/eurostag/network/io/EsgWriter.java index cd1d80ed..e1566bf5 100644 --- a/eurostag-network/src/main/java/eu/itesla_project/eurostag/network/io/EsgWriter.java +++ b/eurostag-network/src/main/java/eu/itesla_project/eurostag/network/io/EsgWriter.java @@ -1,4 +1,5 @@ /** + * Copyright (c) 2017, RTE (http://www.rte-france.com) * Copyright (c) 2016, All partners of the iTesla project (http://www.itesla-project.eu/consortium) * 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 @@ -10,7 +11,9 @@ import java.io.IOException; import java.io.Writer; +import java.util.Comparator; import java.util.Objects; +import java.util.stream.Collectors; /** * @author Geoffroy Jamgotchian @@ -382,6 +385,101 @@ private static void writeStaticVarCompensator(EsgStaticVarCompensator svc, Recor recordWriter.newLine(); } + private static void writeDCNode(EsgDCNode dcNode, RecordWriter recordWriter) throws IOException { + recordWriter.addValue("DC N", 1, 4); + recordWriter.addValue(dcNode.getName().toString(), 6, 13); + recordWriter.addValue(dcNode.getArea().toString(), 15, 16); + recordWriter.addValue(dcNode.getVbase(), 18, 25); + recordWriter.addValue(dcNode.getVinit(), 27, 34); + recordWriter.newLine(); + } + + private static char toChar(EsgDCLink.LinkStatus linkStatus) { + switch (linkStatus) { + case ON: return ' '; + case OFF: return 'S'; + default: throw new InternalError(); + } + } + + private static void writeDCLink(EsgDCLink link, RecordWriter recordWriter) throws IOException { + recordWriter.addValue("DC L", 1, 4); + recordWriter.addValue(link.getNode1Name().toString(), 6, 13); + recordWriter.addValue(link.getNode2Name().toString(), 15, 22); + recordWriter.addValue(link.getXpp(), 24); + recordWriter.addValue(link.getRldc(), 26, 33); + recordWriter.addValue(toChar(link.getLinkStatus()), 35, 35); + recordWriter.newLine(); + } + + private static char toChar(EsgACDCVscConverter.ConverterState state) { + switch (state) { + case OFF: return 'S'; + case ON: return ' '; + default: throw new InternalError(); + } + } + + private static char toChar(EsgACDCVscConverter.DCControlMode dcMode) { + switch (dcMode) { + case AC_ACTIVE_POWER: return 'P'; + case DC_VOLTAGE: return 'V'; + default: throw new InternalError(); + } + } + + private static char toChar(EsgACDCVscConverter.ACControlMode acMode) { + switch (acMode) { + case AC_VOLTAGE: return 'V'; + case AC_REACTIVE_POWER: return 'Q'; + case AC_POWER_FACTOR: return 'A'; + default: throw new InternalError(); + } + } + + private static void writeACDCVscConverter(EsgACDCVscConverter vscConverter, RecordWriter recordWriter) throws IOException { + recordWriter.addValue("DC V", 1, 4); + recordWriter.addValue(vscConverter.getZnconv().toString(), 6, 13); + recordWriter.addValue(vscConverter.getDcNode1().toString(), 15, 22); + recordWriter.addValue(vscConverter.getDcNode2().toString(), 24, 31); + recordWriter.addValue(vscConverter.getAcNode().toString(), 33, 40); + recordWriter.addValue(toChar(vscConverter.getXstate()), 42, 42); + recordWriter.addValue(toChar(vscConverter.getXregl()), 44, 44); + recordWriter.addValue(toChar(vscConverter.getXoper()), 46, 46); + recordWriter.addValue(vscConverter.getRrdc(), 48, 55); + recordWriter.addValue(vscConverter.getRxdc(), 57, 64); + if (!Float.isNaN(vscConverter.getPac())) { + recordWriter.addValue(vscConverter.getPac(), 66, 73); + } + if (!Float.isNaN(vscConverter.getPvd())) { + recordWriter.addValue(vscConverter.getPvd(), 75, 82); + } + if (!Float.isNaN(vscConverter.getPva())) { + recordWriter.addValue(vscConverter.getPva(), 84, 91); + } + if (!Float.isNaN(vscConverter.getPre())) { + recordWriter.addValue(vscConverter.getPre(), 93, 100); + } + if (!Float.isNaN(vscConverter.getPco())) { + recordWriter.addValue(vscConverter.getPco(), 102, 109); + } + if (!Float.isNaN(vscConverter.getQvscsh())) { + recordWriter.addValue(vscConverter.getQvscsh(), 111, 118); + } + recordWriter.newLine(); + recordWriter.addValue("DC V", 1, 4); + recordWriter.addValue(vscConverter.getPvscmin(), 6, 13); + recordWriter.addValue(vscConverter.getPvscmax(), 15, 22); + recordWriter.addValue(vscConverter.getQvscmin(), 24, 31); + recordWriter.addValue(vscConverter.getQvscmax(), 33, 40); + recordWriter.addValue(vscConverter.getVsb0(), 42, 49); + recordWriter.addValue(vscConverter.getVsb1(), 51, 58); + recordWriter.addValue(vscConverter.getVsb2(), 60, 67); + recordWriter.addValue(vscConverter.getMvm(), 69, 76); + recordWriter.addValue(vscConverter.getMva(), 78, 85); + recordWriter.newLine(); + } + public void write(Writer writer) throws IOException { write(writer, null); } @@ -400,7 +498,10 @@ public void write(Writer writer, String comment) throws IOException { writeGeneralComment(recordWriter, comment); if (network.getAreas().size() > 0) { - for (EsgArea area : network.getAreas()) { + for (EsgArea area : network.getAreas() + .stream() + .sorted(Comparator.comparing(EsgArea::getType).thenComparing(area -> area.getName().toString())) + .collect(Collectors.toList())) { writeArea(area, recordWriter); } recordWriter.newLine(); @@ -468,6 +569,28 @@ public void write(Writer writer, String comment) throws IOException { } recordWriter.newLine(); } - } + if (network.getDCNodes().size() > 0) { + for (EsgDCNode dcNode : network.getDCNodes()) { + writeDCNode(dcNode, recordWriter); + } + recordWriter.newLine(); + } + + + if (network.getDCLinks().size() > 0) { + for (EsgDCLink dcLink : network.getDCLinks()) { + writeDCLink(dcLink, recordWriter); + } + recordWriter.newLine(); + } + + + if (network.getACDCVscConverters().size() > 0) { + for (EsgACDCVscConverter vscConv : network.getACDCVscConverters()) { + writeACDCVscConverter(vscConv, recordWriter); + } + recordWriter.newLine(); + } + } } diff --git a/eurostag-step-up-transformer/src/main/java/eu/itesla_project/iidm/ddb/eurostag/EurostagStepUpTransformerInserter.java b/eurostag-step-up-transformer/src/main/java/eu/itesla_project/iidm/ddb/eurostag/EurostagStepUpTransformerInserter.java index 4cad59fe..77cd6aa9 100644 --- a/eurostag-step-up-transformer/src/main/java/eu/itesla_project/iidm/ddb/eurostag/EurostagStepUpTransformerInserter.java +++ b/eurostag-step-up-transformer/src/main/java/eu/itesla_project/iidm/ddb/eurostag/EurostagStepUpTransformerInserter.java @@ -7,6 +7,7 @@ */ package eu.itesla_project.iidm.ddb.eurostag; +import com.google.common.collect.Lists; import com.powsybl.computation.ComputationManager; import com.powsybl.iidm.network.*; import com.powsybl.iidm.network.ReactiveCapabilityCurve.Point; @@ -29,6 +30,7 @@ import java.util.function.Function; import java.util.stream.Collectors; import java.util.stream.Stream; +import java.util.stream.StreamSupport; /** * @@ -530,7 +532,24 @@ private static void removeStepUpTransformersAlreadyPresents(Network n, List lvBusIds = Lists.newArrayList(StreamSupport.stream(lvVl.getBusView().getBuses().spliterator(), false).map(b -> b.getId()).collect(Collectors.toCollection(LinkedList::new)).descendingIterator()); + for (String lvBusId : lvBusIds) { + Bus lvBus = n.getVoltageLevel(lvVlId).getBusView().getBus(lvBusId); + if (lvBus == null) { + String errMsg = "lvVlId: " + lvVlId + "; lvBus with id: " + lvBusId + " not found"; + LOGGER.error(errMsg); + throw new RuntimeException(errMsg); + } + + float v = lvBus.getV(); + if (Float.isNaN(v)) { + v = lvVl.getNominalV(); + } + float a = lvBus.getAngle(); + if (Float.isNaN(a)) { + a = 0; + } // check there is: // - one two windings transformers @@ -587,10 +606,10 @@ public void visitThreeWindingsTransformer(ThreeWindingsTransformer transformer, if (twtLs.isEmpty()) { for (Generator g : genLs) { - g.remove(); + LOGGER.warn("stator voltage level: {}, bus: {}; no connected two-winding-transformer found, no step up transformer removed for generator: {}", lvVlId, lvBus.getId(), g.getId()); } for (Load aux : auxLs) { - aux.remove(); + LOGGER.warn("stator voltage level: {}, bus: {}; no connected two-winding-transformer found, no step up transformer removed for load: {}", lvVlId, lvBus.getId(), aux.getId()); } continue; } @@ -609,10 +628,10 @@ public void visitThreeWindingsTransformer(ThreeWindingsTransformer transformer, VoltageLevel hvVl; Bus hvBus; - if (twt.getTerminal1().getBusBreakerView().getConnectableBus() == lvBus) { + if (twt.getTerminal1().getBusView().getConnectableBus() == lvBus) { hvVl = twt.getTerminal2().getVoltageLevel(); hvBus = twt.getTerminal2().getBusBreakerView().getConnectableBus(); - } else if (twt.getTerminal2().getBusBreakerView().getConnectableBus() == lvBus) { + } else if (twt.getTerminal2().getBusView().getConnectableBus() == lvBus) { hvVl = twt.getTerminal1().getVoltageLevel(); hvBus = twt.getTerminal1().getBusBreakerView().getConnectableBus(); } else { @@ -621,9 +640,9 @@ public void visitThreeWindingsTransformer(ThreeWindingsTransformer transformer, Function fct = sv -> { StateVariable otherSideSv; - if (twt.getTerminal2().getBusBreakerView().getConnectableBus() == lvBus) { + if (twt.getTerminal2().getBusView().getConnectableBus().getId().equals(lvBus.getId())) { otherSideSv = transformerModel.toSv1(new StateVariable(-sv.p, -sv.q, sv.u, sv.theta)); - } else if (twt.getTerminal1().getBusBreakerView().getConnectableBus() == lvBus) { + } else if (twt.getTerminal1().getBusView().getConnectableBus().getId().equals(lvBus.getId())) { otherSideSv = transformerModel.toSv2(new StateVariable(-sv.p, -sv.q, sv.u, sv.theta)); } else { throw new RuntimeException("Unexpected stator substation topology"); @@ -648,14 +667,6 @@ public void visitThreeWindingsTransformer(ThreeWindingsTransformer transformer, for (Load aux : auxLs) { - float v = lvBus.getV(); - if (Float.isNaN(v)) { - v = lvVl.getNominalV(); - } - float a = lvBus.getAngle(); - if (Float.isNaN(a)) { - a = 0; - } StateVariable hlSvAux = fct.apply(new StateVariable(-aux.getP0(), -aux.getQ0(), v, a)); diff --git a/iidm-ddb/iidm-ddb-eurostag-import-export/src/main/java/eu/itesla_project/iidm/ddb/eurostag_imp_exp/DdbDtaImpExp.java b/iidm-ddb/iidm-ddb-eurostag-import-export/src/main/java/eu/itesla_project/iidm/ddb/eurostag_imp_exp/DdbDtaImpExp.java index b30e25a1..57ec5f2d 100644 --- a/iidm-ddb/iidm-ddb-eurostag-import-export/src/main/java/eu/itesla_project/iidm/ddb/eurostag_imp_exp/DdbDtaImpExp.java +++ b/iidm-ddb/iidm-ddb-eurostag-import-export/src/main/java/eu/itesla_project/iidm/ddb/eurostag_imp_exp/DdbDtaImpExp.java @@ -1,19 +1,21 @@ /** - * Copyright (c) 2016, All partners of the iTesla project (http://www.itesla-project.eu/consortium) + * Copyright (c) 2016 All partners of the iTesla project (http://www.itesla-project.eu/consortium) + * Copyright (c) 2018, 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/. */ package eu.itesla_project.iidm.ddb.eurostag_imp_exp; +import com.google.common.base.Strings; import com.google.common.collect.Sets; +import com.powsybl.iidm.network.*; import com.powsybl.iidm.network.util.ConnectedComponents; +import com.powsybl.iidm.network.util.Identifiables; import eu.itesla_project.iidm.ddb.eurostag_imp_exp.utils.Utils; import eu.itesla_project.iidm.ddb.model.*; import eu.itesla_project.iidm.ddb.service.DDBManager; import eu.itesla_project.iidm.ejbclient.EjbClientCtx; -import com.powsybl.iidm.network.*; -import com.powsybl.iidm.network.util.Identifiables; import itesla.converter.Converter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -27,6 +29,7 @@ import java.nio.file.attribute.BasicFileAttributes; import java.text.ParseException; import java.util.*; +import java.util.stream.Collectors; /** * @@ -80,7 +83,7 @@ public class DdbDtaImpExp implements DynamicDatabaseClient { // {"M22D", "I-PHI injector - type Fortescue"}, // {"M23", "IR-II injector"}, // {"M23D", "IR-II injector - type Fortescue"}, - // {"M50", "Converter"}, + {"M50", "Converter"}, // {"M50D", "Converter - type Fortescue"}, {"MA", "Macro-Automaton"}, //// {"R", "Macroblock"} @@ -295,6 +298,9 @@ public void feedDDBWithEurostagData(Path inputFile, case "M21": dbId = populateDDB_M21(zone, dicoMap, ddbmanager, eurostagSim); break; + case "M50": + dbId = populateDDB_M50(zone, dicoMap, ddbmanager, eurostagSim); + break; case "R": dbId = populateDDB_R(zone, dicoMap, ddbmanager, eurostagSim, regsMapping); break; @@ -493,16 +499,144 @@ public String populateDDB_M21(EurostagRecord zone, Map amap, return cimId; } + + public String populateDDB_M50(EurostagRecord zone, Map amap, + DDBManager ddbmanager, SimulatorInst eurostagSim) { + // here we assume that the simulator with the required version already + // exists + if (eurostagSim == null) { + throw new RuntimeException("Eurostag simulator could not be null"); + } + + if (!"M50".equals(zone.getTypeName())) { + throw new RuntimeException("not expected type M50: " + zone); + } + + log.debug("-Creating DDB component {} ", zone.getTypeName()); + String mName = (String) zone.getData().get("machine.name"); + String cimId = amap.get(mName); + if ((cimId == null) || ("".equals(cimId))) { + throw new RuntimeException(mName + ": cimId not found in mapping."); + } + + if (!equipmentsTypeMap.contains(cimId)) { + equipmentsTypeMap.put(cimId, zone.getTypeName()); + } + + Equipment eq1 = ddbmanager.findEquipment(cimId); + if ((eq1 != null) && updateFlag) { + Set connectedInternals = getConnectedInternals(cimId, ddbmanager); + + //remove this equipment graph + log.info("- removing existing equipment {}", cimId); + removeEquipment(cimId, ddbmanager); + eq1 = null; + + for (String internalId : connectedInternals) { + log.info("- removing existing connected internal {}", internalId); + removeInternal(internalId, ddbmanager); + } + + } + + String mtcDdbid = MTC_PREFIX_NAME + zone.getKeyName(); + ModelTemplateContainer mtc1 = ddbmanager + .findModelTemplateContainer(mtcDdbid); + if (mtc1 == null) { + throw new RuntimeException(" template container " + mtcDdbid + + " not defined! "); + } + + ParametersContainer pc1 = ddbmanager.findParametersContainer(mName); + if (pc1 == null) { + log.debug("-- creating Parameters Container " + mName + " plus parameters."); + pc1 = new ParametersContainer(mName); + Parameters pars = new Parameters(eurostagSim); + for (String varName : zone.getData().keySet()) { + String varFType = DtaParser.getVarFType(zone.typeName, varName); + Object varValue = zone.getData().get(varName); + if (log.isDebugEnabled()) { + log.trace("----" + varName + " = " + varValue + ", " + varFType); + } + if (varFType.startsWith("F")) { + if (varValue != null) { + pars.addParameter(new ParameterFloat(varName, + new Float((Double) varValue))); + } else { + pars.addParameter(new ParameterFloat(varName, null)); + } + } else if (varFType.startsWith("A")) { + pars.addParameter(new ParameterString(varName, + (String) varValue)); + } else { + log.error(varFType + " not handled"); + } + } + pc1.getParameters().add(pars); + pc1 = ddbmanager.save(pc1); + } else { + log.debug("-- Parameters Container container " + mName + + " already defined, id: " + pc1.getId()); + // ddbmanager.delete(pc1); + } + + if (eq1 == null) { + log.info("-- creating Equipment " + cimId + "; eurostag name is: " + + mName); + eq1 = new Equipment(cimId); + eq1.setModelContainer(mtc1); + eq1.setParametersContainer(pc1); + eq1 = ddbmanager.save(eq1); + } else { + log.warn("-- Equipment " + cimId + " already defined, id: " + + eq1.getId()); + } + + return cimId; + } + private String populateDDB_R(EurostagRecord zone, Map amap, DDBManager ddbmanager, SimulatorInst eurostagSim, Map regsMapping) { log.debug("-Creating DDB component (R) " + zone); + String machineName = (String) zone.getData().get("machine.name"); + + //macroblock's coupling parameters - map machine IDs to iidm IDs + zone.getData().keySet().stream() + .filter(parName -> parName.startsWith("coupling.par") && (zone.getData().get(parName) != null) && (!"".equals(zone.getData().get(parName)))) + .forEach(parName -> { + String parValue = zone.getData().get(parName).toString(); + String couplingtype = parValue.substring(0, 2); + //log.info("machine: {} - coupling macroblock parameter: {} = {}", machineName, parName, parValue); + switch (couplingtype) { + case "M ": + case "N#": + String couplingMachineName = parValue.substring(3).trim(); + String iidmCouplingMachineName = amap.get(couplingMachineName); + if (iidmCouplingMachineName != null) { + String newparValue = couplingtype + " " + iidmCouplingMachineName; + zone.getData().put(parName, newparValue); + log.info("macroblock {} - coupling parameter {} = '{}' - mapped to '{}'", machineName, parName, parValue, newparValue); + } else { + String errMsg = "macroblock " + machineName + " - coupling parameter " + parName + " = " + parValue + " not mapped"; + log.error(errMsg); + throw new RuntimeException(errMsg); + } + break; + case "N ": + log.warn("macroblock {} - coupling parameter: {} = {} - coupling type {} not stored in DDB", machineName, parName, parValue, couplingtype); + break; + default: + log.warn("macroblock {} - coupling parameter: {} = {} coupling type {} not supported !", machineName, parName, parValue, couplingtype); + } + }); + String macroblockName = (String) zone.getData().get( PAR_MACROBLOCK_NAME); int paramSetNum = (int) zone.getData().get("psetnum"); - String machineName = (String) zone.getData().get("machine.name"); + String nativeId = amap.get(machineName); if ((nativeId == null) || ("".equals(nativeId))) { @@ -937,6 +1071,17 @@ public void dumpDtaFile(Path workingDir, String fileName, } } + //add vscConverter stations to the equipment list + for (HvdcLine hvdcLine : Identifiables.sort(network.getHvdcLines())) { + // skip lines with converter stations not in the main connected component + if (configExport.isExportMainCCOnly() && (!isInMainCc(hvdcLine.getConverterStation1(), configExport.isNoSwitch()) || !isInMainCc(hvdcLine.getConverterStation2(), configExport.isNoSwitch()))) { + log.warn("skipped HVDC line {}: at least one converter station is not in main component", hvdcLine.getId()); + continue; + } + cimIds.add(hvdcLine.getConverterStation1().getId()); + cimIds.add(hvdcLine.getConverterStation2().getId()); + } + // writing a .dta DtaParser.dumpHeader(new Date(), eurostagVersion, dtaOutStream); @@ -1216,6 +1361,9 @@ public void dumpData(Equipment inst, SimulatorInst simInst, DDBManager ddbmanage case "M21": zoneTypeName = "M21"; break; + case "M50": + zoneTypeName = "M50"; + break; case "R": zoneTypeName = "R"; break; @@ -1275,6 +1423,19 @@ public void dumpData(Equipment inst, SimulatorInst simInst, DDBManager ddbmanage // } } break; + case "M50": + if (iidm2eurostagId != null) { + String substMachineName = iidm2eurostagId.get(inst.getCimId()); + if ((substMachineName != null) && (!"".equals(substMachineName))) { + zm.put("machine.name", substMachineName); + } + + if (log.isDebugEnabled()) { + log.debug(" machine.name mapped to new eurostag id: " + substMachineName); + } + + } + break; default: break; } @@ -1527,21 +1688,86 @@ public void dumpData(Internal inst, SimulatorInst simInst, DDBManager ddbmanager zm.put("coupling.par9", null); } } else { - if (couplingDataExists) { - log.warn("coupling macroblock data in ddb - macroblock name: " + zm.get("macroblock.name") + "; machine name: " + machineName); - } - zm.put("coupling.par1", null); - zm.put("coupling.par2", null); - zm.put("coupling.par3", null); - zm.put("coupling.par4", null); - zm.put("coupling.par5", null); - zm.put("coupling.par6", null); - zm.put("coupling.par7", null); - zm.put("coupling.par8", null); - zm.put("coupling.par9", null); - if (log.isDebugEnabled()) { - log.trace(" coupling.par(s) mapped to empty string"); + //handle specific M50 model template (VSC) + if ((MTC_PREFIX_NAME + "M50").equals(eq.getModelContainer().getDdbId())) { + if (isMacroblockIncluded(network.getVscConverterStation(eq.getCimId()), zm)) { + //filter out meta-parameters (i.e. #PMODE #VMODE) + HashMap zmFiltered = zm.entrySet().stream() + .filter(p -> !p.getKey().startsWith("#")) + .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (key, value) -> key, HashMap::new)); + zm = zmFiltered; + } else { + //just skip this macroblock section + return; + } } + + // set coupling macroblock parameters + log.debug("Fixing macroblock's coupling parameters - id: {}, machine name: {} original macrobloc data: {}", eq.getCimId(), machineName, zm); + HashMap zmap = zm; + zm.keySet().stream() + .filter(parName -> parName.startsWith("coupling.par") && (zmap.get(parName) != null) && (!"".equals(zmap.get(parName)))) + .forEach(parName -> { + String parValue = zmap.get(parName).toString(); + String couplingtype = parValue.substring(0, 2); + String newParValue = null; + switch (couplingtype) { + case "M ": + //in DDB, in this specific case (machine name coupling), it is stored the iidm ID of the machine (set at DDB's loading time) + String couplingMachineName = parValue.substring(3); + String iidmCouplingMachineName = iidm2eurostagId.get(couplingMachineName); + if (iidmCouplingMachineName != null) { + newParValue = couplingtype + " " + Strings.padEnd(iidmCouplingMachineName, 8, ' '); + } else { + throw new RuntimeException("id: " + eq.getCimId() + ", name: " + machineName + ", coupling parameter " + parName + "=" + parValue + " - could not map to any machine"); + } + break; + case "N#": + //in DDB, in this specific case N# (node name coupling), it is stored the iidm ID of the machine whose bus has to be retrieved + //note that N# is not Eurostag compliant identifier: it must be converted to 'N' + String refMachineName = parValue.substring(3); + Injection injection = (Injection) network.getIdentifiable(refMachineName); + if (injection == null) { + throw new RuntimeException("id: " + eq.getCimId() + ", name: " + machineName + ", coupling parameter " + parName + "=" + parValue + " - could not find any injection with id: " + refMachineName); + } + + Bus coupledBus = null; + //if the referenced machine is a Generator, deal with stepuptransformers + //fetch the high-voltage node of the step-up transformer of the unit + Generator g; + if ((g = network.getGenerator(injection.getId())) != null) { + VoltageLevel vlGen = g.getTerminal().getVoltageLevel(); + if (vlGen != null) { + TwoWindingsTransformer twt = network.getTwoWindingsTransformerStream().filter(t -> vlGen.getId().equals(t.getTerminal2().getVoltageLevel().getId())).findFirst().orElse(null); + if (twt != null) { + coupledBus = getBus(twt.getTerminal1(), configExport.isNoSwitch()); + } + } + } + if (coupledBus == null) { + coupledBus = getBus(injection.getTerminal(), configExport.isNoSwitch()); + } + if (coupledBus == null) { + throw new RuntimeException("id: " + eq.getCimId() + ", name: " + machineName + ", coupling parameter " + parName + "=" + parValue + " - bus not connected to a bus and not connectable"); + } + String newBusEsgId = iidm2eurostagId.containsKey(coupledBus.getId()) ? iidm2eurostagId.get(coupledBus.getId()) : null; + if (newBusEsgId != null) { + newParValue = "N " + " " + Strings.padEnd(newBusEsgId, 8, ' '); + } else { + throw new RuntimeException("id: " + eq.getCimId() + ", name: " + machineName + ", coupling parameter " + parName + "=" + parValue + " - unknown mapping for bus: " + coupledBus.getId()); + } + break; + case "N ": + log.warn("macroblock {} - coupling parameter {} = {}; coupling type {} not mapped", machineName, parName, parValue, couplingtype); + break; + default: + log.error("macroblock {} - coupling parameter {} = {}; coupling type {} not supported", machineName, parName, parValue, couplingtype); + throw new RuntimeException("macroblock " + machineName + " coupling parameter " + parName + " = " + parValue + "; coupling type " + couplingtype + "not supported"); + } + zmap.put(parName, newParValue); + log.info("macroblock {} - coupling parameter: {} = {} mapped to {}", machineName, parName, parValue, newParValue); + }); + log.debug("Fixing macroblock's coupling parameters - id: {}, machine name: {} fixed macrobloc data: {}", eq.getCimId(), machineName, zm); } EurostagRecord eRecord = new EurostagRecord(zoneTypeName, zm); @@ -1559,6 +1785,47 @@ public void dumpData(Internal inst, SimulatorInst simInst, DDBManager ddbmanager } } + protected HvdcLine getHvdcLineFromConverterStation(VscConverterStation vscStation) { + return network.getHvdcLineStream() + .filter(hLine -> hLine.getConverterStation1().getId().equals(vscStation.getId()) || hLine.getConverterStation2().getId().equals(vscStation.getId())) + .findFirst() + .orElse(null); + } + + protected boolean isPMode(HvdcConverterStation vscConv, HvdcLine hvdcLine) { + Objects.requireNonNull(vscConv); + Objects.requireNonNull(hvdcLine); + HvdcConverterStation side1Conv = hvdcLine.getConverterStation1(); + HvdcConverterStation side2Conv = hvdcLine.getConverterStation2(); + if ((hvdcLine.getConvertersMode().equals(HvdcLine.ConvertersMode.SIDE_1_RECTIFIER_SIDE_2_INVERTER)) + && (vscConv.getId().equals(side1Conv.getId()))) { + return true; + } + if ((hvdcLine.getConvertersMode().equals(HvdcLine.ConvertersMode.SIDE_1_INVERTER_SIDE_2_RECTIFIER)) + && (vscConv.getId().equals(side2Conv.getId()))) { + return true; + } + return false; + } + + private boolean isMacroblockIncluded(VscConverterStation cvStation, HvdcLine hvdcLine, HashMap zm) { + Objects.requireNonNull(cvStation); + Objects.requireNonNull(hvdcLine); + Objects.requireNonNull(zm); + boolean isPmode = isPMode(cvStation, hvdcLine); + boolean metaP = zm.get("#PMODE") != null; + boolean metaV = zm.get("#VMODE") != null; + boolean retVal = (isPmode && metaP) || (!isPmode && metaV) || (!metaP && !metaV); + log.debug("VscConverterStation: {}, HvdcLine: {}, machine name: {}, macroblock name: {}, output macroblock: {}", cvStation.getId(), hvdcLine.getId(), zm.get("machine.name"), zm.get("macroblock.name"), retVal); + return retVal; + } + + private boolean isMacroblockIncluded(VscConverterStation cvStation, HashMap zm) { + Objects.requireNonNull(cvStation); + Objects.requireNonNull(zm); + return isMacroblockIncluded(cvStation, getHvdcLineFromConverterStation(cvStation), zm); + } + // Dump data without DDB parameters public void dumpDataAutomatons(SimulatorInst eurostagSim, DDBManager ddbmanager, PrintStream dtaOutStream, Map iidm2eurostagId) throws ParseException, IOException { diff --git a/iidm-ddb/iidm-ddb-eurostag-import-export/src/main/java/eu/itesla_project/iidm/ddb/eurostag_imp_exp/DtaParser.java b/iidm-ddb/iidm-ddb-eurostag-import-export/src/main/java/eu/itesla_project/iidm/ddb/eurostag_imp_exp/DtaParser.java index a219687f..0ed4a3a9 100644 --- a/iidm-ddb/iidm-ddb-eurostag-import-export/src/main/java/eu/itesla_project/iidm/ddb/eurostag_imp_exp/DtaParser.java +++ b/iidm-ddb/iidm-ddb-eurostag-import-export/src/main/java/eu/itesla_project/iidm/ddb/eurostag_imp_exp/DtaParser.java @@ -137,6 +137,16 @@ private DtaParser(final String name) { "TerminalVoltage", "KG", "SCL", "SLOPE", "VPCC", "SETMODE", "MODE", "BSVC", "ZERO", "BMAX", "BTCR", "MSCON", "MSRON" }); + componentsVariablesNames.put("M50", new String[]{ + "machine.type, type.fortescue, XMACOUP, fnum1, fnum2, falpha", + "machine.name,connection.node.name,TFIL,PP,PQ,RP,RPX,SN" + }); + + componentsInterfaceVariablesNames.put("M50", new String[]{ + "TerminalVoltage", "UDC", "UDCA", "UDCB", "UREF" + }); + + componentsVariablesNames.put("MA", new String[]{ "keyword", "ma.name,equipment.type,equipment.name" @@ -242,6 +252,11 @@ private DtaParser(final String name) { "(A8, 1X, A8, 1X, A1, 17X, F8, 1X, F8, 19X, F8, F8, 1X, F8, 1X, F8, 1X, F8)" }); + componentsDescriptors.put("M50", new String[]{ + "(A3, A1, 1X, A1, 7X, F8, 1X, F8, 1X, A8)", + "(A8, 1X, A8, 3X, F8, 8X, F8, 1X, F8, 1X, F8, 1X, F8, 1X, F8)" + }); + componentsDescriptors.put("MA", new String[]{ "(A2)", "(A8, 1X, A2, 1X, A8)" @@ -387,6 +402,7 @@ public static void dumpZone(EurostagRecord zone, PrintStream out) throws ParseEx case "BAT": case "MA": case "A33_ACMC": + case "M50": int rcount = 1; String[] recordsFormatting = componentsDescriptors.get(zone.typeName); for (String string : recordsFormatting) {