From 9b590354607cf7994df8584481e596e848d575ef Mon Sep 17 00:00:00 2001 From: Coline PILOQUET Date: Fri, 28 Feb 2025 16:01:51 +0100 Subject: [PATCH] Distinguish regulation depending on whether it is active Signed-off-by: Coline PILOQUET --- docs/grid_exchange_formats/ucte/export.md | 7 ++- .../export/elements/GeneratorUcteExport.java | 55 ++++++++++++++----- .../converter/UcteExporterGeneratorTest.java | 13 +++++ 3 files changed, 60 insertions(+), 15 deletions(-) diff --git a/docs/grid_exchange_formats/ucte/export.md b/docs/grid_exchange_formats/ucte/export.md index ae2ae8b3a9a..9c8e80a4838 100644 --- a/docs/grid_exchange_formats/ucte/export.md +++ b/docs/grid_exchange_formats/ucte/export.md @@ -11,7 +11,12 @@ the generators connected to the associated bus: 3 if the bus is the slack node and 0 (PQ node) by default. - The voltage reference of the UCTE node is obtained from the `TargetV` of the generators connected to the bus. If multiple generators are regulating voltage with a different `TargetV`, then the `TargetV` that is the closest to the -`nominalV` of the `VoltageLevel` is kept. +`nominalV` of the `VoltageLevel` is selected. If some generators are regulating remotely or have a `TargetV` without active regulation, +the `TargetV` is determined the following priority order: + 1. TargetV of generators regulating locally + 2. TargetV of generators regulating remotely + 3. TargetV of generators with local deactivated regulation + 4. TargetV of generators with remote deactivated regulation - The active power generation of the UCTE node is the negated sum of the `TargetP` of every connected generator that are not `NaN`. - The reactive power generation of the UCTE node is the negated sum of the `TargetQ` of every connected generator that are not `NaN`. - The minimum permissible generation in active power of the UCTE node is the sum of the `minP` of every connected generator diff --git a/ucte/ucte-converter/src/main/java/com/powsybl/ucte/converter/export/elements/GeneratorUcteExport.java b/ucte/ucte-converter/src/main/java/com/powsybl/ucte/converter/export/elements/GeneratorUcteExport.java index 9f41092c075..c1a7fab176c 100644 --- a/ucte/ucte-converter/src/main/java/com/powsybl/ucte/converter/export/elements/GeneratorUcteExport.java +++ b/ucte/ucte-converter/src/main/java/com/powsybl/ucte/converter/export/elements/GeneratorUcteExport.java @@ -33,8 +33,12 @@ private GeneratorUcteExport() { public static void convertGenerators(UcteNode ucteNode, Bus bus) { double activePowerGeneration = 0; double reactivePowerGeneration = 0; - List localVoltageReferences = new ArrayList<>(); - List remoteVoltageReferences = new ArrayList<>(); + + List localActiveVoltageReferences = new ArrayList<>(); + List remoteActiveVoltageReferences = new ArrayList<>(); + List localInactiveVoltageReferences = new ArrayList<>(); + List remoteInactiveVoltageReferences = new ArrayList<>(); + List minPs = new ArrayList<>(); List maxPs = new ArrayList<>(); List minQs = new ArrayList<>(); @@ -54,16 +58,11 @@ public static void convertGenerators(UcteNode ucteNode, Bus bus) { reactivePowerGeneration -= generator.getTargetQ(); } if (!Double.isNaN(generator.getTargetV())) { - if (generator.getRegulatingTerminal().getConnectable().getId().equals(generator.getId())) { - localVoltageReferences.add(generator.getTargetV()); - } else { - remoteVoltageReferences.add(getTargetV(generator)); + categorizeVoltageReference(generator, localActiveVoltageReferences, remoteActiveVoltageReferences, localInactiveVoltageReferences, remoteInactiveVoltageReferences); + if (generator.isVoltageRegulatorOn()) { + nodeType = UcteNodeTypeCode.PU; // If one of the generators regulates voltage, then the node is a PU node. } } - if (generator.isVoltageRegulatorOn()) { - // If one of the generators regulates voltage, then the node is a PU node. - nodeType = UcteNodeTypeCode.PU; - } minPs.add(generator.getMinP()); maxPs.add(generator.getMaxP()); powerPlantTypes.add(energySourceToUctePowerPlantType(generator)); @@ -71,7 +70,7 @@ public static void convertGenerators(UcteNode ucteNode, Bus bus) { ucteNode.setActivePowerGeneration(activePowerGeneration); ucteNode.setReactivePowerGeneration(reactivePowerGeneration); - ucteNode.setVoltageReference(getVoltageReference(localVoltageReferences, remoteVoltageReferences, bus.getVoltageLevel().getNominalV())); + ucteNode.setVoltageReference(getVoltageReference(localActiveVoltageReferences, remoteActiveVoltageReferences, localInactiveVoltageReferences, remoteInactiveVoltageReferences, bus.getVoltageLevel().getNominalV())); ucteNode.setPowerPlantType(getUctePowerPlantType(powerPlantTypes, bus)); ucteNode.setTypeCode(nodeType); // for minP, maxP, minQ, maxQ, sum the values on each generator unless it is equal to Double.MAX_VALUE or DEFAULT_POWER_LIMIT (equivalent to undefined) @@ -83,6 +82,25 @@ public static void convertGenerators(UcteNode ucteNode, Bus bus) { ucteNode.setMaximumPermissibleReactivePowerGeneration(-computeMaxPower(maxQs)); } + private static void categorizeVoltageReference(Generator generator, List localActiveVoltageReferences, List remoteActiveVoltageReferences, List localInactiveVoltageReferences, List remoteInactiveVoltageReferences) { + double targetV = getTargetV(generator); + boolean isLocalRegulation = generator.getId().equals(generator.getRegulatingTerminal().getConnectable().getId()); + + if (isLocalRegulation) { + if (generator.isVoltageRegulatorOn()) { + localActiveVoltageReferences.add(targetV); + } else { + localInactiveVoltageReferences.add(targetV); + } + } else { + if (generator.isVoltageRegulatorOn()) { + remoteActiveVoltageReferences.add(targetV); + } else { + remoteInactiveVoltageReferences.add(targetV); + } + } + } + private static double computeMaxPower(List powers) { return powers.isEmpty() || powers.contains(Double.MAX_VALUE) || powers.contains((double) DEFAULT_POWER_LIMIT) ? Double.NaN : powers.stream().reduce(Double::sum).orElse(Double.NaN); } @@ -106,9 +124,18 @@ private static UctePowerPlantType getUctePowerPlantType(Set return UctePowerPlantType.F; } - private static double getVoltageReference(List localVoltageReferences, List remoteVoltageReferences, double nominalV) { - return findClosestVoltageToNominalV(localVoltageReferences, nominalV) - .orElseGet(() -> findClosestVoltageToNominalV(remoteVoltageReferences, nominalV).orElse(Double.NaN)); + // Voltage reference depends on the generator connected to the bus, and if the regulation is remote or local + // Priority is given to: + // 1. TargetV of generators regulating locally + // 2. TargetV of generators regulating remotely + // 3. TargetV of generators with local deactivated regulation + // 4. TargetV of generators with remote deactivated regulation + // In any case, the value closest to the nominalV of the voltage level is kept. + private static double getVoltageReference(List localActiveVoltageReferences, List remoteActiveVoltageReferences, List localInactiveVoltageReferences, List remoteInactiveVoltageReferences, double nominalV) { + return findClosestVoltageToNominalV(localActiveVoltageReferences, nominalV) + .orElseGet(() -> findClosestVoltageToNominalV(remoteActiveVoltageReferences, nominalV) + .orElseGet(() -> findClosestVoltageToNominalV(localInactiveVoltageReferences, nominalV) + .orElseGet(() -> findClosestVoltageToNominalV(remoteInactiveVoltageReferences, nominalV).orElse(Double.NaN)))); } private static Optional findClosestVoltageToNominalV(List voltageReferences, double nominalV) { diff --git a/ucte/ucte-converter/src/test/java/com/powsybl/ucte/converter/UcteExporterGeneratorTest.java b/ucte/ucte-converter/src/test/java/com/powsybl/ucte/converter/UcteExporterGeneratorTest.java index 04e74f6086b..2a58675816c 100644 --- a/ucte/ucte-converter/src/test/java/com/powsybl/ucte/converter/UcteExporterGeneratorTest.java +++ b/ucte/ucte-converter/src/test/java/com/powsybl/ucte/converter/UcteExporterGeneratorTest.java @@ -96,6 +96,19 @@ void testRemoteAndLocalRegulatingGenerator() throws IOException { testExporter(network, "/eurostag.uct", p); } + @Test + void testActiveRemoteAndInactiveLocalRegulatingGenerator() throws IOException { + // Create a second generator and make it regulate remotely after the transformer with a different targetV + createGen2AndSplitGeneration(); + network.getGenerator("GEN2").setRegulatingTerminal(network.getTwoWindingsTransformer("NGEN_NHV1").getTerminal2()).setTargetV(24.5*380/24); + // The local generator has inactive regulation + network.getGenerator("GEN").setVoltageRegulatorOn(false).setTargetV(25); + Properties p = new Properties(); + p.put(UcteExporter.NAMING_STRATEGY, "Counter"); + // TargetV exported will be the one of the local generator GEN and not the targetV of the remote generator + testExporter(network, "/eurostag.uct", p); + } + private void createGen2AndSplitGeneration() { // Splits generation on two generators network.getVoltageLevel("VLGEN").newGenerator()