}
+ */
+public final class SeasonConversion {
+
+ private SeasonConversion() {
+ // Utility class
+ }
+
+ public static IeeeCdfTitle.Season fromString(String str) {
+ char season = str.charAt(0);
+ return switch (season) {
+ case 'S' -> IeeeCdfTitle.Season.SUMMER;
+ case 'W' -> IeeeCdfTitle.Season.WINTER;
+ default -> throw new IllegalStateException("Unknown season: " + season);
+ };
+ }
+
+ public static String revert(IeeeCdfTitle.Season season) {
+ return switch (season) {
+ case SUMMER -> "S";
+ case WINTER -> "W";
+ default -> throw new IllegalStateException("Unknown season: " + season);
+ };
+ }
+}
diff --git a/ieee-cdf/ieee-cdf-model/src/main/java/com/powsybl/ieeecdf/model/elements/AbstractIeeeElement.java b/ieee-cdf/ieee-cdf-model/src/main/java/com/powsybl/ieeecdf/model/elements/AbstractIeeeElement.java
new file mode 100644
index 00000000000..0a973e10493
--- /dev/null
+++ b/ieee-cdf/ieee-cdf-model/src/main/java/com/powsybl/ieeecdf/model/elements/AbstractIeeeElement.java
@@ -0,0 +1,17 @@
+/*
+ * Copyright (c) 2025, RTE (http://www.rte-france.com)
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ * SPDX-License-Identifier: MPL-2.0
+ */
+package com.powsybl.ieeecdf.model.elements;
+
+/**
+ * @author Nicolas Rol {@literal }
+ */
+public abstract class AbstractIeeeElement {
+ protected AbstractIeeeElement() {
+ // Constructor
+ }
+}
diff --git a/ieee-cdf/ieee-cdf-model/src/main/java/com/powsybl/ieeecdf/model/IeeeCdfBranch.java b/ieee-cdf/ieee-cdf-model/src/main/java/com/powsybl/ieeecdf/model/elements/IeeeCdfBranch.java
similarity index 81%
rename from ieee-cdf/ieee-cdf-model/src/main/java/com/powsybl/ieeecdf/model/IeeeCdfBranch.java
rename to ieee-cdf/ieee-cdf-model/src/main/java/com/powsybl/ieeecdf/model/elements/IeeeCdfBranch.java
index 6e2c3a98c90..5a7a6aa1ea6 100644
--- a/ieee-cdf/ieee-cdf-model/src/main/java/com/powsybl/ieeecdf/model/IeeeCdfBranch.java
+++ b/ieee-cdf/ieee-cdf-model/src/main/java/com/powsybl/ieeecdf/model/elements/IeeeCdfBranch.java
@@ -1,16 +1,11 @@
-/**
- * Copyright (c) 2019, RTE (http://www.rte-france.com)
+/*
+ * Copyright (c) 2019-2025, RTE (http://www.rte-france.com)
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
* SPDX-License-Identifier: MPL-2.0
*/
-package com.powsybl.ieeecdf.model;
-
-import com.univocity.parsers.annotations.Convert;
-import com.univocity.parsers.annotations.FixedWidth;
-import com.univocity.parsers.annotations.Parsed;
-import com.univocity.parsers.fixed.FieldAlignment;
+package com.powsybl.ieeecdf.model.elements;
/**
*
@@ -55,7 +50,7 @@
*
* @author Geoffroy Jamgotchian {@literal }
*/
-public class IeeeCdfBranch {
+public class IeeeCdfBranch extends AbstractIeeeElement {
/**
* 0 - Transmission line
@@ -81,157 +76,111 @@ public enum Side {
/**
* Tap bus number (I) *
*/
- @FixedWidth(from = 0, to = 4, alignment = FieldAlignment.RIGHT)
- @Parsed
private int tapBusNumber;
/**
* Z bus number (I) *
*/
- @FixedWidth(from = 5, to = 9, alignment = FieldAlignment.RIGHT)
- @Parsed
private int zBusNumber;
/**
* Load flow area (I)
*/
- @FixedWidth(from = 10, to = 12, alignment = FieldAlignment.RIGHT)
- @Parsed
private int area;
/**
* Loss zone (I)
*/
- @FixedWidth(from = 13, to = 15, alignment = FieldAlignment.RIGHT)
- @Parsed
private int lossZone;
/**
* Circuit (I) * (Use 1 for single lines)
*/
- @FixedWidth(from = 16, to = 17, alignment = FieldAlignment.RIGHT)
- @Parsed
private int circuit;
/**
* Type (I) *
*/
- @FixedWidth(from = 18, to = 19)
- @Parsed
- @Convert(conversionClass = BranchTypeConversion.class)
private Type type;
/**
* Branch resistance R, per unit (F) *
*/
- @FixedWidth(from = 19, to = 29, alignment = FieldAlignment.RIGHT)
- @Parsed
private double resistance;
/**
* Branch reactance X, per unit (F) * No zero impedance lines
*/
- @FixedWidth(from = 29, to = 40, alignment = FieldAlignment.RIGHT)
- @Parsed
private double reactance;
/**
* Line charging B, per unit (F) * (total line charging, +B)
*/
- @FixedWidth(from = 40, to = 50, alignment = FieldAlignment.RIGHT)
- @Parsed
private double chargingSusceptance;
/**
* Line MVA rating No 1 (I) Left justify!
*/
- @FixedWidth(from = 50, to = 55, alignment = FieldAlignment.RIGHT)
- @Parsed
private int rating1;
/**
* Line MVA rating No 2 (I) Left justify!
*/
- @FixedWidth(from = 56, to = 61, alignment = FieldAlignment.RIGHT)
- @Parsed
private int rating2;
/**
* Line MVA rating No 3 (I) Left justify!
*/
- @FixedWidth(from = 62, to = 67, alignment = FieldAlignment.RIGHT)
- @Parsed
private int rating3;
/**
* Control bus number
*/
- @FixedWidth(from = 68, to = 72, alignment = FieldAlignment.RIGHT)
- @Parsed
private int controlBusNumber;
/**
* Side (I)
*/
- @FixedWidth(from = 73, to = 74, alignment = FieldAlignment.RIGHT)
- @Parsed
- @Convert(conversionClass = BranchSideConversion.class)
private Side side;
/**
* Transformer final turns ratio (F)
*/
- @FixedWidth(from = 76, to = 82)
- @Parsed
private double finalTurnsRatio;
/**
* Transformer (phase shifter) final angle (F)
*/
- @FixedWidth(from = 83, to = 90)
- @Parsed
private double finalAngle;
/**
* Minimum tap or phase shift (F)
*/
- @FixedWidth(from = 90, to = 97)
- @Parsed
private double minTapOrPhaseShift;
/**
* Maximum tap or phase shift (F)
*/
- @FixedWidth(from = 97, to = 104)
- @Parsed
private double maxTapOrPhaseShift;
/**
* Step size (F)
*/
- @FixedWidth(from = 105, to = 112)
- @Parsed
private double stepSize;
/**
* Minimum voltage, MVAR or MW limit (F)
*/
- @FixedWidth(from = 112, to = 118)
- @Parsed
private double minVoltageActiveOrReactivePowerLimit;
/**
* Maximum voltage, MVAR or MW limit (F)
*/
- @FixedWidth(from = 119, to = 126)
- @Parsed
private double maxVoltageActiveOrReactivePowerLimit;
/**
* This parameter does not exist in the specification but is present in 300 buses case.
*/
- @FixedWidth(from = 126, to = 133)
- @Parsed
private int unused;
public int getTapBusNumber() {
@@ -401,4 +350,12 @@ public double getMaxVoltageActiveOrReactivePowerLimit() {
public void setMaxVoltageActiveOrReactivePowerLimit(double maxVoltageActiveOrReactivePowerLimit) {
this.maxVoltageActiveOrReactivePowerLimit = maxVoltageActiveOrReactivePowerLimit;
}
+
+ public int getUnused() {
+ return unused;
+ }
+
+ public void setUnused(int unused) {
+ this.unused = unused;
+ }
}
diff --git a/ieee-cdf/ieee-cdf-model/src/main/java/com/powsybl/ieeecdf/model/IeeeCdfBus.java b/ieee-cdf/ieee-cdf-model/src/main/java/com/powsybl/ieeecdf/model/elements/IeeeCdfBus.java
similarity index 82%
rename from ieee-cdf/ieee-cdf-model/src/main/java/com/powsybl/ieeecdf/model/IeeeCdfBus.java
rename to ieee-cdf/ieee-cdf-model/src/main/java/com/powsybl/ieeecdf/model/elements/IeeeCdfBus.java
index 4e5ae60825f..44cf83bbe52 100644
--- a/ieee-cdf/ieee-cdf-model/src/main/java/com/powsybl/ieeecdf/model/IeeeCdfBus.java
+++ b/ieee-cdf/ieee-cdf-model/src/main/java/com/powsybl/ieeecdf/model/elements/IeeeCdfBus.java
@@ -1,16 +1,11 @@
-/**
- * Copyright (c) 2019, RTE (http://www.rte-france.com)
+/*
+ * Copyright (c) 2019-2025, RTE (http://www.rte-france.com)
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
* SPDX-License-Identifier: MPL-2.0
*/
-package com.powsybl.ieeecdf.model;
-
-import com.univocity.parsers.annotations.Convert;
-import com.univocity.parsers.annotations.FixedWidth;
-import com.univocity.parsers.annotations.Parsed;
-import com.univocity.parsers.fixed.FieldAlignment;
+package com.powsybl.ieeecdf.model.elements;
/**
*
@@ -44,7 +39,7 @@
*
* @author Geoffroy Jamgotchian {@literal }
*/
-public class IeeeCdfBus {
+public class IeeeCdfBus extends AbstractIeeeElement {
/**
* 0 - Unregulated (load, PQ)
@@ -62,133 +57,97 @@ public enum Type {
/**
* Bus number
*/
- @FixedWidth(from = 0, to = 4, alignment = FieldAlignment.RIGHT)
- @Parsed
private int number;
/**
* Name
*/
- @FixedWidth(from = 5, to = 17)
- @Parsed
private String name;
/**
* Load flow area number
*/
- @FixedWidth(from = 18, to = 20, alignment = FieldAlignment.RIGHT)
- @Parsed
private int areaNumber;
/**
* Loss zone number
*/
- @FixedWidth(from = 20, to = 23, alignment = FieldAlignment.RIGHT)
- @Parsed
private int lossZoneNumber;
/**
* Type
*/
- @FixedWidth(from = 24, to = 26, alignment = FieldAlignment.RIGHT)
- @Parsed
- @Convert(conversionClass = BusTypeConversion.class)
private Type type;
/**
* Final voltage, p.u.
*/
- @FixedWidth(from = 27, to = 33)
- @Parsed
private double finalVoltage;
/**
* Final angle, degrees
*/
- @FixedWidth(from = 33, to = 40)
- @Parsed
private double finalAngle;
/**
* Load MW
*/
- @FixedWidth(from = 40, to = 49)
- @Parsed
private double activeLoad;
/**
* Load MVAR
*/
- @FixedWidth(from = 49, to = 59)
- @Parsed
private double reactiveLoad;
/**
* Generation MW
*/
- @FixedWidth(from = 59, to = 67)
- @Parsed
private double activeGeneration;
/**
* Generation MVAR
*/
- @FixedWidth(from = 67, to = 75)
- @Parsed
private double reactiveGeneration;
/**
* Base KV
*/
- @FixedWidth(from = 76, to = 83)
- @Parsed
private double baseVoltage;
/**
* Desired volts (pu) (F) (This is desired remote voltage if this bus is controlling another bus.
*/
- @FixedWidth(from = 84, to = 90)
- @Parsed
private double desiredVoltage;
/**
* Maximum MVAR or voltage limit
*/
- @FixedWidth(from = 90, to = 98)
- @Parsed
private double maxReactivePowerOrVoltageLimit;
/**
* Minimum MVAR or voltage limit
*/
- @FixedWidth(from = 98, to = 106)
- @Parsed
private double minReactivePowerOrVoltageLimit;
/**
* Shunt conductance G (per unit)
*/
- @FixedWidth(from = 106, to = 114)
- @Parsed
private double shuntConductance;
/**
* Shunt susceptance B (per unit)
*/
- @FixedWidth(from = 114, to = 122)
- @Parsed
private double shuntSusceptance;
/**
* Remote controlled bus number
*/
- @FixedWidth(from = 123, to = 127)
- @Parsed
private int remoteControlledBusNumber;
- @FixedWidth(from = 127, to = 133)
- @Parsed
- private int unused;
+ /**
+ * Additional columns
+ */
+ private Integer unused = null;
public int getNumber() {
return number;
@@ -333,4 +292,12 @@ public int getRemoteControlledBusNumber() {
public void setRemoteControlledBusNumber(int remoteControlledBusNumber) {
this.remoteControlledBusNumber = remoteControlledBusNumber;
}
+
+ public Integer getUnused() {
+ return unused;
+ }
+
+ public void setUnused(int unused) {
+ this.unused = unused;
+ }
}
diff --git a/ieee-cdf/ieee-cdf-model/src/main/java/com/powsybl/ieeecdf/model/IeeeCdfInterchangeData.java b/ieee-cdf/ieee-cdf-model/src/main/java/com/powsybl/ieeecdf/model/elements/IeeeCdfInterchangeData.java
similarity index 83%
rename from ieee-cdf/ieee-cdf-model/src/main/java/com/powsybl/ieeecdf/model/IeeeCdfInterchangeData.java
rename to ieee-cdf/ieee-cdf-model/src/main/java/com/powsybl/ieeecdf/model/elements/IeeeCdfInterchangeData.java
index abd8e4e5bb1..cc19554a94d 100644
--- a/ieee-cdf/ieee-cdf-model/src/main/java/com/powsybl/ieeecdf/model/IeeeCdfInterchangeData.java
+++ b/ieee-cdf/ieee-cdf-model/src/main/java/com/powsybl/ieeecdf/model/elements/IeeeCdfInterchangeData.java
@@ -1,14 +1,11 @@
-/**
- * Copyright (c) 2019, RTE (http://www.rte-france.com)
+/*
+ * Copyright (c) 2019-2025, RTE (http://www.rte-france.com)
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
* SPDX-License-Identifier: MPL-2.0
*/
-package com.powsybl.ieeecdf.model;
-
-import com.univocity.parsers.annotations.FixedWidth;
-import com.univocity.parsers.annotations.Parsed;
+package com.powsybl.ieeecdf.model.elements;
/**
*
@@ -27,55 +24,41 @@
*
* @author Geoffroy Jamgotchian {@literal }
*/
-public class IeeeCdfInterchangeData {
+public class IeeeCdfInterchangeData extends AbstractIeeeElement {
/**
* Area number
*/
- @FixedWidth(from = 0, to = 2)
- @Parsed
private int areaNumber;
/**
* Interchange slack bus number
*/
- @FixedWidth(from = 3, to = 7)
- @Parsed
private int interchangeSlackBusNumber;
/**
* Alternate swing bus name
*/
- @FixedWidth(from = 8, to = 20)
- @Parsed
private String alternateSwingBusName;
/**
* Area interchange export, MW
*/
- @FixedWidth(from = 20, to = 28)
- @Parsed
private double areaInterchangeExport;
/**
* Area interchange tolerance, MW
*/
- @FixedWidth(from = 29, to = 35)
- @Parsed
private double areaInterchangeTolerance;
/**
* Area code (abbreviated name)
*/
- @FixedWidth(from = 37, to = 43)
- @Parsed
private String areaCode;
/**
* Area name
*/
- @FixedWidth(from = 45, to = 75)
- @Parsed
private String areaName;
public int getAreaNumber() {
diff --git a/ieee-cdf/ieee-cdf-model/src/main/java/com/powsybl/ieeecdf/model/IeeeCdfLossZone.java b/ieee-cdf/ieee-cdf-model/src/main/java/com/powsybl/ieeecdf/model/elements/IeeeCdfLossZone.java
similarity index 75%
rename from ieee-cdf/ieee-cdf-model/src/main/java/com/powsybl/ieeecdf/model/IeeeCdfLossZone.java
rename to ieee-cdf/ieee-cdf-model/src/main/java/com/powsybl/ieeecdf/model/elements/IeeeCdfLossZone.java
index 3c7e433e788..ec95b9d0a60 100644
--- a/ieee-cdf/ieee-cdf-model/src/main/java/com/powsybl/ieeecdf/model/IeeeCdfLossZone.java
+++ b/ieee-cdf/ieee-cdf-model/src/main/java/com/powsybl/ieeecdf/model/elements/IeeeCdfLossZone.java
@@ -1,14 +1,11 @@
-/**
- * Copyright (c) 2019, RTE (http://www.rte-france.com)
+/*
+ * Copyright (c) 2019-2025, RTE (http://www.rte-france.com)
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
* SPDX-License-Identifier: MPL-2.0
*/
-package com.powsybl.ieeecdf.model;
-
-import com.univocity.parsers.annotations.FixedWidth;
-import com.univocity.parsers.annotations.Parsed;
+package com.powsybl.ieeecdf.model.elements;
/**
*
@@ -22,20 +19,16 @@
*
* @author Geoffroy Jamgotchian {@literal }
*/
-public class IeeeCdfLossZone {
+public class IeeeCdfLossZone extends AbstractIeeeElement {
/**
* Loss zone number
*/
- @FixedWidth(from = 0, to = 3)
- @Parsed
private int number;
/**
* Loss zone name
*/
- @FixedWidth(from = 4, to = 16)
- @Parsed
private String name;
public int getNumber() {
diff --git a/ieee-cdf/ieee-cdf-model/src/main/java/com/powsybl/ieeecdf/model/IeeeCdfTieLine.java b/ieee-cdf/ieee-cdf-model/src/main/java/com/powsybl/ieeecdf/model/elements/IeeeCdfTieLine.java
similarity index 81%
rename from ieee-cdf/ieee-cdf-model/src/main/java/com/powsybl/ieeecdf/model/IeeeCdfTieLine.java
rename to ieee-cdf/ieee-cdf-model/src/main/java/com/powsybl/ieeecdf/model/elements/IeeeCdfTieLine.java
index 22f6ff70789..1904681d6e3 100644
--- a/ieee-cdf/ieee-cdf-model/src/main/java/com/powsybl/ieeecdf/model/IeeeCdfTieLine.java
+++ b/ieee-cdf/ieee-cdf-model/src/main/java/com/powsybl/ieeecdf/model/elements/IeeeCdfTieLine.java
@@ -1,14 +1,11 @@
-/**
- * Copyright (c) 2019, RTE (http://www.rte-france.com)
+/*
+ * Copyright (c) 2019-2025, RTE (http://www.rte-france.com)
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
* SPDX-License-Identifier: MPL-2.0
*/
-package com.powsybl.ieeecdf.model;
-
-import com.univocity.parsers.annotations.FixedWidth;
-import com.univocity.parsers.annotations.Parsed;
+package com.powsybl.ieeecdf.model.elements;
/**
*
@@ -25,41 +22,31 @@
*
* @author Geoffroy Jamgotchian {@literal }
*/
-public class IeeeCdfTieLine {
+public class IeeeCdfTieLine extends AbstractIeeeElement {
/**
* Metered bus number
*/
- @FixedWidth(from = 0, to = 4)
- @Parsed
private int meteredBusNumber;
/**
* Metered area number
*/
- @FixedWidth(from = 6, to = 8)
- @Parsed
private int meteredAreaNumber;
/**
* Non-metered bus number
*/
- @FixedWidth(from = 10, to = 14)
- @Parsed
private int nonMeteredBusNumber;
/**
* Non-metered area number
*/
- @FixedWidth(from = 16, to = 18)
- @Parsed
private int nonMeteredAreaNumber;
/**
* Circuit number
*/
- @FixedWidth(from = 20, to = 21)
- @Parsed
private int circuitNumber;
public int getMeteredBusNumber() {
diff --git a/ieee-cdf/ieee-cdf-model/src/main/java/com/powsybl/ieeecdf/model/IeeeCdfTitle.java b/ieee-cdf/ieee-cdf-model/src/main/java/com/powsybl/ieeecdf/model/elements/IeeeCdfTitle.java
similarity index 76%
rename from ieee-cdf/ieee-cdf-model/src/main/java/com/powsybl/ieeecdf/model/IeeeCdfTitle.java
rename to ieee-cdf/ieee-cdf-model/src/main/java/com/powsybl/ieeecdf/model/elements/IeeeCdfTitle.java
index 6c17e62cec1..d256555d5d2 100644
--- a/ieee-cdf/ieee-cdf-model/src/main/java/com/powsybl/ieeecdf/model/IeeeCdfTitle.java
+++ b/ieee-cdf/ieee-cdf-model/src/main/java/com/powsybl/ieeecdf/model/elements/IeeeCdfTitle.java
@@ -1,15 +1,11 @@
-/**
- * Copyright (c) 2019, RTE (http://www.rte-france.com)
+/*
+ * Copyright (c) 2019-2025, RTE (http://www.rte-france.com)
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
* SPDX-License-Identifier: MPL-2.0
*/
-package com.powsybl.ieeecdf.model;
-
-import com.univocity.parsers.annotations.Convert;
-import com.univocity.parsers.annotations.FixedWidth;
-import com.univocity.parsers.annotations.Parsed;
+package com.powsybl.ieeecdf.model.elements;
import java.time.LocalDate;
@@ -29,7 +25,7 @@
*
* @author Geoffroy Jamgotchian {@literal }
*/
-public class IeeeCdfTitle {
+public class IeeeCdfTitle extends AbstractIeeeElement {
public enum Season {
SUMMER,
@@ -39,45 +35,31 @@ public enum Season {
/**
* Date
*/
- @FixedWidth(from = 1, to = 10)
- @Parsed
- @Convert(conversionClass = LocalDateConversion.class)
private LocalDate date;
/**
* Originator's name
*/
- @FixedWidth(from = 10, to = 30)
- @Parsed
private String originatorName;
/**
* MVA Base
*/
- @FixedWidth(from = 31, to = 37)
- @Parsed
private double mvaBase;
/**
* Year
*/
- @FixedWidth(from = 38, to = 42)
- @Parsed
private int year;
/**
* Season
*/
- @FixedWidth(from = 43, to = 44)
- @Parsed
- @Convert(conversionClass = SeasonConversion.class)
private Season season;
/**
* Case identification
*/
- @FixedWidth(from = 45, to = 73)
- @Parsed
private String caseIdentification;
public LocalDate getDate() {
diff --git a/ieee-cdf/ieee-cdf-model/src/main/java/com/powsybl/ieeecdf/model/reader/AbstractIeeeCdfReader.java b/ieee-cdf/ieee-cdf-model/src/main/java/com/powsybl/ieeecdf/model/reader/AbstractIeeeCdfReader.java
new file mode 100644
index 00000000000..2b9b23b5b42
--- /dev/null
+++ b/ieee-cdf/ieee-cdf-model/src/main/java/com/powsybl/ieeecdf/model/reader/AbstractIeeeCdfReader.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2025, RTE (http://www.rte-france.com)
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ * SPDX-License-Identifier: MPL-2.0
+ */
+package com.powsybl.ieeecdf.model.reader;
+
+import com.powsybl.ieeecdf.model.elements.AbstractIeeeElement;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Consumer;
+import java.util.function.DoubleConsumer;
+import java.util.function.Function;
+import java.util.function.IntConsumer;
+
+/**
+ * @author Nicolas Rol {@literal }
+ */
+public abstract class AbstractIeeeCdfReader {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(AbstractIeeeCdfReader.class);
+
+ protected AbstractIeeeCdfReader() {
+ }
+
+ protected static List readLines(BufferedReader reader, int footerValue,
+ Function constructor,
+ int expectedItemsNumber) throws IOException {
+ List ieeeElements = new ArrayList<>();
+ String currentLine;
+ while ((currentLine = reader.readLine()) != null) {
+ if (currentLine.startsWith(String.valueOf(footerValue))) {
+ break; // fin de section
+ }
+ ieeeElements.add(constructor.apply(currentLine));
+ }
+ if (ieeeElements.size() != expectedItemsNumber) {
+ LOGGER.warn("Wrong number of elements parsed in the IEEE-CDF file: {} (expected {})", ieeeElements.size(), expectedItemsNumber);
+ }
+ return ieeeElements;
+ }
+
+ protected static void readInteger(String line, int start, int end, IntConsumer consumer) {
+ if (start < line.length() + 1) {
+ String value = line.substring(start - 1, Math.min(end, line.length())).trim();
+ if (!value.isEmpty()) {
+ consumer.accept(Integer.parseInt(value));
+ }
+ }
+ }
+
+ protected static void readInteger(String line, int index, IntConsumer consumer) {
+ readInteger(line, index, index, consumer);
+ }
+
+ protected static void readDouble(String line, int start, int end, DoubleConsumer consumer) {
+ if (start < line.length() + 1) {
+ String value = line.substring(start - 1, Math.min(end, line.length())).trim();
+ if (!value.isEmpty()) {
+ consumer.accept(Double.parseDouble(value));
+ }
+ }
+ }
+
+ protected static void readString(String line, int start, int end, Consumer consumer) {
+ if (start < line.length() + 1) {
+ String value = line.substring(start - 1, Math.min(end, line.length())).trim();
+ if (!value.isEmpty()) {
+ consumer.accept(value.trim());
+ }
+ }
+ }
+
+ protected static void readString(String line, int index, Consumer consumer) {
+ readString(line, index, index, consumer);
+ }
+}
diff --git a/ieee-cdf/ieee-cdf-model/src/main/java/com/powsybl/ieeecdf/model/reader/IeeeCdfBranchReader.java b/ieee-cdf/ieee-cdf-model/src/main/java/com/powsybl/ieeecdf/model/reader/IeeeCdfBranchReader.java
new file mode 100644
index 00000000000..2a7a20309fe
--- /dev/null
+++ b/ieee-cdf/ieee-cdf-model/src/main/java/com/powsybl/ieeecdf/model/reader/IeeeCdfBranchReader.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2025, RTE (http://www.rte-france.com)
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ * SPDX-License-Identifier: MPL-2.0
+ */
+package com.powsybl.ieeecdf.model.reader;
+
+import com.powsybl.ieeecdf.model.elements.IeeeCdfBranch;
+import com.powsybl.ieeecdf.model.conversion.BranchSideConversion;
+import com.powsybl.ieeecdf.model.conversion.BranchTypeConversion;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.util.List;
+
+/**
+ * @author Nicolas Rol {@literal }
+ */
+public final class IeeeCdfBranchReader extends AbstractIeeeCdfReader {
+
+ private IeeeCdfBranchReader() {
+ // private constructor to prevent instantiation
+ super();
+ }
+
+ public static List parseBranches(BufferedReader reader, int expectedItemsNumber) throws IOException {
+ return readLines(reader, -999, IeeeCdfBranchReader::parseBranch, expectedItemsNumber);
+ }
+
+ private static IeeeCdfBranch parseBranch(String line) {
+ IeeeCdfBranch branch = new IeeeCdfBranch();
+ readInteger(line, 1, 4, branch::setTapBusNumber);
+ readInteger(line, 6, 9, branch::setzBusNumber);
+ readInteger(line, 11, 12, branch::setArea);
+ readInteger(line, 14, 15, branch::setLossZone);
+ readInteger(line, 17, branch::setCircuit);
+ readString(line, 19, type -> branch.setType(BranchTypeConversion.fromString(type)));
+ readDouble(line, 20, 29, branch::setResistance);
+ readDouble(line, 30, 39, branch::setReactance);
+ readDouble(line, 41, 49, branch::setChargingSusceptance);
+ readInteger(line, 51, 55, branch::setRating1);
+ readInteger(line, 57, 61, branch::setRating2);
+ readInteger(line, 63, 67, branch::setRating3);
+ readInteger(line, 69, 72, branch::setControlBusNumber);
+ readString(line, 74, side -> branch.setSide(BranchSideConversion.fromString(side)));
+ readDouble(line, 77, 82, branch::setFinalTurnsRatio);
+ readDouble(line, 84, 90, branch::setFinalAngle);
+ readDouble(line, 91, 97, branch::setMinTapOrPhaseShift);
+ readDouble(line, 98, 104, branch::setMaxTapOrPhaseShift);
+ readDouble(line, 105, 111, branch::setStepSize);
+ try {
+ readDouble(line, 113, 119, branch::setMinVoltageActiveOrReactivePowerLimit);
+ } catch (NumberFormatException e) {
+ // In some cases, this column is one character shorter than usual
+ readDouble(line, 113, 118, branch::setMinVoltageActiveOrReactivePowerLimit);
+ }
+ readDouble(line, 120, 126, branch::setMaxVoltageActiveOrReactivePowerLimit);
+ readInteger(line, 127, 133, branch::setUnused);
+ return branch;
+ }
+}
diff --git a/ieee-cdf/ieee-cdf-model/src/main/java/com/powsybl/ieeecdf/model/reader/IeeeCdfBusReader.java b/ieee-cdf/ieee-cdf-model/src/main/java/com/powsybl/ieeecdf/model/reader/IeeeCdfBusReader.java
new file mode 100644
index 00000000000..3e2fbd1a9e0
--- /dev/null
+++ b/ieee-cdf/ieee-cdf-model/src/main/java/com/powsybl/ieeecdf/model/reader/IeeeCdfBusReader.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2025, RTE (http://www.rte-france.com)
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ * SPDX-License-Identifier: MPL-2.0
+ */
+package com.powsybl.ieeecdf.model.reader;
+
+import com.powsybl.ieeecdf.model.elements.IeeeCdfBus;
+import com.powsybl.ieeecdf.model.conversion.BusTypeConversion;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.util.List;
+
+/**
+ * @author Nicolas Rol {@literal }
+ */
+public final class IeeeCdfBusReader extends AbstractIeeeCdfReader {
+
+ private IeeeCdfBusReader() {
+ // private constructor to prevent instantiation
+ super();
+ }
+
+ public static List parseBuses(BufferedReader reader, int expectedItemsNumber) throws IOException {
+ return readLines(reader, -999, IeeeCdfBusReader::parseBus, expectedItemsNumber);
+ }
+
+ private static IeeeCdfBus parseBus(String line) {
+ IeeeCdfBus bus = new IeeeCdfBus();
+ readInteger(line, 1, 4, bus::setNumber);
+ readString(line, 6, 17, bus::setName);
+ readInteger(line, 19, 20, bus::setAreaNumber);
+ readInteger(line, 21, 23, bus::setLossZoneNumber);
+ readString(line, 25, 26, type -> bus.setType(BusTypeConversion.fromString(type)));
+ readDouble(line, 28, 33, bus::setFinalVoltage);
+ readDouble(line, 34, 40, bus::setFinalAngle);
+ readDouble(line, 41, 49, bus::setActiveLoad);
+ readDouble(line, 50, 58, bus::setReactiveLoad);
+ readDouble(line, 59, 67, bus::setActiveGeneration);
+ readDouble(line, 68, 75, bus::setReactiveGeneration);
+ readDouble(line, 77, 83, bus::setBaseVoltage);
+ readDouble(line, 85, 90, bus::setDesiredVoltage);
+ readDouble(line, 91, 98, bus::setMaxReactivePowerOrVoltageLimit);
+ readDouble(line, 99, 106, bus::setMinReactivePowerOrVoltageLimit);
+ readDouble(line, 107, 114, bus::setShuntConductance);
+ readDouble(line, 115, 122, bus::setShuntSusceptance);
+ readInteger(line, 124, 127, bus::setRemoteControlledBusNumber);
+ readInteger(line, 128, 132, bus::setUnused);
+ return bus;
+ }
+}
diff --git a/ieee-cdf/ieee-cdf-model/src/main/java/com/powsybl/ieeecdf/model/reader/IeeeCdfInterchangeDataReader.java b/ieee-cdf/ieee-cdf-model/src/main/java/com/powsybl/ieeecdf/model/reader/IeeeCdfInterchangeDataReader.java
new file mode 100644
index 00000000000..1f961f40d17
--- /dev/null
+++ b/ieee-cdf/ieee-cdf-model/src/main/java/com/powsybl/ieeecdf/model/reader/IeeeCdfInterchangeDataReader.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2025, RTE (http://www.rte-france.com)
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ * SPDX-License-Identifier: MPL-2.0
+ */
+package com.powsybl.ieeecdf.model.reader;
+
+import com.powsybl.ieeecdf.model.elements.IeeeCdfInterchangeData;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.util.List;
+
+/**
+ * @author Nicolas Rol {@literal }
+ */
+public final class IeeeCdfInterchangeDataReader extends AbstractIeeeCdfReader {
+
+ private IeeeCdfInterchangeDataReader() {
+ // private constructor to prevent instantiation
+ super();
+ }
+
+ public static List parseInterchangeData(BufferedReader reader, int expectedItemsNumber) throws IOException {
+ return readLines(reader, -9, IeeeCdfInterchangeDataReader::parseInterchangeData, expectedItemsNumber);
+ }
+
+ private static IeeeCdfInterchangeData parseInterchangeData(String line) {
+ IeeeCdfInterchangeData interchangeData = new IeeeCdfInterchangeData();
+ readInteger(line, 1, 2, interchangeData::setAreaNumber);
+ readInteger(line, 4, 7, interchangeData::setInterchangeSlackBusNumber);
+ readString(line, 9, 20, interchangeData::setAlternateSwingBusName);
+ readDouble(line, 21, 28, interchangeData::setAreaInterchangeExport);
+ readDouble(line, 29, 35, interchangeData::setAreaInterchangeTolerance);
+ readString(line, 38, 43, interchangeData::setAreaCode);
+ readString(line, 46, 75, interchangeData::setAreaName);
+ return interchangeData;
+ }
+}
diff --git a/ieee-cdf/ieee-cdf-model/src/main/java/com/powsybl/ieeecdf/model/reader/IeeeCdfLossZoneReader.java b/ieee-cdf/ieee-cdf-model/src/main/java/com/powsybl/ieeecdf/model/reader/IeeeCdfLossZoneReader.java
new file mode 100644
index 00000000000..1e3a27a2223
--- /dev/null
+++ b/ieee-cdf/ieee-cdf-model/src/main/java/com/powsybl/ieeecdf/model/reader/IeeeCdfLossZoneReader.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2025, RTE (http://www.rte-france.com)
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ * SPDX-License-Identifier: MPL-2.0
+ */
+package com.powsybl.ieeecdf.model.reader;
+
+import com.powsybl.ieeecdf.model.elements.IeeeCdfLossZone;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.util.List;
+
+/**
+ * @author Nicolas Rol {@literal }
+ */
+public final class IeeeCdfLossZoneReader extends AbstractIeeeCdfReader {
+
+ private IeeeCdfLossZoneReader() {
+ // private constructor to prevent instantiation
+ super();
+ }
+
+ public static List parseLossZones(BufferedReader reader, int expectedItemsNumber) throws IOException {
+ return readLines(reader, -99, IeeeCdfLossZoneReader::parseLossZone, expectedItemsNumber);
+ }
+
+ private static IeeeCdfLossZone parseLossZone(String line) {
+ IeeeCdfLossZone lossZone = new IeeeCdfLossZone();
+ readInteger(line, 1, 3, lossZone::setNumber);
+ readString(line, 5, 16, lossZone::setName);
+ return lossZone;
+ }
+
+}
diff --git a/ieee-cdf/ieee-cdf-model/src/main/java/com/powsybl/ieeecdf/model/reader/IeeeCdfTieLineReader.java b/ieee-cdf/ieee-cdf-model/src/main/java/com/powsybl/ieeecdf/model/reader/IeeeCdfTieLineReader.java
new file mode 100644
index 00000000000..89c0dec65b2
--- /dev/null
+++ b/ieee-cdf/ieee-cdf-model/src/main/java/com/powsybl/ieeecdf/model/reader/IeeeCdfTieLineReader.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2025, RTE (http://www.rte-france.com)
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ * SPDX-License-Identifier: MPL-2.0
+ */
+package com.powsybl.ieeecdf.model.reader;
+
+import com.powsybl.ieeecdf.model.elements.IeeeCdfTieLine;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.util.List;
+
+/**
+ * @author Nicolas Rol {@literal }
+ */
+public final class IeeeCdfTieLineReader extends AbstractIeeeCdfReader {
+
+ private IeeeCdfTieLineReader() {
+ // private constructor to prevent instantiation
+ super();
+ }
+
+ public static List parseTieLine(BufferedReader reader, int expectedItemsNumber) throws IOException {
+ return readLines(reader, -999, IeeeCdfTieLineReader::parseTieLine, expectedItemsNumber);
+ }
+
+ private static IeeeCdfTieLine parseTieLine(String line) {
+ IeeeCdfTieLine tieLine = new IeeeCdfTieLine();
+ readInteger(line, 1, 4, tieLine::setMeteredBusNumber);
+ readInteger(line, 7, 8, tieLine::setMeteredAreaNumber);
+ readInteger(line, 11, 14, tieLine::setNonMeteredBusNumber);
+ readInteger(line, 17, 18, tieLine::setNonMeteredAreaNumber);
+ readInteger(line, 21, tieLine::setCircuitNumber);
+ return tieLine;
+ }
+}
diff --git a/ieee-cdf/ieee-cdf-model/src/main/java/com/powsybl/ieeecdf/model/reader/IeeeCdfTitleReader.java b/ieee-cdf/ieee-cdf-model/src/main/java/com/powsybl/ieeecdf/model/reader/IeeeCdfTitleReader.java
new file mode 100644
index 00000000000..52bb85e57a4
--- /dev/null
+++ b/ieee-cdf/ieee-cdf-model/src/main/java/com/powsybl/ieeecdf/model/reader/IeeeCdfTitleReader.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2025, RTE (http://www.rte-france.com)
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ * SPDX-License-Identifier: MPL-2.0
+ */
+package com.powsybl.ieeecdf.model.reader;
+
+import com.powsybl.ieeecdf.model.conversion.LocalDateConversion;
+import com.powsybl.ieeecdf.model.conversion.SeasonConversion;
+import com.powsybl.ieeecdf.model.elements.IeeeCdfTitle;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+
+/**
+ * @author Nicolas Rol {@literal }
+ */
+public final class IeeeCdfTitleReader extends AbstractIeeeCdfReader {
+
+ private IeeeCdfTitleReader() {
+ // private constructor to prevent instantiation
+ super();
+ }
+
+ public static IeeeCdfTitle parseTitle(BufferedReader reader) throws IOException {
+ String titleLine = reader.readLine();
+ return titleLine == null ? null : parseTitle(titleLine);
+ }
+
+ private static IeeeCdfTitle parseTitle(String line) {
+ IeeeCdfTitle title = new IeeeCdfTitle();
+ readString(line, 2, 9, date -> title.setDate(LocalDateConversion.fromString(date)));
+ readString(line, 11, 30, title::setOriginatorName);
+ readDouble(line, 32, 37, title::setMvaBase);
+ readInteger(line, 39, 42, title::setYear);
+ readString(line, 44, season -> title.setSeason(SeasonConversion.fromString(season)));
+ readString(line, 46, 73, title::setCaseIdentification);
+ return title;
+ }
+}
diff --git a/ieee-cdf/ieee-cdf-model/src/main/java/com/powsybl/ieeecdf/model/writer/AbstractIeeeCdfWriter.java b/ieee-cdf/ieee-cdf-model/src/main/java/com/powsybl/ieeecdf/model/writer/AbstractIeeeCdfWriter.java
new file mode 100644
index 00000000000..d45df6cf721
--- /dev/null
+++ b/ieee-cdf/ieee-cdf-model/src/main/java/com/powsybl/ieeecdf/model/writer/AbstractIeeeCdfWriter.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2025, RTE (http://www.rte-france.com)
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ * SPDX-License-Identifier: MPL-2.0
+ */
+package com.powsybl.ieeecdf.model.writer;
+
+import java.io.BufferedWriter;
+import java.io.IOException;
+import java.text.DecimalFormat;
+import java.util.List;
+
+/**
+ * @author Nicolas Rol {@literal }
+ */
+public abstract class AbstractIeeeCdfWriter {
+
+ protected static final String FILLER = " ";
+ protected static final String FILLER2 = FILLER + FILLER;
+ private static final DecimalFormat DF = new DecimalFormat();
+
+ protected AbstractIeeeCdfWriter() {
+ // private constructor to prevent instantiation
+ DF.setGroupingUsed(false);
+ }
+
+ protected static void writeFooter(BufferedWriter writer, int footerValue) throws IOException {
+ writer.write(String.valueOf(footerValue));
+ writer.newLine();
+ }
+
+ protected static void writeHeader(BufferedWriter writer, String header, List> elements) throws IOException {
+ writer.write(String.format(header, elements.size()));
+ writer.newLine();
+ }
+
+ protected static String toString(int value, int index) {
+ return toString(value, index, index, true);
+ }
+
+ protected static String toString(int value, int start, int end, boolean alignedToTheLeft) {
+ return pad(Integer.toString(value), end - start + 1, alignedToTheLeft);
+ }
+
+ protected static String toString(double value, int start, int end, boolean alignedToTheLeft) {
+ return pad(Double.toString(value), end - start + 1, alignedToTheLeft);
+ }
+
+ protected static String toString(String value, int index) {
+ return toString(value, index, index, true);
+ }
+
+ protected static String toString(String value, int start, int end, boolean alignedToTheLeft) {
+ return pad(value, end - start + 1, alignedToTheLeft);
+ }
+
+ private static String pad(String s, int width, boolean leftAlign) {
+ int len = s.length();
+ if (len == width) {
+ return s; // exact fit
+ }
+ if (len > width) {
+ return s.substring(0, width); // truncate
+ }
+
+ int pad = width - len;
+ StringBuilder sb = new StringBuilder(width);
+
+ if (leftAlign) {
+ sb.append(s);
+ sb.append(" ".repeat(pad));
+ } else {
+ sb.append(" ".repeat(pad));
+ sb.append(s);
+ }
+ return sb.toString();
+ }
+}
diff --git a/ieee-cdf/ieee-cdf-model/src/main/java/com/powsybl/ieeecdf/model/writer/IeeeCdfBranchWriter.java b/ieee-cdf/ieee-cdf-model/src/main/java/com/powsybl/ieeecdf/model/writer/IeeeCdfBranchWriter.java
new file mode 100644
index 00000000000..a86668ac58b
--- /dev/null
+++ b/ieee-cdf/ieee-cdf-model/src/main/java/com/powsybl/ieeecdf/model/writer/IeeeCdfBranchWriter.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2025, RTE (http://www.rte-france.com)
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ * SPDX-License-Identifier: MPL-2.0
+ */
+package com.powsybl.ieeecdf.model.writer;
+
+import com.powsybl.ieeecdf.model.conversion.BranchSideConversion;
+import com.powsybl.ieeecdf.model.conversion.BranchTypeConversion;
+import com.powsybl.ieeecdf.model.elements.IeeeCdfBranch;
+
+import java.io.BufferedWriter;
+import java.io.IOException;
+import java.util.List;
+
+/**
+ * @author Nicolas Rol {@literal }
+ */
+public final class IeeeCdfBranchWriter extends AbstractIeeeCdfWriter {
+
+ private IeeeCdfBranchWriter() {
+ // private constructor to prevent instantiation
+ }
+
+ public static void writeBranches(BufferedWriter writer, List branchList) throws IOException {
+ writeHeader(writer, "BRANCH DATA FOLLOWS %d ITEMS", branchList);
+ for (IeeeCdfBranch bean : branchList) {
+ writer.write(convertBranchToLine(bean));
+ writer.newLine();
+ }
+ writeFooter(writer, -999);
+ }
+
+ private static String convertBranchToLine(IeeeCdfBranch branch) {
+ return toString(branch.getTapBusNumber(), 1, 4, false) +
+ FILLER +
+ toString(branch.getzBusNumber(), 6, 9, true) +
+ FILLER +
+ toString(branch.getArea(), 11, 12, true) +
+ FILLER +
+ toString(branch.getLossZone(), 14, 15, true) +
+ FILLER +
+ toString(branch.getCircuit(), 17) +
+ FILLER +
+ toString(BranchTypeConversion.revert(branch.getType()), 19) +
+ toString(branch.getResistance(), 20, 29, true) +
+ toString(branch.getReactance(), 30, 39, true) +
+ FILLER +
+ toString(branch.getChargingSusceptance(), 41, 49, true) +
+ FILLER +
+ toString(branch.getRating1(), 51, 55, true) +
+ FILLER +
+ toString(branch.getRating2(), 57, 61, true) +
+ FILLER +
+ toString(branch.getRating3(), 63, 67, true) +
+ FILLER +
+ toString(branch.getControlBusNumber(), 69, 72, true) +
+ FILLER +
+ toString(BranchSideConversion.revert(branch.getSide()), 74) +
+ FILLER2 +
+ toString(branch.getFinalTurnsRatio(), 77, 82, true) +
+ FILLER +
+ toString(branch.getFinalAngle(), 84, 90, true) +
+ toString(branch.getMinTapOrPhaseShift(), 91, 97, true) +
+ toString(branch.getMaxTapOrPhaseShift(), 98, 104, true) +
+ toString(branch.getStepSize(), 105, 111, true) +
+ FILLER +
+ toString(branch.getMinVoltageActiveOrReactivePowerLimit(), 113, 119, true) +
+ toString(branch.getMaxVoltageActiveOrReactivePowerLimit(), 120, 126, true) +
+ toString(branch.getUnused(), 127, 133, true);
+ }
+}
diff --git a/ieee-cdf/ieee-cdf-model/src/main/java/com/powsybl/ieeecdf/model/writer/IeeeCdfBusWriter.java b/ieee-cdf/ieee-cdf-model/src/main/java/com/powsybl/ieeecdf/model/writer/IeeeCdfBusWriter.java
new file mode 100644
index 00000000000..f1e25ddaa73
--- /dev/null
+++ b/ieee-cdf/ieee-cdf-model/src/main/java/com/powsybl/ieeecdf/model/writer/IeeeCdfBusWriter.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2025, RTE (http://www.rte-france.com)
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ * SPDX-License-Identifier: MPL-2.0
+ */
+package com.powsybl.ieeecdf.model.writer;
+
+import com.powsybl.ieeecdf.model.conversion.BusTypeConversion;
+import com.powsybl.ieeecdf.model.elements.IeeeCdfBus;
+
+import java.io.BufferedWriter;
+import java.io.IOException;
+import java.util.List;
+
+/**
+ * @author Nicolas Rol {@literal }
+ */
+public final class IeeeCdfBusWriter extends AbstractIeeeCdfWriter {
+
+ private IeeeCdfBusWriter() {
+ // private constructor to prevent instantiation
+ }
+
+ public static void writeBuses(BufferedWriter writer, List busList) throws IOException {
+ writeHeader(writer, "BUS DATA FOLLOWS %d ITEMS", busList);
+ for (IeeeCdfBus bean : busList) {
+ writer.write(convertBusToLins(bean));
+ writer.newLine();
+ }
+ writeFooter(writer, -999);
+ }
+
+ private static String convertBusToLins(IeeeCdfBus bus) {
+ return toString(bus.getNumber(), 1, 4, false) +
+ FILLER +
+ toString(bus.getName(), 6, 17, true) +
+ FILLER +
+ toString(bus.getAreaNumber(), 19, 20, true) +
+ toString(bus.getLossZoneNumber(), 21, 23, true) +
+ FILLER +
+ toString(BusTypeConversion.revert(bus.getType()), 25, 26, true) +
+ FILLER +
+ toString(bus.getFinalVoltage(), 28, 33, true) +
+ toString(bus.getFinalAngle(), 34, 40, true) +
+ toString(bus.getActiveLoad(), 41, 49, true) +
+ toString(bus.getReactiveLoad(), 50, 58, true) +
+ toString(bus.getActiveGeneration(), 59, 67, true) +
+ toString(bus.getReactiveGeneration(), 68, 75, true) +
+ FILLER +
+ toString(bus.getBaseVoltage(), 77, 83, true) +
+ FILLER +
+ toString(bus.getDesiredVoltage(), 85, 90, true) +
+ toString(bus.getMaxReactivePowerOrVoltageLimit(), 91, 98, true) +
+ toString(bus.getMinReactivePowerOrVoltageLimit(), 99, 106, true) +
+ toString(bus.getShuntConductance(), 107, 114, true) +
+ toString(bus.getShuntSusceptance(), 115, 122, true) +
+ FILLER +
+ toString(bus.getRemoteControlledBusNumber(), 124, 127, true) +
+ (bus.getUnused() != null ? toString(bus.getUnused(), 128, 132, true) : "");
+ }
+}
diff --git a/ieee-cdf/ieee-cdf-model/src/main/java/com/powsybl/ieeecdf/model/writer/IeeeCdfInterchangeDataWriter.java b/ieee-cdf/ieee-cdf-model/src/main/java/com/powsybl/ieeecdf/model/writer/IeeeCdfInterchangeDataWriter.java
new file mode 100644
index 00000000000..511b08b190a
--- /dev/null
+++ b/ieee-cdf/ieee-cdf-model/src/main/java/com/powsybl/ieeecdf/model/writer/IeeeCdfInterchangeDataWriter.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2025, RTE (http://www.rte-france.com)
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ * SPDX-License-Identifier: MPL-2.0
+ */
+package com.powsybl.ieeecdf.model.writer;
+
+import com.powsybl.ieeecdf.model.elements.IeeeCdfInterchangeData;
+
+import java.io.BufferedWriter;
+import java.io.IOException;
+import java.util.List;
+
+/**
+ * @author Nicolas Rol {@literal }
+ */
+public final class IeeeCdfInterchangeDataWriter extends AbstractIeeeCdfWriter {
+
+ private IeeeCdfInterchangeDataWriter() {
+ // private constructor to prevent instantiation
+ }
+
+ public static void writeInterchangeData(BufferedWriter writer, List interchangeDataList) throws IOException {
+ writeHeader(writer, "INTERCHANGE DATA FOLLOWS %d ITEMS", interchangeDataList);
+ for (IeeeCdfInterchangeData bean : interchangeDataList) {
+ writer.write(convertInterchangeDataToLine(bean));
+ writer.newLine();
+ }
+ writeFooter(writer, -9);
+ }
+
+ private static String convertInterchangeDataToLine(IeeeCdfInterchangeData interchangeData) {
+ return toString(interchangeData.getAreaNumber(), 1, 2, false) +
+ FILLER +
+ toString(interchangeData.getInterchangeSlackBusNumber(), 4, 7, true) +
+ FILLER +
+ toString(interchangeData.getAlternateSwingBusName(), 9, 20, true) +
+ toString(interchangeData.getAreaInterchangeExport(), 21, 28, true) +
+ toString(interchangeData.getAreaInterchangeTolerance(), 29, 35, true) +
+ FILLER2 +
+ toString(interchangeData.getAreaCode(), 38, 43, true) +
+ FILLER2 +
+ toString(interchangeData.getAreaName(), 46, 75, true);
+ }
+}
diff --git a/ieee-cdf/ieee-cdf-model/src/main/java/com/powsybl/ieeecdf/model/writer/IeeeCdfLossZoneWriter.java b/ieee-cdf/ieee-cdf-model/src/main/java/com/powsybl/ieeecdf/model/writer/IeeeCdfLossZoneWriter.java
new file mode 100644
index 00000000000..955342bdde9
--- /dev/null
+++ b/ieee-cdf/ieee-cdf-model/src/main/java/com/powsybl/ieeecdf/model/writer/IeeeCdfLossZoneWriter.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2025, RTE (http://www.rte-france.com)
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ * SPDX-License-Identifier: MPL-2.0
+ */
+package com.powsybl.ieeecdf.model.writer;
+
+import com.powsybl.ieeecdf.model.elements.IeeeCdfLossZone;
+
+import java.io.BufferedWriter;
+import java.io.IOException;
+import java.util.List;
+
+/**
+ * @author Nicolas Rol {@literal }
+ */
+public final class IeeeCdfLossZoneWriter extends AbstractIeeeCdfWriter {
+
+ private IeeeCdfLossZoneWriter() {
+ // private constructor to prevent instantiation
+ }
+
+ public static void writeLossZone(BufferedWriter writer, List lossZoneList) throws IOException {
+ writeHeader(writer, "LOSS ZONES FOLLOWS %d ITEMS", lossZoneList);
+ for (IeeeCdfLossZone bean : lossZoneList) {
+ writer.write(convertLossZoneToLine(bean));
+ writer.newLine();
+ }
+ writeFooter(writer, -99);
+ }
+
+ private static String convertLossZoneToLine(IeeeCdfLossZone lossZone) {
+ return toString(lossZone.getNumber(), 1, 3, false) +
+ FILLER +
+ toString(lossZone.getName(), 5, 16, true);
+ }
+}
diff --git a/ieee-cdf/ieee-cdf-model/src/main/java/com/powsybl/ieeecdf/model/writer/IeeeCdfTieLineWriter.java b/ieee-cdf/ieee-cdf-model/src/main/java/com/powsybl/ieeecdf/model/writer/IeeeCdfTieLineWriter.java
new file mode 100644
index 00000000000..5a924eefdd5
--- /dev/null
+++ b/ieee-cdf/ieee-cdf-model/src/main/java/com/powsybl/ieeecdf/model/writer/IeeeCdfTieLineWriter.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2025, RTE (http://www.rte-france.com)
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ * SPDX-License-Identifier: MPL-2.0
+ */
+package com.powsybl.ieeecdf.model.writer;
+
+import com.powsybl.ieeecdf.model.elements.IeeeCdfTieLine;
+
+import java.io.BufferedWriter;
+import java.io.IOException;
+import java.util.List;
+
+/**
+ * @author Nicolas Rol {@literal }
+ */
+public final class IeeeCdfTieLineWriter extends AbstractIeeeCdfWriter {
+
+ private IeeeCdfTieLineWriter() {
+ // private constructor to prevent instantiation
+ }
+
+ public static void writeTieLines(BufferedWriter writer, List tieLineList) throws IOException {
+ writeHeader(writer, "TIE LINES FOLLOWS %d ITEMS", tieLineList);
+ for (IeeeCdfTieLine bean : tieLineList) {
+ writer.write(convertTieLineToLine(bean));
+ writer.newLine();
+ }
+ writeFooter(writer, -999);
+ }
+
+ private static String convertTieLineToLine(IeeeCdfTieLine tieLine) {
+ return toString(tieLine.getMeteredBusNumber(), 1, 4, false) +
+ FILLER2 +
+ toString(tieLine.getMeteredAreaNumber(), 7, 8, true) +
+ FILLER2 +
+ toString(tieLine.getNonMeteredBusNumber(), 11, 14, true) +
+ FILLER2 +
+ toString(tieLine.getNonMeteredAreaNumber(), 17, 18, true) +
+ FILLER2 +
+ toString(tieLine.getCircuitNumber(), 21);
+
+ }
+}
diff --git a/ieee-cdf/ieee-cdf-model/src/main/java/com/powsybl/ieeecdf/model/writer/IeeeCdfTitleWriter.java b/ieee-cdf/ieee-cdf-model/src/main/java/com/powsybl/ieeecdf/model/writer/IeeeCdfTitleWriter.java
new file mode 100644
index 00000000000..6219bb89ac1
--- /dev/null
+++ b/ieee-cdf/ieee-cdf-model/src/main/java/com/powsybl/ieeecdf/model/writer/IeeeCdfTitleWriter.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2025, RTE (http://www.rte-france.com)
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ * SPDX-License-Identifier: MPL-2.0
+ */
+package com.powsybl.ieeecdf.model.writer;
+
+import com.powsybl.ieeecdf.model.conversion.LocalDateConversion;
+import com.powsybl.ieeecdf.model.conversion.SeasonConversion;
+import com.powsybl.ieeecdf.model.elements.IeeeCdfTitle;
+
+import java.io.BufferedWriter;
+import java.io.IOException;
+
+/**
+ * @author Nicolas Rol {@literal }
+ */
+public final class IeeeCdfTitleWriter extends AbstractIeeeCdfWriter {
+
+ private IeeeCdfTitleWriter() {
+ // private constructor to prevent instantiation
+ }
+
+ public static void writeTitle(BufferedWriter writer, IeeeCdfTitle title) throws IOException {
+ writer.write(convertToLine(title));
+ writer.newLine();
+ }
+
+ private static String convertToLine(IeeeCdfTitle title) {
+ return FILLER +
+ toString(LocalDateConversion.revert(title.getDate()), 2, 9, true) +
+ FILLER +
+ toString(title.getOriginatorName(), 11, 30, true) +
+ FILLER +
+ toString(title.getMvaBase(), 32, 37, true) +
+ FILLER +
+ toString(title.getYear(), 39, 42, true) +
+ FILLER +
+ toString(SeasonConversion.revert(title.getSeason()), 44) +
+ FILLER +
+ toString(title.getCaseIdentification(), 46, 73, true);
+ }
+}
diff --git a/ieee-cdf/ieee-cdf-model/src/test/java/com/powsybl/ieeecdf/model/IeeeCdfReaderWriterTest.java b/ieee-cdf/ieee-cdf-model/src/test/java/com/powsybl/ieeecdf/model/IeeeCdfReaderWriterTest.java
index 9f3fda8eb7a..29f9208f9a0 100644
--- a/ieee-cdf/ieee-cdf-model/src/test/java/com/powsybl/ieeecdf/model/IeeeCdfReaderWriterTest.java
+++ b/ieee-cdf/ieee-cdf-model/src/test/java/com/powsybl/ieeecdf/model/IeeeCdfReaderWriterTest.java
@@ -11,6 +11,7 @@
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.google.common.jimfs.Configuration;
import com.google.common.jimfs.Jimfs;
+import com.powsybl.ieeecdf.model.elements.IeeeCdfTieLine;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@@ -24,6 +25,7 @@
import java.nio.file.FileSystem;
import java.nio.file.Files;
import java.nio.file.Path;
+import java.util.Objects;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
@@ -50,7 +52,7 @@ void tearDown() throws Exception {
private void testIeeeFile(String fileName) throws IOException {
IeeeCdfModel model;
// read
- try (BufferedReader reader = new BufferedReader(new InputStreamReader(getClass().getResourceAsStream("/" + fileName)))) {
+ try (BufferedReader reader = new BufferedReader(new InputStreamReader(Objects.requireNonNull(getClass().getResourceAsStream("/" + fileName))))) {
model = new IeeeCdfReader().read(reader);
}
@@ -108,10 +110,10 @@ void testIeee9zeroimpedance() throws IOException {
@Test
void testTieLine() throws IOException {
- try (BufferedReader reader = new BufferedReader(new InputStreamReader(getClass().getResourceAsStream("/tieline.txt")))) {
+ try (BufferedReader reader = new BufferedReader(new InputStreamReader(Objects.requireNonNull(getClass().getResourceAsStream("/tieline.txt"))))) {
IeeeCdfModel model = new IeeeCdfReader().read(reader);
assertEquals(1, model.getTieLines().size());
- IeeeCdfTieLine tieLine = model.getTieLines().get(0);
+ IeeeCdfTieLine tieLine = model.getTieLines().getFirst();
assertEquals(1, tieLine.getMeteredBusNumber());
assertEquals(2, tieLine.getMeteredAreaNumber());
assertEquals(3, tieLine.getNonMeteredBusNumber());
diff --git a/ieee-cdf/ieee-cdf-model/src/test/resources/tieline.txt b/ieee-cdf/ieee-cdf-model/src/test/resources/tieline.txt
index b9c781ea9e1..faf5a22404e 100644
--- a/ieee-cdf/ieee-cdf-model/src/test/resources/tieline.txt
+++ b/ieee-cdf/ieee-cdf-model/src/test/resources/tieline.txt
@@ -1,5 +1,5 @@
11/14/19 TIELINE TEST 100.0 2019 W POWSYBL TEST CASE
-TIE LINES FOLLOWS 0 ITEMS
+TIE LINES FOLLOWS 1 ITEMS
1 2 3 4 5
-999
END OF DATA