diff --git a/matpower/matpower-converter/src/main/java/com/powsybl/matpower/converter/MatpowerExporter.java b/matpower/matpower-converter/src/main/java/com/powsybl/matpower/converter/MatpowerExporter.java index 6fb3aa8a283..c77dadf495f 100644 --- a/matpower/matpower-converter/src/main/java/com/powsybl/matpower/converter/MatpowerExporter.java +++ b/matpower/matpower-converter/src/main/java/com/powsybl/matpower/converter/MatpowerExporter.java @@ -479,30 +479,43 @@ private void createVSCs(Network network, MatpowerModel model, Context context) { private static void addMgen(MatpowerModel model, Context context, Bus bus, VoltageLevel vl, String id, double targetV, double targetP, double minP, double maxP, double targetQ, double minQ, double maxQ, Bus regulatedBus, boolean voltageRegulation, double ratedS) { - MGen mGen = new MGen(); - mGen.setNumber(context.mBusesNumbersByIds.get(bus.getId())); - mGen.setStatus(CONNECTED_STATUS); - mGen.setRealPowerOutput(targetP); - mGen.setReactivePowerOutput(targetQ); - if (voltageRegulation && regulatedBus != null) { - double targetVpu = targetV / vl.getNominalV(); - if (!regulatedBus.getId().equals(bus.getId())) { - double oldTargetV = targetVpu; - targetVpu *= vl.getNominalV() / regulatedBus.getVoltageLevel().getNominalV(); - LOGGER.warn( - "Generator remote voltage control not supported in Matpower model, rescale targetV of '{}' from {} to {}", - id, oldTargetV, targetVpu); - } - mGen.setVoltageMagnitudeSetpoint(targetVpu); + int busNum = context.mBusesNumbersByIds.get(bus.getId()); + MBus mBus = model.getBusByNum(busNum); + boolean validVoltageRegulation = voltageRegulation && regulatedBus != null; + // Matpower power flow does not support bus with multiple generators that do not have the same voltage regulation + // status. if the bus has PV type, all of its generator must have a valid voltage set point. + if (!validVoltageRegulation && mBus.getType() == MBus.Type.PV) { + // convert to load + mBus.setRealPowerDemand(mBus.getRealPowerDemand() - targetP); + mBus.setReactivePowerDemand(mBus.getReactivePowerDemand() - targetQ); } else { - mGen.setVoltageMagnitudeSetpoint(0); + MGen mGen = new MGen(); + mGen.setNumber(busNum); + mGen.setStatus(CONNECTED_STATUS); + mGen.setRealPowerOutput(targetP); + mGen.setReactivePowerOutput(targetQ); + if (validVoltageRegulation) { + double targetVpu = targetV / vl.getNominalV(); + if (!regulatedBus.getId().equals(bus.getId())) { + double oldTargetV = targetVpu; + targetVpu *= vl.getNominalV() / regulatedBus.getVoltageLevel().getNominalV(); + LOGGER.warn( + "Generator remote voltage control not supported in Matpower model, rescale targetV of '{}' from {} to {}", + id, oldTargetV, targetVpu); + } + mGen.setVoltageMagnitudeSetpoint(targetVpu); + } else { + // we can safely set voltage setpoint to zero, because a PQ bus never go back to PV even if reactive limits + // are activated in Matpower power flow + mGen.setVoltageMagnitudeSetpoint(0); + } + mGen.setMinimumRealPowerOutput(minP); + mGen.setMaximumRealPowerOutput(maxP); + mGen.setMinimumReactivePowerOutput(minQ); + mGen.setMaximumReactivePowerOutput(maxQ); + mGen.setTotalMbase(Double.isNaN(ratedS) ? 0 : ratedS); + model.addGenerator(mGen); } - mGen.setMinimumRealPowerOutput(minP); - mGen.setMaximumRealPowerOutput(maxP); - mGen.setMinimumReactivePowerOutput(minQ); - mGen.setMaximumReactivePowerOutput(maxQ); - mGen.setTotalMbase(Double.isNaN(ratedS) ? 0 : ratedS); - model.addGenerator(mGen); } private static int getBranchCount(Bus bus) { diff --git a/matpower/matpower-converter/src/test/java/com/powsybl/matpower/converter/MatpowerExporterTest.java b/matpower/matpower-converter/src/test/java/com/powsybl/matpower/converter/MatpowerExporterTest.java index 67063c6b77b..e8132d82d27 100644 --- a/matpower/matpower-converter/src/test/java/com/powsybl/matpower/converter/MatpowerExporterTest.java +++ b/matpower/matpower-converter/src/test/java/com/powsybl/matpower/converter/MatpowerExporterTest.java @@ -87,4 +87,19 @@ void testCase30ConsideringBaseVoltage() throws IOException { exportToMatAndCompareTo(network, "/ieee30-considering-base-voltage.json"); } + + @Test + void testNonRegulatingGenOnPVBus() throws IOException { + var network = EurostagTutorialExample1Factory.create(); + network.getVoltageLevel("VLGEN").newGenerator() + .setId("GEN2") + .setBus("NGEN") + .setTargetP(10) + .setTargetQ(5) + .setMinP(0) + .setMaxP(1000) + .setVoltageRegulatorOn(false) + .add(); + exportToMatAndCompareTo(network, "/sim1-with-non-regulating-gen.json"); + } } diff --git a/matpower/matpower-converter/src/test/resources/sim1-with-non-regulating-gen.json b/matpower/matpower-converter/src/test/resources/sim1-with-non-regulating-gen.json new file mode 100644 index 00000000000..17a1a0027b3 --- /dev/null +++ b/matpower/matpower-converter/src/test/resources/sim1-with-non-regulating-gen.json @@ -0,0 +1,146 @@ +{ + "caseName" : "sim1", + "baseMva" : 100.0, + "version" : "2", + "buses" : [ { + "number" : 1, + "type" : "PV", + "name" : "VLGEN_0", + "realPowerDemand" : -10.0, + "reactivePowerDemand" : -5.0, + "shuntConductance" : 0.0, + "shuntSusceptance" : 0.0, + "areaNumber" : 1, + "voltageMagnitude" : 1.0, + "voltageAngle" : 0.0, + "baseVoltage" : 24.0, + "lossZone" : 1, + "maximumVoltageMagnitude" : 0.0, + "minimumVoltageMagnitude" : 0.0 + }, { + "number" : 2, + "type" : "REF", + "name" : "VLHV1_0", + "realPowerDemand" : 0.0, + "reactivePowerDemand" : 0.0, + "shuntConductance" : 0.0, + "shuntSusceptance" : 0.0, + "areaNumber" : 1, + "voltageMagnitude" : 1.0, + "voltageAngle" : 0.0, + "baseVoltage" : 380.0, + "lossZone" : 1, + "maximumVoltageMagnitude" : 0.0, + "minimumVoltageMagnitude" : 0.0 + }, { + "number" : 3, + "type" : "PQ", + "name" : "VLHV2_0", + "realPowerDemand" : 0.0, + "reactivePowerDemand" : 0.0, + "shuntConductance" : 0.0, + "shuntSusceptance" : 0.0, + "areaNumber" : 1, + "voltageMagnitude" : 1.0, + "voltageAngle" : 0.0, + "baseVoltage" : 380.0, + "lossZone" : 1, + "maximumVoltageMagnitude" : 0.0, + "minimumVoltageMagnitude" : 0.0 + }, { + "number" : 4, + "type" : "PQ", + "name" : "VLLOAD_0", + "realPowerDemand" : 600.0, + "reactivePowerDemand" : 200.0, + "shuntConductance" : 0.0, + "shuntSusceptance" : 0.0, + "areaNumber" : 1, + "voltageMagnitude" : 1.0, + "voltageAngle" : 0.0, + "baseVoltage" : 150.0, + "lossZone" : 1, + "maximumVoltageMagnitude" : 0.0, + "minimumVoltageMagnitude" : 0.0 + } ], + "generators" : [ { + "number" : 1, + "realPowerOutput" : 607.0, + "reactivePowerOutput" : 301.0, + "maximumReactivePowerOutput" : 9999.99, + "minimumReactivePowerOutput" : -9999.99, + "voltageMagnitudeSetpoint" : 1.0208333333333333, + "totalMbase" : 0.0, + "status" : 1, + "maximumRealPowerOutput" : 9999.99, + "minimumRealPowerOutput" : -9999.99, + "pc1" : 0.0, + "pc2" : 0.0, + "qc1Min" : 0.0, + "qc1Max" : 0.0, + "qc2Min" : 0.0, + "qc2Max" : 0.0, + "rampAgc" : 0.0, + "rampTenMinutes" : 0.0, + "rampThirtyMinutes" : 0.0, + "rampQ" : 0.0, + "apf" : 0.0 + } ], + "branches" : [ { + "from" : 2, + "to" : 3, + "r" : 0.002077562326869806, + "x" : 0.022853185595567867, + "b" : 0.557384, + "rateA" : 0.0, + "rateB" : 0.0, + "rateC" : 0.0, + "ratio" : 0.0, + "phaseShiftAngle" : 0.0, + "status" : 1.0, + "angMin" : 0.0, + "angMax" : 0.0 + }, { + "from" : 2, + "to" : 3, + "r" : 0.002077562326869806, + "x" : 0.022853185595567867, + "b" : 0.557384, + "rateA" : 0.0, + "rateB" : 0.0, + "rateC" : 0.0, + "ratio" : 0.0, + "phaseShiftAngle" : 0.0, + "status" : 1.0, + "angMin" : 0.0, + "angMax" : 0.0 + }, { + "from" : 1, + "to" : 2, + "r" : 1.846153846153846E-4, + "x" : 0.007690091988585015, + "b" : 0.0, + "rateA" : 0.0, + "rateB" : 0.0, + "rateC" : 0.0, + "ratio" : 0.9500000000000001, + "phaseShiftAngle" : 0.0, + "status" : 1.0, + "angMin" : 0.0, + "angMax" : 0.0 + }, { + "from" : 3, + "to" : 4, + "r" : 2.0999999999999998E-4, + "x" : 0.017998774958313132, + "b" : 0.0, + "rateA" : 0.0, + "rateB" : 0.0, + "rateC" : 0.0, + "ratio" : 0.9986679988158019, + "phaseShiftAngle" : 0.0, + "status" : 1.0, + "angMin" : 0.0, + "angMax" : 0.0 + } ] +} \ No newline at end of file