Skip to content
Draft
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,7 @@ core.ucte.export.busesAndSwitches = Buses and Switches
core.ucte.export.fileWritten = Network exported to file ${fileName}
core.ucte.export.lines = Lines
core.ucte.export.networkCreation = Creating UCTE Network
core.ucte.export.nominalPowerMissing = Transformer ${transformerId}: No nominal power provided. Defaulting to ${nominalPowerNovalue}
core.ucte.export.switchCurrentLimitMissing = Switch ${switchId}: No current limit provided
core.ucte.export.tieLines = Tie-Lines
core.ucte.export.transformers = Transformers
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,7 @@ core.ucte.export.busesAndSwitches = Noeuds et organes de coupure
core.ucte.export.fileWritten = Réseau exporté vers le fichier ${fileName}.
core.ucte.export.lines = Lignes
core.ucte.export.networkCreation = Création du réseau UCTE
core.ucte.export.nominalPowerMissing = Transformateur ${transformerId}: aucune puissance nominale fournie. Export de la valeur par défaut ${nominalPowerNovalue}
core.ucte.export.switchCurrentLimitMissing = Organe de coupure ${switchId} : aucune limite de courant fournie.
core.ucte.export.tieLines = Interconnexions
core.ucte.export.transformers = Transformateurs
Expand Down
16 changes: 12 additions & 4 deletions docs/grid_exchange_formats/ucte/export.md
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,7 @@ The table below maps every UCTE-DEF transformer attribute to its source in IIDM.
| Element name | `elementName` property | Used as-is; left undefined if the property is absent. |
| Rated voltage 1 (kV, non-regulated winding) | Transformer's `ratedU2` | Used as-is; swapped with rated voltage 2, see [Rated voltages](#rated-voltages). |
| Rated voltage 2 (kV, regulated winding) | Transformer's `ratedU1` | Used as-is; swapped with rated voltage 1, see [Rated voltages](#rated-voltages). |
| Nominal power (MVA) | `nomimalPower` property | Parsed as a double; left undefined if the property is absent, and a warning is [reported](#reporting). |
| Nominal power (MVA) | Transformer's `ratedS` | See [Nominal power](#nominal-power). |
| Conductance G (μS) | Transformer's `g` | Used as-is. |

#### Rated voltages
Expand All @@ -217,7 +217,15 @@ Because UCTE-DEF has the regulated winding on side 2, while IIDM has it on side
export: the UCTE transformer's rated voltage 1 (non-regulated winding) is the transformer's `ratedU2`, and its rated
voltage 2 (regulated winding) is its `ratedU1`.

If the transformer has a ratio and/or a phase tap changer, a regulation is exported.
#### Nominal power

The nominal power is the transformer's rated apparent power `ratedS`, used as-is. If `ratedS` is undefined
(`Double.NaN`), the value of the `nomimalPower` property is used instead: the property was set by the UCTE-DEF import in
older PowSyBl versions, and this is to remain retro-compatible with networks serialized with these older versions.
Comment thread
armic-artelys marked this conversation as resolved.
Outdated

If both `ratedS` is `NaN` and legacy property `nomimalPower` is absent or null, `99999` MVA is written in the file, and
a warning is [reported](#reporting). This value is chosen because it cannot be mistaken for the nominal power of a real
transformer (too large).

#### Phase regulation

Expand Down Expand Up @@ -283,5 +291,5 @@ conversion step (buses and switches, boundary lines, lines, tie lines, transform
following situations are reported with a `WARN` severity:

- a switch has no usable `currentLimit` property (see [Current limit](#current-limit)),
- a two-winding transformer has no usable nominal power
(see [two-winding transformer conversion](#two-winding-transformer-conversion)).
- a two-winding transformer has no nominal power, and the default value `99999` is exported instead
(see [Nominal power](#nominal-power)).
3 changes: 2 additions & 1 deletion docs/grid_exchange_formats/ucte/import.md
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,8 @@ The element name of the UCTE line is stored in the `elementName` property and th
### Two-winding transformer conversion
The two-winding transformers connected between two real nodes are converted into a [two-winding transformer](../../grid_model/network_subnetwork.md#two-winding-transformer).
If the current limits are defined, a permanent limit is created only for the second side.
The element name of the transformer is stored in the `elementName` property and the nominal power is stored in the `nominalPower` property.
The element name of the transformer is stored in the `elementName` property and the nominal power is stored in the dedicated transformer field for rated
apparent power: `ratedS`.
Comment thread
armic-artelys marked this conversation as resolved.
Outdated

If a two-winding transformer is connected between a real node and an X-node, a fictitious intermediate voltage level is created,
with a single bus called a Y-node. This new voltage level is created in the same substation as the real node.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ public class UcteExporter implements Exporter {

private static final List<Parameter> STATIC_PARAMETERS = List.of(NAMING_STRATEGY_PARAMETER, COMBINE_PHASE_ANGLE_REGULATION_PARAMETER);

public static final int NOMINAL_POWER_NOVALUE = 99999;

private final ParameterDefaultValueConfig defaultValueConfig;

public UcteExporter() {
Expand Down Expand Up @@ -669,10 +671,9 @@ private static void convertTwoWindingsTransformer(UcteNetwork ucteNetwork, TwoWi
UcteElementId elementId = context.getNamingStrategy().getUcteElementId(twoWindingsTransformer);
UcteElementStatus status = getStatus(twoWindingsTransformer);
String elementName = twoWindingsTransformer.getProperty(ELEMENT_NAME_PROPERTY_KEY, null);
double nominalPower = Double.NaN;
if (twoWindingsTransformer.hasProperty(NOMINAL_POWER_KEY)) {
nominalPower = Double.parseDouble(twoWindingsTransformer.getProperty(NOMINAL_POWER_KEY, null));
}

double nominalPower;
nominalPower = extractNominalPowerFromTransformer(twoWindingsTransformer, context);

UcteTransformer ucteTransformer = new UcteTransformer(
elementId,
Expand All @@ -691,6 +692,36 @@ private static void convertTwoWindingsTransformer(UcteNetwork ucteNetwork, TwoWi
convertRegulation(ucteNetwork, elementId, twoWindingsTransformer, context.withCombinePhaseAngleRegulation());
}

/**
* Extract a nominal power value from the provided transformer. It should be present in the dedicated
* {@link TwoWindingsTransformer} as the {@code ratedS} field. For retro-compatibility, if ratedS is {@code NaN},
* also look in the {@code nomimalPower}" property (now deprecated).
Comment thread
jeandemanged marked this conversation as resolved.
* <br>
* If both are absent, return default value {@code 99999} and log + report a warning
* @param twoWindingsTransformer a transformer
* @return The nominal power of the transformer
*/
private static double extractNominalPowerFromTransformer(TwoWindingsTransformer twoWindingsTransformer,
UcteExporterContext context) {
if (!Double.isNaN(twoWindingsTransformer.getRatedS())) {
return twoWindingsTransformer.getRatedS();
}
String legacyNominalPowerProperty = twoWindingsTransformer.getProperty(NOMINAL_POWER_KEY, null);
if (legacyNominalPowerProperty != null) {
double legacyNominalPower = Double.parseDouble(legacyNominalPowerProperty);
if (!Double.isNaN(legacyNominalPower)) {
return legacyNominalPower;
}
}
LOGGER.warn("Transformer {}: No nominal power provided. Defaulting to {}",
twoWindingsTransformer.getId(),
NOMINAL_POWER_NOVALUE);
UcteExporterReports.nominalPowerMissing(context.getReportNode(),
twoWindingsTransformer.getId(),
NOMINAL_POWER_NOVALUE);
return NOMINAL_POWER_NOVALUE;
}

/**
* Creates and adds to the ucteNetwork the {@link UcteRegulation} linked to the TwoWindingsTransformer.
* <li>{@link RatioTapChanger} into {@link UctePhaseRegulation}</li>
Expand Down Expand Up @@ -795,15 +826,18 @@ private static boolean isSymm(TwoWindingsTransformer twoWindingsTransformer) {
}

private static void setSwitchCurrentLimit(UcteLine ucteLine, Switch sw, UcteExporterContext context) {
boolean wasSet = false;
if (sw.hasProperty(CURRENT_LIMIT_PROPERTY_KEY)) {
try {
ucteLine.setCurrentLimit(Integer.parseInt(sw.getProperty(CURRENT_LIMIT_PROPERTY_KEY)));
} catch (NumberFormatException exception) {
ucteLine.setCurrentLimit(null);
LOGGER.warn("Switch {}: No current limit provided", sw.getId());
UcteExporterReports.switchCurrentLimitMissing(context.getReportNode(), sw.getId());
wasSet = true;
} catch (NumberFormatException e) {
LOGGER.debug("Couldn't parse number '{}': {}",
sw.getProperty(CURRENT_LIMIT_PROPERTY_KEY),
e.getMessage());
}
} else {
}
if (!wasSet) {
ucteLine.setCurrentLimit(null);
LOGGER.warn("Switch {}: No current limit provided", sw.getId());
UcteExporterReports.switchCurrentLimitMissing(context.getReportNode(), sw.getId());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -807,7 +807,7 @@ private static void createTransformers(UcteNetworkExt ucteNetwork, Network netwo

addElementNameProperty(ucteTransfo, transformer);
addTapChangers(ucteNetwork, ucteTransfo, transformer, combinePhaseAngleRegulation);
addNominalPowerProperty(ucteTransfo, transformer);
transformer.setRatedS(ucteTransfo.getNominalPower());
}
}

Expand Down Expand Up @@ -882,12 +882,6 @@ private static void addOrderCodeProperty(UcteLine ucteLine, Switch sw) {
sw.setProperty(ORDER_CODE, String.valueOf(ucteLineId.charAt(ucteLineId.length() - 1)));
}

private static void addNominalPowerProperty(UcteTransformer transformer, TwoWindingsTransformer twoWindingsTransformer) {
if (!Double.isNaN(transformer.getNominalPower())) {
twoWindingsTransformer.setProperty(NOMINAL_POWER_KEY, String.valueOf(transformer.getNominalPower()));
}
}

private static void addXnodeStatusProperty(UcteNode ucteNode, Identifiable<?> identifiable) {
identifiable.setProperty(STATUS_PROPERTY_KEY + X_NODE, ucteNode.getStatus().toString());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@
import com.powsybl.commons.report.ReportNode;
import com.powsybl.commons.report.TypedValue;

import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.util.Locale;

/**
* Message-templated {@link ReportNode} entries reported by {@link com.powsybl.ucte.converter.UcteExporter}.
*
Expand Down Expand Up @@ -70,4 +74,29 @@ public static void switchCurrentLimitMissing(ReportNode reportNode, String switc
.withSeverity(TypedValue.WARN_SEVERITY)
.add();
}

public static void nominalPowerMissing(ReportNode reportNode, String transformerId, double nominalPowerNovalue) {
reportNode.newReportNode()
.withMessageTemplate("core.ucte.export.nominalPowerMissing")
.withUntypedValue("transformerId", transformerId)
// nominal power is exported in a 5 chars column
.withUntypedValue("nominalPowerNovalue", formatAsExported(nominalPowerNovalue, 5))
Comment thread
jeandemanged marked this conversation as resolved.
Outdated
.withSeverity(TypedValue.WARN_SEVERITY)
.add();
}

/**
* Formats a double value the same way {@link com.powsybl.ucte.network.io.UcteWriter} exports it: as a decimal
* string truncated to {@code fieldLength} characters.
*/
private static String formatAsExported(double value, int fieldLength) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is dead code, remove.

DecimalFormat numberFormatter = new DecimalFormat();
numberFormatter.setGroupingUsed(false);
numberFormatter.setDecimalFormatSymbols(DecimalFormatSymbols.getInstance(Locale.US));
numberFormatter.setMinimumIntegerDigits(1);
numberFormatter.setMinimumFractionDigits(1);
numberFormatter.setMaximumFractionDigits(fieldLength);
String formatted = numberFormatter.format(value);
return formatted.length() > fieldLength ? formatted.substring(0, fieldLength) : formatted;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,31 @@ void testExportReportsNetworkCreationAndFileWriting() {
""", rootReportNode));
}

/**
* Checks that a transformer with no nominal power (no {@code ratedS} and no legacy property) is reported
* when exporting the network.
*/
@Test
void testNominalPowerMissingReported() {
Network network = loadNetworkFromResourceFile("/expectedExport.uct");
network.getTwoWindingsTransformer("F_SU1_11 F_SU1_21 1").setRatedS(Double.NaN);

ReportNode rootReportNode = newTestRootReportNode();
new UcteExporter().export(network, new Properties(), new MemDataSource(), rootReportNode);

assertTrue(checkReportNode("""
+ Test exporting UCTE network
+ Creating UCTE Network
Buses and Switches
Boundary Lines
Lines
Tie-Lines
+ Transformers
Transformer F_SU1_11 F_SU1_21 1: No nominal power provided. Defaulting to 99999
Network exported to file .uct
""", rootReportNode));
}

/**
* Checks that a closed switch with no current limit is reported when exporting the network.
* <p>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,12 @@
import com.powsybl.commons.PowsyblException;
import com.powsybl.commons.datasource.MemDataSource;
import com.powsybl.commons.datasource.ReadOnlyDataSource;
import com.powsybl.commons.datasource.ReadOnlyMemDataSource;
import com.powsybl.commons.datasource.ResourceDataSource;
import com.powsybl.commons.datasource.ResourceSet;
import com.powsybl.commons.test.AbstractSerDeTest;
import com.powsybl.iidm.network.*;
import com.powsybl.ucte.converter.util.UcteConverterConstants;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

checkstyle error

import com.powsybl.iidm.network.regulation.RegulationMode;
import org.apache.commons.io.FilenameUtils;
import org.junit.jupiter.api.Test;
Expand Down Expand Up @@ -49,6 +51,18 @@ private static Network loadNetworkFromResourceFile(String filePath, Properties p
return new UcteImporter().importData(dataSource, NetworkFactory.findDefault(), parameters);
}

/**
* Re-imports a network exported to a {@link MemDataSource}. A plain {@link MemDataSource} always stores its
* content under an empty base name, which {@link UcteImporter} would otherwise turn into an invalid empty
* network id, so the exported bytes are copied into a named {@link ReadOnlyMemDataSource} first.
*/
private static Network reimport(MemDataSource dataSource) {
byte[] exported = dataSource.getData(null, "uct");
ReadOnlyMemDataSource namedDataSource = new ReadOnlyMemDataSource("network");
namedDataSource.putData("network.uct", exported);
return new UcteImporter().importData(namedDataSource, NetworkFactory.findDefault(), null);
}

private static void testExporter(Network network, String reference) throws IOException {
testExporter(network, reference, new Properties());
}
Expand Down Expand Up @@ -261,6 +275,48 @@ void testTapChangers() {
exportedNetwork.getTwoWindingsTransformer(ptcId2).getPhaseTapChanger().getCurrentStep().getAlpha(), 0.0001);
}

@Test
void testNominalPowerExportedFromRatedS() {
Network network = loadNetworkFromResourceFile("/expectedExport.uct");
TwoWindingsTransformer transformer = network.getTwoWindingsTransformer("F_SU1_11 F_SU1_21 1");
transformer.setRatedS(1234.5);

MemDataSource dataSource = new MemDataSource();
new UcteExporter().export(network, new Properties(), dataSource);
Network reimported = reimport(dataSource);

// Here, we accept a difference of 1 (the exporter can only export 5 characters, so exported value is '1234.')
assertEquals(1234.5, reimported.getTwoWindingsTransformer("F_SU1_11 F_SU1_21 1").getRatedS(), 1);
}

@Test
void testNominalPowerExportedFromLegacyPropertyWhenRatedSMissing() {
Network network = loadNetworkFromResourceFile("/expectedExport.uct");
TwoWindingsTransformer transformer = network.getTwoWindingsTransformer("F_SU1_11 F_SU1_21 1");
transformer.setRatedS(Double.NaN);
transformer.setProperty(UcteConverterConstants.NOMINAL_POWER_KEY, "1200.0");

MemDataSource dataSource = new MemDataSource();
new UcteExporter().export(network, new Properties(), dataSource);
Network reimported = reimport(dataSource);

assertEquals(1200.0, reimported.getTwoWindingsTransformer("F_SU1_11 F_SU1_21 1").getRatedS());
}

@Test
void testNominalPowerDefaultedWhenNotProvided() {
Network network = loadNetworkFromResourceFile("/expectedExport.uct");
TwoWindingsTransformer transformer = network.getTwoWindingsTransformer("F_SU1_11 F_SU1_21 1");
transformer.setRatedS(Double.NaN);

MemDataSource dataSource = new MemDataSource();
new UcteExporter().export(network, new Properties(), dataSource);
Network reimported = reimport(dataSource);

assertEquals(UcteExporter.NOMINAL_POWER_NOVALUE,
reimported.getTwoWindingsTransformer("F_SU1_11 F_SU1_21 1").getRatedS());
}

@Test
void roundTripOfCombineRtcAndPtc() throws IOException {
Properties parameters = new Properties();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,22 @@ void testDontCreateAreas() {
assertEquals(0, network.getAreaCount());
}

@Test
void testNominalPowerImportedAsRatedS() {
ResourceDataSource dataSource =
new ResourceDataSource("nominalPower", new ResourceSet("/", "nominalPower.uct"));
Network network = new UcteImporter().importData(dataSource, NetworkFactory.findDefault(), null);
assertEquals(5000., network.getTwoWindingsTransformer("F_SU1_11 F_SU1_21 1").getRatedS());
}

@Test
void testMissingNominalPowerImportedAsNanRatedS() {
ResourceDataSource dataSource =
new ResourceDataSource("nominalPower", new ResourceSet("/", "nominalPower.uct"));
Network network = new UcteImporter().importData(dataSource, NetworkFactory.findDefault(), null);
assertTrue(Double.isNaN(network.getTwoWindingsTransformer("B_SU1_11 B_SU1_21 1").getRatedS()));
}
Comment thread
jeandemanged marked this conversation as resolved.

@Test
void testMetaInfos() throws IOException {
try (var fs = Jimfs.newFileSystem(Configuration.unix())) {
Expand Down
15 changes: 15 additions & 0 deletions ucte/ucte-converter/src/test/resources/nominalPower.uct
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
##C 2007.05.01
Generated by powsybl, 2019.09.23 17:47
Case date: 2019.09.24 19:30
##N
##ZBE
B_SU1_11 Belgium Nod1 0 0 400.00 50.0000 0.00000 0.00000 0.00000
B_SU1_21 Belgium Nod2 0 3 225.00 0.00000 0.00000 100.000 0.00000
##ZFR
F_SU1_11 France Node1 0 0 400.00 50.0000 0.00000 0.00000 0.00000
F_SU1_21 France Node2 0 0 225.00 0.00000 0.00000 0.00000 0.00000
##L
##T
F_SU1_11 F_SU1_21 1 0 400.0 225.0 5000. 0.5500 1.6800 132.5000 200.00 5000 Test 2WT 1
B_SU1_11 B_SU1_21 1 0 400.0 225.0 0.5500 1.6800 13.25000 0.0000 5000 Test 2WT 2
##R
Loading