From bffd5f8a10071a3e4567fe62a86ec14be7c577a0 Mon Sep 17 00:00:00 2001 From: EtienneLt <32468651+EtienneLt@users.noreply.github.com> Date: Thu, 2 May 2024 16:49:52 +0200 Subject: [PATCH] move per unitage to java (#718) Signed-off-by: Etienne LESOT --- cpp/src/bindings.cpp | 4 +- cpp/src/pypowsybl.cpp | 8 +- cpp/src/pypowsybl.h | 4 +- .../dataframe/AbstractDataframeMapper.java | 28 +- .../dataframe/BaseDataframeMapperBuilder.java | 20 +- .../dataframe/BooleanSeriesMapper.java | 4 +- .../powsybl/dataframe/DataframeMapper.java | 5 +- .../powsybl/dataframe/DoubleSeriesMapper.java | 20 +- .../powsybl/dataframe/EnumSeriesMapper.java | 4 +- .../powsybl/dataframe/IntSeriesMapper.java | 4 +- .../com/powsybl/dataframe/MappingUtils.java | 18 + .../com/powsybl/dataframe/SeriesMapper.java | 6 +- .../powsybl/dataframe/StringSeriesMapper.java | 4 +- .../AbstractNetworkDataframeMapper.java | 4 +- .../dataframe/network/DataframeContext.java | 33 ++ .../dataframe/network/NetworkDataframes.java | 475 +++++++++-------- .../dataframe/network/PerUnitUtil.java | 268 ++++++++++ .../ActivePowerControlDataframeProvider.java | 2 +- .../BranchObservabilityDataframeProvider.java | 16 +- ...natedReactiveControlDataframeProvider.java | 2 +- ...eneratorShortCircuitDataframeProvider.java | 9 +- ...opActivePowerControlDataframeProvider.java | 24 +- ...atorActivePowerRangeDataframeProvider.java | 4 +- ...tifiableShortCircuitDataframeProvider.java | 4 +- ...jectionObservabilityDataframeProvider.java | 12 +- .../LoadDetailDataframeProvider.java | 8 +- .../network/extensions/NetworkExtensions.java | 5 +- ...ondaryVoltageControlDataframeProvider.java | 2 +- .../StandByAutomatonDataframeProvider.java | 36 +- .../powsybl/python/network/Dataframes.java | 15 +- .../python/network/NetworkCFunctions.java | 19 +- .../dataframe/DataframeMapperBuilderTest.java | 141 ++--- .../network/NetworkDataframesTest.java | 6 +- .../validation/LoadFlowValidationTest.java | 5 +- .../python/security/SecurityAnalysisTest.java | 3 +- .../ShortCircuitAnalysisTest.java | 9 +- pypowsybl/_pypowsybl.pyi | 4 +- pypowsybl/network/impl/network.py | 39 +- tests/test_java_perunit.py | 493 ++++++++++++++++++ tests/test_network.py | 2 +- tests/test_per_unit.py | 37 ++ 41 files changed, 1384 insertions(+), 422 deletions(-) create mode 100644 java/src/main/java/com/powsybl/dataframe/network/DataframeContext.java create mode 100644 java/src/main/java/com/powsybl/dataframe/network/PerUnitUtil.java create mode 100644 tests/test_java_perunit.py diff --git a/cpp/src/bindings.cpp b/cpp/src/bindings.cpp index e45bd15ca1..b7e15d30c9 100644 --- a/cpp/src/bindings.cpp +++ b/cpp/src/bindings.cpp @@ -741,7 +741,7 @@ PYBIND11_MODULE(_pypowsybl, m) { py::arg("element_type")); m.def("create_network_elements_series_array", &pypowsybl::createNetworkElementsSeriesArray, "Create a network elements series array for a given element type", - py::call_guard(), py::arg("network"), py::arg("element_type"), py::arg("filter_attributes_type"), py::arg("attributes"), py::arg("array")); + py::call_guard(), py::arg("network"), py::arg("element_type"), py::arg("filter_attributes_type"), py::arg("attributes"), py::arg("array"), py::arg("per_unit"), py::arg("nominal_apparent_power")); m.def("create_network_elements_extension_series_array", &pypowsybl::createNetworkElementsExtensionSeriesArray, "Create a network elements extensions series array for a given extension name", py::call_guard(), py::arg("network"), py::arg("extension_name"), py::arg("table_name")); @@ -751,7 +751,7 @@ PYBIND11_MODULE(_pypowsybl, m) { m.def("get_extensions_information", &pypowsybl::getExtensionsInformation, "get more information about all extensions"); m.def("update_network_elements_with_series", pypowsybl::updateNetworkElementsWithSeries, "Update network elements for a given element type with a series", - py::call_guard(), py::arg("network"), py::arg("dataframe"), py::arg("element_type")); + py::call_guard(), py::arg("network"), py::arg("dataframe"), py::arg("element_type"), py::arg("per_unit"), py::arg("nominal_apparent_power")); m.def("create_dataframe", ::createDataframe, "create dataframe to update or create new elements", py::arg("columns_values"), py::arg("columns_names"), py::arg("columns_types"), py::arg("is_index")); diff --git a/cpp/src/pypowsybl.cpp b/cpp/src/pypowsybl.cpp index 0227cfa64d..6508c85059 100644 --- a/cpp/src/pypowsybl.cpp +++ b/cpp/src/pypowsybl.cpp @@ -986,9 +986,9 @@ matrix* getReferenceMatrix(const JavaHandle& sensitivityAnalysisResultContext, c (char*) matrixId.c_str(), (char*) contingencyId.c_str()); } -SeriesArray* createNetworkElementsSeriesArray(const JavaHandle& network, element_type elementType, filter_attributes_type filterAttributesType, const std::vector& attributes, dataframe* dataframe) { +SeriesArray* createNetworkElementsSeriesArray(const JavaHandle& network, element_type elementType, filter_attributes_type filterAttributesType, const std::vector& attributes, dataframe* dataframe, bool perUnit, double nominalApparentPower) { ToCharPtrPtr attributesPtr(attributes); - return new SeriesArray(callJava(::createNetworkElementsSeriesArray, network, elementType, filterAttributesType, attributesPtr.get(), attributes.size(), dataframe)); + return new SeriesArray(callJava(::createNetworkElementsSeriesArray, network, elementType, filterAttributesType, attributesPtr.get(), attributes.size(), dataframe, perUnit, nominalApparentPower)); } SeriesArray* createNetworkElementsExtensionSeriesArray(const JavaHandle& network, const std::string& extensionName, const std::string& tableName) { @@ -1091,8 +1091,8 @@ SeriesArray* getBusBreakerViewElements(const JavaHandle& network, std::string& v return new SeriesArray(callJava(::getBusBreakerViewElements, network, (char*) voltageLevel.c_str())); } -void updateNetworkElementsWithSeries(pypowsybl::JavaHandle network, dataframe* dataframe, element_type elementType) { - pypowsybl::callJava<>(::updateNetworkElementsWithSeries, network, elementType, dataframe); +void updateNetworkElementsWithSeries(pypowsybl::JavaHandle network, dataframe* dataframe, element_type elementType, bool perUnit, double nominalApparentPower) { + pypowsybl::callJava<>(::updateNetworkElementsWithSeries, network, elementType, dataframe, perUnit, nominalApparentPower); } std::vector convertDataframeMetadata(dataframe_metadata* dataframeMetadata) { diff --git a/cpp/src/pypowsybl.h b/cpp/src/pypowsybl.h index d561cba157..b81b6b943d 100644 --- a/cpp/src/pypowsybl.h +++ b/cpp/src/pypowsybl.h @@ -448,7 +448,7 @@ matrix* getSensitivityMatrix(const JavaHandle& sensitivityAnalysisResultContext, matrix* getReferenceMatrix(const JavaHandle& sensitivityAnalysisResultContext, const std::string& matrixId, const std::string& contingencyId); -SeriesArray* createNetworkElementsSeriesArray(const JavaHandle& network, element_type elementType, filter_attributes_type filterAttributesType, const std::vector& attributes, dataframe* dataframe); +SeriesArray* createNetworkElementsSeriesArray(const JavaHandle& network, element_type elementType, filter_attributes_type filterAttributesType, const std::vector& attributes, dataframe* dataframe, bool perUnit, double nominalApparentPower); void removeNetworkElements(const JavaHandle& network, const std::vector& elementIds); @@ -458,7 +458,7 @@ std::vector getExtensionsNames(); SeriesArray* getExtensionsInformation(); -void updateNetworkElementsWithSeries(pypowsybl::JavaHandle network, dataframe* dataframe, element_type elementType); +void updateNetworkElementsWithSeries(pypowsybl::JavaHandle network, dataframe* dataframe, element_type elementType, bool perUnit, double nominalApparentPower); std::string getWorkingVariantId(const JavaHandle& network); diff --git a/java/src/main/java/com/powsybl/dataframe/AbstractDataframeMapper.java b/java/src/main/java/com/powsybl/dataframe/AbstractDataframeMapper.java index e98393518d..b0f68990a8 100644 --- a/java/src/main/java/com/powsybl/dataframe/AbstractDataframeMapper.java +++ b/java/src/main/java/com/powsybl/dataframe/AbstractDataframeMapper.java @@ -7,6 +7,7 @@ package com.powsybl.dataframe; import com.powsybl.commons.PowsyblException; +import com.powsybl.dataframe.network.DataframeContext; import com.powsybl.dataframe.update.DoubleSeries; import com.powsybl.dataframe.update.IntSeries; import com.powsybl.dataframe.update.StringSeries; @@ -30,7 +31,7 @@ public abstract class AbstractDataframeMapper implements DataframeMapper> seriesMappers) { this.seriesMappers = seriesMappers.stream() - .collect(toImmutableMap(mapper -> mapper.getMetadata().getName(), Function.identity())); + .collect(toImmutableMap(mapper -> mapper.getMetadata().getName(), Function.identity())); } @Override @@ -48,15 +49,16 @@ public SeriesMetadata getSeriesMetadata(String seriesName) { } @Override - public void createDataframe(T object, DataframeHandler dataframeHandler, DataframeFilter dataframeFilter) { + public void createDataframe(T object, DataframeHandler dataframeHandler, DataframeFilter dataframeFilter, DataframeContext dataframeContext) { Collection> mappers = getSeriesMappers(dataframeFilter); dataframeHandler.allocate(mappers.size()); List items = getItems(object); - mappers.stream().forEach(mapper -> mapper.createSeries(items, dataframeHandler)); + mappers.forEach(mapper -> mapper.createSeries(items, dataframeHandler, dataframeContext)); } interface ColumnUpdater { - void update(int index, U object); + + void update(int index, U object, DataframeContext context); } private static final class IntColumnUpdater implements ColumnUpdater { @@ -69,7 +71,7 @@ private IntColumnUpdater(IntSeries values, SeriesMapper mapper) { } @Override - public void update(int index, U object) { + public void update(int index, U object, DataframeContext context) { mapper.updateInt(object, values.get(index)); } } @@ -84,8 +86,8 @@ private DoubleColumnUpdater(DoubleSeries values, SeriesMapper mapper) { } @Override - public void update(int index, U object) { - mapper.updateDouble(object, values.get(index)); + public void update(int index, U object, DataframeContext context) { + mapper.updateDouble(object, values.get(index), context); } } @@ -99,13 +101,13 @@ private StringColumnUpdater(StringSeries values, SeriesMapper mapper) { } @Override - public void update(int index, U object) { + public void update(int index, U object, DataframeContext context) { mapper.updateString(object, values.get(index)); } } @Override - public void updateSeries(T object, UpdatingDataframe updatingDataframe) { + public void updateSeries(T object, UpdatingDataframe updatingDataframe, DataframeContext context) { //Setup links to minimize searches on column names List> updaters = new ArrayList<>(); @@ -127,7 +129,7 @@ public void updateSeries(T object, UpdatingDataframe updatingDataframe) { for (int i = 0; i < updatingDataframe.getRowCount(); i++) { U item = getItem(object, updatingDataframe, i); int itemIndex = i; - updaters.forEach(updater -> updater.update(itemIndex, item)); + updaters.forEach(updater -> updater.update(itemIndex, item, context)); } } @@ -139,15 +141,15 @@ public boolean isSeriesMetaDataExists(String seriesName) { public Collection> getSeriesMappers(DataframeFilter dataframeFilter) { Collection> mappers = seriesMappers.values(); return mappers.stream() - .filter(mapper -> filterMapper(mapper, dataframeFilter)) - .collect(Collectors.toList()); + .filter(mapper -> filterMapper(mapper, dataframeFilter)) + .collect(Collectors.toList()); } protected boolean filterMapper(SeriesMapper mapper, DataframeFilter dataframeFilter) { return switch (dataframeFilter.getAttributeFilterType()) { case DEFAULT_ATTRIBUTES -> mapper.getMetadata().isDefaultAttribute() || mapper.getMetadata().isIndex(); case INPUT_ATTRIBUTES -> - dataframeFilter.getInputAttributes().contains(mapper.getMetadata().getName()) || mapper.getMetadata().isIndex(); + dataframeFilter.getInputAttributes().contains(mapper.getMetadata().getName()) || mapper.getMetadata().isIndex(); case ALL_ATTRIBUTES -> true; }; } diff --git a/java/src/main/java/com/powsybl/dataframe/BaseDataframeMapperBuilder.java b/java/src/main/java/com/powsybl/dataframe/BaseDataframeMapperBuilder.java index bbf6a04a38..1fc2d5697a 100644 --- a/java/src/main/java/com/powsybl/dataframe/BaseDataframeMapperBuilder.java +++ b/java/src/main/java/com/powsybl/dataframe/BaseDataframeMapperBuilder.java @@ -7,6 +7,7 @@ package com.powsybl.dataframe; import com.powsybl.commons.PowsyblException; +import com.powsybl.dataframe.network.DataframeContext; import com.powsybl.dataframe.update.UpdatingDataframe; import java.util.ArrayList; @@ -65,11 +66,20 @@ public B doubles(String name, ToDoubleFunction value, DoubleSeriesMapper.Doub return doubles(name, value, updater, true); } - public B doubles(String name, ToDoubleFunction value, DoubleSeriesMapper.DoubleUpdater updater, boolean defaultAttribute) { + public B doubles(String name, ToDoubleBiFunction value, DoubleSeriesMapper.DoubleUpdater updater, boolean defaultAttribute) { series.add(new DoubleSeriesMapper<>(name, value, updater, defaultAttribute)); return (B) this; } + public B doubles(String name, ToDoubleBiFunction value, DoubleSeriesMapper.DoubleUpdater updater) { + series.add(new DoubleSeriesMapper<>(name, value, updater, true)); + return (B) this; + } + + public B doubles(String name, ToDoubleFunction value, DoubleSeriesMapper.DoubleUpdater updater, boolean defaultAttribute) { + return doubles(name, (u, pu) -> value.applyAsDouble(u), updater, defaultAttribute); + } + public B doubles(Map> nameValuesMap) { nameValuesMap.forEach(this::doubles); return (B) this; @@ -79,6 +89,14 @@ public B doubles(String name, ToDoubleFunction value) { return doubles(name, value, null, true); } + public B doubles(String name, ToDoubleBiFunction value) { + return doubles(name, value, (DoubleSeriesMapper.DoubleUpdater) null, true); + } + + public B doubles(String name, ToDoubleBiFunction value, boolean defaultAttribute) { + return doubles(name, value, (DoubleSeriesMapper.DoubleUpdater) null, defaultAttribute); + } + public B doubles(String name, ToDoubleFunction value, boolean defaultAttribute) { return doubles(name, value, null, defaultAttribute); } diff --git a/java/src/main/java/com/powsybl/dataframe/BooleanSeriesMapper.java b/java/src/main/java/com/powsybl/dataframe/BooleanSeriesMapper.java index 6fd1d6ccd1..cfe945a7e3 100644 --- a/java/src/main/java/com/powsybl/dataframe/BooleanSeriesMapper.java +++ b/java/src/main/java/com/powsybl/dataframe/BooleanSeriesMapper.java @@ -6,6 +6,8 @@ */ package com.powsybl.dataframe; +import com.powsybl.dataframe.network.DataframeContext; + import java.util.List; import java.util.function.Predicate; @@ -39,7 +41,7 @@ public SeriesMetadata getMetadata() { } @Override - public void createSeries(List items, DataframeHandler handler) { + public void createSeries(List items, DataframeHandler handler, DataframeContext dataframeContext) { DataframeHandler.BooleanSeriesWriter writer = handler.newBooleanSeries(metadata.getName(), items.size()); for (int i = 0; i < items.size(); i++) { writer.set(i, value.test(items.get(i))); diff --git a/java/src/main/java/com/powsybl/dataframe/DataframeMapper.java b/java/src/main/java/com/powsybl/dataframe/DataframeMapper.java index a6698e7aa9..70c08e0557 100644 --- a/java/src/main/java/com/powsybl/dataframe/DataframeMapper.java +++ b/java/src/main/java/com/powsybl/dataframe/DataframeMapper.java @@ -6,6 +6,7 @@ */ package com.powsybl.dataframe; +import com.powsybl.dataframe.network.DataframeContext; import com.powsybl.dataframe.update.UpdatingDataframe; import java.util.List; @@ -26,7 +27,7 @@ public interface DataframeMapper { * Provides dataframe data to the handler, which is responsible to * format it as needed. */ - void createDataframe(T object, DataframeHandler dataframeHandler, DataframeFilter dataframeFilter); + void createDataframe(T object, DataframeHandler dataframeHandler, DataframeFilter dataframeFilter, DataframeContext dataframeContext); List getSeriesMetadata(); @@ -35,7 +36,7 @@ public interface DataframeMapper { /** * Updates object data with the provided series. */ - void updateSeries(T object, UpdatingDataframe updatingDataframe); + void updateSeries(T object, UpdatingDataframe updatingDataframe, DataframeContext context); boolean isSeriesMetaDataExists(String seriesName); } diff --git a/java/src/main/java/com/powsybl/dataframe/DoubleSeriesMapper.java b/java/src/main/java/com/powsybl/dataframe/DoubleSeriesMapper.java index fd15a5c732..dff8f2ee5a 100644 --- a/java/src/main/java/com/powsybl/dataframe/DoubleSeriesMapper.java +++ b/java/src/main/java/com/powsybl/dataframe/DoubleSeriesMapper.java @@ -6,8 +6,10 @@ */ package com.powsybl.dataframe; +import com.powsybl.dataframe.network.DataframeContext; + import java.util.List; -import java.util.function.ToDoubleFunction; +import java.util.function.ToDoubleBiFunction; /** * @author Sylvain Leclerc @@ -16,18 +18,18 @@ public class DoubleSeriesMapper implements SeriesMapper { private final SeriesMetadata metadata; private final DoubleUpdater updater; - private final ToDoubleFunction value; + private final ToDoubleBiFunction value; @FunctionalInterface public interface DoubleUpdater { - void update(U object, double value); + void update(U object, double value, DataframeContext context); } - public DoubleSeriesMapper(String name, ToDoubleFunction value) { + public DoubleSeriesMapper(String name, ToDoubleBiFunction value) { this(name, value, null, true); } - public DoubleSeriesMapper(String name, ToDoubleFunction value, DoubleUpdater updater, boolean defaultAttribute) { + public DoubleSeriesMapper(String name, ToDoubleBiFunction value, DoubleUpdater updater, boolean defaultAttribute) { this.metadata = new SeriesMetadata(false, name, updater != null, SeriesDataType.DOUBLE, defaultAttribute); this.updater = updater; this.value = value; @@ -39,19 +41,19 @@ public SeriesMetadata getMetadata() { } @Override - public void createSeries(List items, DataframeHandler factory) { + public void createSeries(List items, DataframeHandler factory, DataframeContext dataframeContext) { DataframeHandler.DoubleSeriesWriter writer = factory.newDoubleSeries(metadata.getName(), items.size()); for (int i = 0; i < items.size(); i++) { - writer.set(i, value.applyAsDouble(items.get(i))); + writer.set(i, value.applyAsDouble(items.get(i), dataframeContext)); } } @Override - public void updateDouble(T object, double value) { + public void updateDouble(T object, double value, DataframeContext context) { if (updater == null) { throw new UnsupportedOperationException("Series '" + getMetadata().getName() + "' is not modifiable."); } - updater.update(object, value); + updater.update(object, value, context); } } diff --git a/java/src/main/java/com/powsybl/dataframe/EnumSeriesMapper.java b/java/src/main/java/com/powsybl/dataframe/EnumSeriesMapper.java index 3bc2c5dbe2..4a8f79756b 100644 --- a/java/src/main/java/com/powsybl/dataframe/EnumSeriesMapper.java +++ b/java/src/main/java/com/powsybl/dataframe/EnumSeriesMapper.java @@ -6,6 +6,8 @@ */ package com.powsybl.dataframe; +import com.powsybl.dataframe.network.DataframeContext; + import java.util.List; import java.util.Objects; import java.util.function.BiConsumer; @@ -38,7 +40,7 @@ public SeriesMetadata getMetadata() { } @Override - public void createSeries(List items, DataframeHandler factory) { + public void createSeries(List items, DataframeHandler factory, DataframeContext dataframeContext) { DataframeHandler.StringSeriesWriter writer = factory.newStringSeries(metadata.getName(), items.size()); for (int i = 0; i < items.size(); i++) { writer.set(i, Objects.toString(value.apply(items.get(i)), "")); diff --git a/java/src/main/java/com/powsybl/dataframe/IntSeriesMapper.java b/java/src/main/java/com/powsybl/dataframe/IntSeriesMapper.java index 4ba02d265c..f968645ec2 100644 --- a/java/src/main/java/com/powsybl/dataframe/IntSeriesMapper.java +++ b/java/src/main/java/com/powsybl/dataframe/IntSeriesMapper.java @@ -6,6 +6,8 @@ */ package com.powsybl.dataframe; +import com.powsybl.dataframe.network.DataframeContext; + import java.util.List; import java.util.function.ToIntFunction; @@ -51,7 +53,7 @@ public SeriesMetadata getMetadata() { } @Override - public void createSeries(List items, DataframeHandler handler) { + public void createSeries(List items, DataframeHandler handler, DataframeContext dataframeContext) { boolean index = metadata.isIndex(); String name = metadata.getName(); DataframeHandler.IntSeriesWriter writer = index ? handler.newIntIndex(name, items.size()) : handler.newIntSeries(name, items.size()); diff --git a/java/src/main/java/com/powsybl/dataframe/MappingUtils.java b/java/src/main/java/com/powsybl/dataframe/MappingUtils.java index 93147564c1..b92d9621cb 100644 --- a/java/src/main/java/com/powsybl/dataframe/MappingUtils.java +++ b/java/src/main/java/com/powsybl/dataframe/MappingUtils.java @@ -6,7 +6,11 @@ */ package com.powsybl.dataframe; +import com.powsybl.dataframe.network.DataframeContext; +import com.powsybl.dataframe.network.PerUnitUtil; + import java.util.function.Function; +import java.util.function.ToDoubleBiFunction; import java.util.function.ToDoubleFunction; import java.util.function.ToIntFunction; @@ -40,6 +44,20 @@ public static ToDoubleFunction ifExistsDouble(Function objectGet return ifExistsDouble(objectGetter, valueGetter, Double.NaN); } + public static ToDoubleBiFunction ifExistsDoublePerUnitPQ(Function objectGetter, ToDoubleFunction valueGetter) { + return (item, context) -> { + U object = objectGetter.apply(item); + return object != null ? PerUnitUtil.perUnitPQ(context, valueGetter.applyAsDouble(object)) : Double.NaN; + }; + } + + public static ToDoubleBiFunction ifExistsDoublePerUnitAngle(Function objectGetter, ToDoubleFunction valueGetter) { + return (item, context) -> { + U object = objectGetter.apply(item); + return object != null ? PerUnitUtil.perUnitAngle(context, valueGetter.applyAsDouble(object)) : Double.NaN; + }; + } + /** * Maps T to another object U and returns the specified undefined value if U is {@code null} */ diff --git a/java/src/main/java/com/powsybl/dataframe/SeriesMapper.java b/java/src/main/java/com/powsybl/dataframe/SeriesMapper.java index 468231d4f5..569e5fbef4 100644 --- a/java/src/main/java/com/powsybl/dataframe/SeriesMapper.java +++ b/java/src/main/java/com/powsybl/dataframe/SeriesMapper.java @@ -6,6 +6,8 @@ */ package com.powsybl.dataframe; +import com.powsybl.dataframe.network.DataframeContext; + import java.util.List; /** @@ -19,13 +21,13 @@ public interface SeriesMapper { SeriesMetadata getMetadata(); - void createSeries(List items, DataframeHandler factory); + void createSeries(List items, DataframeHandler factory, DataframeContext dataframeContext); default void updateInt(T object, int value) { throw new UnsupportedOperationException("Cannot update series with int: " + getMetadata().getName()); } - default void updateDouble(T object, double value) { + default void updateDouble(T object, double value, DataframeContext context) { throw new UnsupportedOperationException("Cannot update series with double: " + getMetadata().getName()); } diff --git a/java/src/main/java/com/powsybl/dataframe/StringSeriesMapper.java b/java/src/main/java/com/powsybl/dataframe/StringSeriesMapper.java index 35c244bdcf..0d049796d7 100644 --- a/java/src/main/java/com/powsybl/dataframe/StringSeriesMapper.java +++ b/java/src/main/java/com/powsybl/dataframe/StringSeriesMapper.java @@ -6,6 +6,8 @@ */ package com.powsybl.dataframe; +import com.powsybl.dataframe.network.DataframeContext; + import java.util.List; import java.util.function.BiConsumer; import java.util.function.Function; @@ -51,7 +53,7 @@ public SeriesMetadata getMetadata() { } @Override - public void createSeries(List items, DataframeHandler handler) { + public void createSeries(List items, DataframeHandler handler, DataframeContext dataframeContext) { boolean index = getMetadata().isIndex(); String name = getMetadata().getName(); DataframeHandler.StringSeriesWriter writer = index ? handler.newStringIndex(name, items.size()) : handler.newStringSeries(name, items.size()); diff --git a/java/src/main/java/com/powsybl/dataframe/network/AbstractNetworkDataframeMapper.java b/java/src/main/java/com/powsybl/dataframe/network/AbstractNetworkDataframeMapper.java index 32307ce8fd..dac034c97f 100644 --- a/java/src/main/java/com/powsybl/dataframe/network/AbstractNetworkDataframeMapper.java +++ b/java/src/main/java/com/powsybl/dataframe/network/AbstractNetworkDataframeMapper.java @@ -30,14 +30,14 @@ public AbstractNetworkDataframeMapper(List> seriesMappers, boole } @Override - public void createDataframe(Network network, DataframeHandler dataframeHandler, DataframeFilter dataframeFilter) { + public void createDataframe(Network network, DataframeHandler dataframeHandler, DataframeFilter dataframeFilter, DataframeContext dataframeContext) { List items = getFilteredItems(network, dataframeFilter); List> mappers = new ArrayList<>(getSeriesMappers(dataframeFilter)); if (addProperties) { mappers.addAll(getPropertiesSeries(items, dataframeFilter)); } dataframeHandler.allocate(mappers.size()); - mappers.stream().forEach(mapper -> mapper.createSeries(items, dataframeHandler)); + mappers.forEach(mapper -> mapper.createSeries(items, dataframeHandler, dataframeContext)); } protected List getFilteredItems(Network network, DataframeFilter dataframeFilter) { diff --git a/java/src/main/java/com/powsybl/dataframe/network/DataframeContext.java b/java/src/main/java/com/powsybl/dataframe/network/DataframeContext.java new file mode 100644 index 0000000000..fdc48538d3 --- /dev/null +++ b/java/src/main/java/com/powsybl/dataframe/network/DataframeContext.java @@ -0,0 +1,33 @@ +/** + * Copyright (c) 2024, 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.dataframe.network; + +/** + * @author Etienne Lesot {@literal } + */ +public class DataframeContext { + private final boolean perUnit; + private final double nominalApparentPower; + + public DataframeContext(boolean perUnit, double nominalApparentPower) { + this.perUnit = perUnit; + this.nominalApparentPower = nominalApparentPower; + } + + public boolean isPerUnit() { + return perUnit; + } + + public double getNominalApparentPower() { + return nominalApparentPower; + } + + public static DataframeContext deactivate() { + return new DataframeContext(false, 0); + } +} diff --git a/java/src/main/java/com/powsybl/dataframe/network/NetworkDataframes.java b/java/src/main/java/com/powsybl/dataframe/network/NetworkDataframes.java index 8b36ba5e0e..697a022382 100644 --- a/java/src/main/java/com/powsybl/dataframe/network/NetworkDataframes.java +++ b/java/src/main/java/com/powsybl/dataframe/network/NetworkDataframes.java @@ -9,7 +9,7 @@ import com.powsybl.commons.PowsyblException; import com.powsybl.dataframe.BooleanSeriesMapper; import com.powsybl.dataframe.DataframeElementType; -import com.powsybl.dataframe.DoubleSeriesMapper.DoubleUpdater; +import com.powsybl.dataframe.DoubleSeriesMapper; import com.powsybl.dataframe.network.extensions.NetworkExtensions; import com.powsybl.dataframe.network.extensions.ExtensionDataframeKey; import com.powsybl.dataframe.update.UpdatingDataframe; @@ -21,14 +21,11 @@ import org.apache.commons.lang3.tuple.Triple; import java.util.*; -import java.util.function.BiFunction; -import java.util.function.Function; -import java.util.function.ToDoubleFunction; -import java.util.function.ToIntFunction; +import java.util.function.*; import java.util.stream.Stream; -import static com.powsybl.dataframe.MappingUtils.ifExistsDouble; -import static com.powsybl.dataframe.MappingUtils.ifExistsInt; +import static com.powsybl.dataframe.MappingUtils.*; +import static com.powsybl.dataframe.network.PerUnitUtil.*; /** * Main user entry point of the package : @@ -88,56 +85,56 @@ private static Map createMappers() return Collections.unmodifiableMap(mappers); } - static > ToDoubleFunction getP() { - return inj -> inj.getTerminal().getP(); + static > ToDoubleBiFunction getPerUnitP() { + return (inj, context) -> perUnitPQ(context, inj.getTerminal().getP()); } static ToDoubleFunction getOppositeP() { return inj -> -inj.getTerminal().getP(); } - static > ToDoubleFunction getQ() { - return inj -> inj.getTerminal().getQ(); + static > ToDoubleBiFunction getPerUnitQ() { + return (inj, context) -> perUnitPQ(context, inj.getTerminal().getQ()); } - static > DoubleUpdater setP() { - return (inj, p) -> inj.getTerminal().setP(p); + static > DoubleSeriesMapper.DoubleUpdater setPerUnitP() { + return (inj, p, context) -> inj.getTerminal().setP(unPerUnitPQ(context, p)); } - static > DoubleUpdater setQ() { - return (inj, q) -> inj.getTerminal().setQ(q); + static > DoubleSeriesMapper.DoubleUpdater setPerUnitQ() { + return (inj, q, context) -> inj.getTerminal().setQ(unPerUnitPQ(context, q)); } - static > ToDoubleFunction getP1() { - return b -> b.getTerminal1().getP(); + static > ToDoubleBiFunction getPerUnitP1() { + return (b, context) -> perUnitPQ(context, b.getTerminal1().getP()); } - static > ToDoubleFunction getQ1() { - return b -> b.getTerminal1().getQ(); + static > ToDoubleBiFunction getPerUnitQ1() { + return (b, context) -> perUnitPQ(context, b.getTerminal1().getQ()); } - static > DoubleUpdater setP1() { - return (b, p) -> b.getTerminal1().setP(p); + static > DoubleSeriesMapper.DoubleUpdater setPerUnitP1() { + return (b, p, context) -> b.getTerminal1().setP(unPerUnitPQ(context, p)); } - static > DoubleUpdater setQ1() { - return (b, q) -> b.getTerminal1().setQ(q); + static > DoubleSeriesMapper.DoubleUpdater setPerUnitQ1() { + return (b, q, context) -> b.getTerminal1().setQ(unPerUnitPQ(context, q)); } - static > ToDoubleFunction getP2() { - return b -> b.getTerminal2().getP(); + static > ToDoubleBiFunction getPerUnitP2() { + return (b, context) -> perUnitPQ(context, b.getTerminal2().getP()); } - static > ToDoubleFunction getQ2() { - return b -> b.getTerminal2().getQ(); + static > ToDoubleBiFunction getPerUnitQ2() { + return (b, context) -> perUnitPQ(context, b.getTerminal2().getQ()); } - static > DoubleUpdater setP2() { - return (b, p) -> b.getTerminal2().setP(p); + static > DoubleSeriesMapper.DoubleUpdater setPerUnitP2() { + return (b, p, context) -> b.getTerminal2().setP(unPerUnitPQ(context, p)); } - static > DoubleUpdater setQ2() { - return (b, q) -> b.getTerminal2().setQ(q); + static > DoubleSeriesMapper.DoubleUpdater setPerUnitQ2() { + return (b, q, context) -> b.getTerminal2().setQ(unPerUnitPQ(context, q)); } static > Function getVoltageLevelId() { @@ -149,40 +146,42 @@ private static MinMaxReactiveLimits getMinMaxReactiveLimits(ReactiveLimitsHolder return reactiveLimits instanceof MinMaxReactiveLimits ? (MinMaxReactiveLimits) reactiveLimits : null; } - static ToDoubleFunction getMinQ(ToDoubleFunction pGetter) { - return g -> { + static ToDoubleBiFunction getPerUnitMinQ(ToDoubleFunction pGetter) { + return (g, context) -> { ReactiveLimits reactiveLimits = g.getReactiveLimits(); - return (reactiveLimits == null) ? Double.NaN : reactiveLimits.getMinQ(pGetter.applyAsDouble(g)); + return (reactiveLimits == null) ? Double.NaN : perUnitPQ(context, reactiveLimits.getMinQ(pGetter.applyAsDouble(g))); }; } - static ToDoubleFunction getMaxQ(ToDoubleFunction pGetter) { - return g -> { + static ToDoubleBiFunction getPerUnitMaxQ(ToDoubleFunction pGetter) { + return (g, context) -> { ReactiveLimits reactiveLimits = g.getReactiveLimits(); - return (reactiveLimits == null) ? Double.NaN : reactiveLimits.getMaxQ(pGetter.applyAsDouble(g)); + return (reactiveLimits == null) ? Double.NaN : perUnitPQ(context, reactiveLimits.getMaxQ(pGetter.applyAsDouble(g))); }; } - static DoubleUpdater setMinQ() { - return (g, minQ) -> { + static DoubleSeriesMapper.DoubleUpdater setPerUnitMinQ() { + return (g, minQ, context) -> { MinMaxReactiveLimits minMaxReactiveLimits = getMinMaxReactiveLimits(g); if (minMaxReactiveLimits != null) { - g.newMinMaxReactiveLimits().setMinQ(minQ).setMaxQ(minMaxReactiveLimits.getMaxQ()).add(); + g.newMinMaxReactiveLimits().setMinQ(unPerUnitPQ(context, minQ)) + .setMaxQ(minMaxReactiveLimits.getMaxQ()).add(); } else { throw new UnsupportedOperationException("Cannot update minQ to " + minQ + - ": Min-Max reactive limits do not exist."); + ": Min-Max reactive limits do not exist."); } }; } - static DoubleUpdater setMaxQ() { - return (g, maxQ) -> { + static DoubleSeriesMapper.DoubleUpdater setPerUnitMaxQ() { + return (g, maxQ, context) -> { MinMaxReactiveLimits minMaxReactiveLimits = getMinMaxReactiveLimits(g); if (minMaxReactiveLimits != null) { - g.newMinMaxReactiveLimits().setMaxQ(maxQ).setMinQ(minMaxReactiveLimits.getMinQ()).add(); + g.newMinMaxReactiveLimits().setMaxQ(unPerUnitPQ(context, maxQ)) + .setMinQ(minMaxReactiveLimits.getMinQ()).add(); } else { throw new UnsupportedOperationException("Cannot update maxQ to " + maxQ + - ": Min-Max reactive limits do not exist."); + ": Min-Max reactive limits do not exist."); } }; } @@ -247,25 +246,28 @@ static NetworkDataframeMapper generators() { .stringsIndex("id", Generator::getId) .strings("name", g -> g.getOptionalName().orElse("")) .enums("energy_source", EnergySource.class, Generator::getEnergySource, Generator::setEnergySource) - .doubles("target_p", Generator::getTargetP, Generator::setTargetP) - .doubles("min_p", Generator::getMinP, Generator::setMinP) - .doubles("max_p", Generator::getMaxP, Generator::setMaxP) - .doubles("min_q", ifExistsDouble(NetworkDataframes::getMinMaxReactiveLimits, MinMaxReactiveLimits::getMinQ), setMinQ()) - .doubles("max_q", ifExistsDouble(NetworkDataframes::getMinMaxReactiveLimits, MinMaxReactiveLimits::getMaxQ), setMaxQ()) - .doubles("min_q_at_target_p", getMinQ(Generator::getTargetP), false) - .doubles("max_q_at_target_p", getMaxQ(Generator::getTargetP), false) - .doubles("min_q_at_p", getMinQ(getOppositeP()), false) - .doubles("max_q_at_p", getMaxQ(getOppositeP()), false) - .doubles("rated_s", Generator::getRatedS, Generator::setRatedS) + .doubles("target_p", (g, context) -> perUnitPQ(context, g.getTargetP()), (g, targetP, context) -> g.setTargetP(unPerUnitPQ(context, targetP))) + .doubles("min_p", (g, context) -> perUnitPQ(context, g.getMinP()), (g, minP, context) -> g.setMinP(unPerUnitPQ(context, minP))) + .doubles("max_p", (g, context) -> perUnitPQ(context, g.getMaxP()), (g, maxP, context) -> g.setMaxP(unPerUnitPQ(context, maxP))) + .doubles("min_q", ifExistsDoublePerUnitPQ(NetworkDataframes::getMinMaxReactiveLimits, MinMaxReactiveLimits::getMinQ), + setPerUnitMinQ()) + .doubles("max_q", ifExistsDoublePerUnitPQ(NetworkDataframes::getMinMaxReactiveLimits, MinMaxReactiveLimits::getMaxQ), + setPerUnitMaxQ()) + .doubles("min_q_at_target_p", getPerUnitMinQ(Generator::getTargetP), false) + .doubles("max_q_at_target_p", getPerUnitMaxQ(Generator::getTargetP), false) + .doubles("min_q_at_p", getPerUnitMinQ(getOppositeP()), false) + .doubles("max_q_at_p", getPerUnitMaxQ(getOppositeP()), false) + .doubles("rated_s", (g, context) -> g.getRatedS(), (g, ratedS, context) -> g.setRatedS(ratedS)) .strings("reactive_limits_kind", NetworkDataframes::getReactiveLimitsKind) - .doubles("target_v", Generator::getTargetV, Generator::setTargetV) - .doubles("target_q", Generator::getTargetQ, Generator::setTargetQ) + .doubles("target_v", (g, context) -> perUnitV(context, g.getTargetV(), g.getRegulatingTerminal()), + (g, v, context) -> g.setTargetV(unPerUnitV(context, v, g.getRegulatingTerminal()))) + .doubles("target_q", (g, context) -> perUnitPQ(context, g.getTargetQ()), (g, q, context) -> g.setTargetQ(unPerUnitPQ(context, q))) .booleans("voltage_regulator_on", Generator::isVoltageRegulatorOn, Generator::setVoltageRegulatorOn) .strings("regulated_element_id", generator -> NetworkUtil.getRegulatedElementId(generator::getRegulatingTerminal), (generator, elementId) -> NetworkUtil.setRegulatingTerminal(generator::setRegulatingTerminal, generator.getNetwork(), elementId)) - .doubles("p", getP(), setP()) - .doubles("q", getQ(), setQ()) - .doubles("i", g -> g.getTerminal().getI()) + .doubles("p", getPerUnitP(), setPerUnitP()) + .doubles("q", getPerUnitQ(), setPerUnitQ()) + .doubles("i", (g, context) -> perUnitI(context, g.getTerminal())) .strings("voltage_level_id", getVoltageLevelId()) .strings("bus_id", g -> getBusId(g.getTerminal())) .strings("bus_breaker_bus_id", busBreakerViewBusId(), false) @@ -288,8 +290,9 @@ static NetworkDataframeMapper buses() { getOrThrow((b, id) -> b.getBusView().getBus(id), "Bus")) .stringsIndex("id", Bus::getId) .strings("name", b -> b.getOptionalName().orElse("")) - .doubles("v_mag", Bus::getV, Bus::setV) - .doubles("v_angle", Bus::getAngle, Bus::setAngle) + .doubles("v_mag", (b, context) -> perUnitV(context, b.getV(), b), + (b, v, context) -> b.setV(unPerUnitV(context, v, b))) + .doubles("v_angle", (b, context) -> perUnitAngle(context, b.getAngle()), (b, vAngle, context) -> b.setAngle(unPerUnitAngle(context, vAngle))) .ints("connected_component", ifExistsInt(Bus::getConnectedComponent, Component::getNum)) .ints("synchronous_component", ifExistsInt(Bus::getSynchronousComponent, Component::getNum)) .strings("voltage_level_id", b -> b.getVoltageLevel().getId()) @@ -303,11 +306,11 @@ static NetworkDataframeMapper loads() { .stringsIndex("id", Load::getId) .strings("name", l -> l.getOptionalName().orElse("")) .enums("type", LoadType.class, Load::getLoadType) - .doubles("p0", Load::getP0, Load::setP0) - .doubles("q0", Load::getQ0, Load::setQ0) - .doubles("p", getP(), setP()) - .doubles("q", getQ(), setQ()) - .doubles("i", l -> l.getTerminal().getI()) + .doubles("p0", (l, context) -> perUnitPQ(context, l.getP0()), (l, p, context) -> l.setP0(unPerUnitPQ(context, p))) + .doubles("q0", (l, context) -> perUnitPQ(context, l.getQ0()), (l, q, context) -> l.setQ0(unPerUnitPQ(context, q))) + .doubles("p", getPerUnitP(), setPerUnitP()) + .doubles("q", getPerUnitQ(), setPerUnitQ()) + .doubles("i", (l, context) -> perUnitI(context, l.getTerminal())) .strings("voltage_level_id", getVoltageLevelId()) .strings("bus_id", l -> getBusId(l.getTerminal())) .strings("bus_breaker_bus_id", busBreakerViewBusId(), false) @@ -322,16 +325,18 @@ static NetworkDataframeMapper batteries() { return NetworkDataframeMapperBuilder.ofStream(Network::getBatteryStream, getOrThrow(Network::getBattery, "Battery")) .stringsIndex("id", Battery::getId) .strings("name", b -> b.getOptionalName().orElse("")) - .doubles("max_p", Battery::getMaxP, Battery::setMaxP) - .doubles("min_p", Battery::getMinP, Battery::setMinP) - .doubles("min_q", ifExistsDouble(NetworkDataframes::getMinMaxReactiveLimits, MinMaxReactiveLimits::getMinQ), setMinQ()) - .doubles("max_q", ifExistsDouble(NetworkDataframes::getMinMaxReactiveLimits, MinMaxReactiveLimits::getMaxQ), setMaxQ()) + .doubles("max_p", (b, context) -> perUnitPQ(context, b.getMaxP()), (b, maxP, context) -> b.setMaxP(unPerUnitPQ(context, maxP))) + .doubles("min_p", (b, context) -> perUnitPQ(context, b.getMinP()), (b, minP, context) -> b.setMinP(unPerUnitPQ(context, minP))) + .doubles("min_q", ifExistsDoublePerUnitPQ(NetworkDataframes::getMinMaxReactiveLimits, MinMaxReactiveLimits::getMinQ), + setPerUnitMinQ()) + .doubles("max_q", ifExistsDoublePerUnitPQ(NetworkDataframes::getMinMaxReactiveLimits, MinMaxReactiveLimits::getMaxQ), + setPerUnitMaxQ()) .strings("reactive_limits_kind", NetworkDataframes::getReactiveLimitsKind) - .doubles("target_p", Battery::getTargetP, Battery::setTargetP) - .doubles("target_q", Battery::getTargetQ, Battery::setTargetQ) - .doubles("p", getP(), setP()) - .doubles("q", getQ(), setQ()) - .doubles("i", b -> b.getTerminal().getI()) + .doubles("target_p", (b, context) -> perUnitPQ(context, b.getTargetP()), (b, targetP, context) -> b.setTargetP(unPerUnitPQ(context, targetP))) + .doubles("target_q", (b, context) -> perUnitPQ(context, b.getTargetQ()), (b, targetQ, context) -> b.setTargetQ(unPerUnitPQ(context, targetQ))) + .doubles("p", getPerUnitP(), setPerUnitP()) + .doubles("q", getPerUnitQ(), setPerUnitQ()) + .doubles("i", (b, context) -> perUnitI(context, b.getTerminal())) .strings("voltage_level_id", getVoltageLevelId()) .strings("bus_id", b -> getBusId(b.getTerminal())) .strings("bus_breaker_bus_id", busBreakerViewBusId(), false) @@ -346,18 +351,20 @@ static NetworkDataframeMapper shunts() { return NetworkDataframeMapperBuilder.ofStream(Network::getShuntCompensatorStream, getOrThrow(Network::getShuntCompensator, "Shunt compensator")) .stringsIndex("id", ShuntCompensator::getId) .strings("name", sc -> sc.getOptionalName().orElse("")) - .doubles("g", ShuntCompensator::getG) - .doubles("b", ShuntCompensator::getB) + .doubles("g", (shunt, context) -> perUnitG(context, shunt)) + .doubles("b", (shunt, context) -> perUnitB(context, shunt)) .enums("model_type", ShuntCompensatorModelType.class, ShuntCompensator::getModelType) .ints("max_section_count", ShuntCompensator::getMaximumSectionCount) .ints("section_count", ShuntCompensator::getSectionCount, ShuntCompensator::setSectionCount) .booleans("voltage_regulation_on", ShuntCompensator::isVoltageRegulatorOn, ShuntCompensator::setVoltageRegulatorOn) - .doubles("target_v", ShuntCompensator::getTargetV, ShuntCompensator::setTargetV) - .doubles("target_deadband", ShuntCompensator::getTargetDeadband, ShuntCompensator::setTargetDeadband) + .doubles("target_v", (sc, context) -> perUnitV(context, sc.getTargetV(), sc.getRegulatingTerminal()), + (sc, v, context) -> sc.setTargetV(unPerUnitV(context, v, sc.getRegulatingTerminal()))) + .doubles("target_deadband", (sc, context) -> perUnitV(context, sc.getTargetDeadband(), sc.getRegulatingTerminal()), + (sc, tb, context) -> sc.setTargetDeadband(unPerUnitV(context, tb, sc.getRegulatingTerminal()))) .strings("regulating_bus_id", sc -> getBusId(sc.getRegulatingTerminal())) - .doubles("p", getP(), setP()) - .doubles("q", getQ(), setQ()) - .doubles("i", sc -> sc.getTerminal().getI()) + .doubles("p", getPerUnitP(), setPerUnitP()) + .doubles("q", getPerUnitQ(), setPerUnitQ()) + .doubles("i", (sc, context) -> perUnitI(context, sc.getTerminal())) .strings("voltage_level_id", getVoltageLevelId()) .strings("bus_id", sc -> getBusId(sc.getTerminal())) .strings("bus_breaker_bus_id", busBreakerViewBusId(), false) @@ -369,22 +376,24 @@ static NetworkDataframeMapper shunts() { } static NetworkDataframeMapper shuntsNonLinear() { - Function>> nonLinearShunts = network -> + Function>> nonLinearShunts = network -> network.getShuntCompensatorStream() .filter(sc -> sc.getModelType() == ShuntCompensatorModelType.NON_LINEAR) .flatMap(shuntCompensator -> { ShuntCompensatorNonLinearModel model = (ShuntCompensatorNonLinearModel) shuntCompensator.getModel(); - return model.getAllSections().stream().map(section -> Triple.of(shuntCompensator.getId(), section, model.getAllSections().indexOf(section))); + return model.getAllSections().stream().map(section -> Triple.of(shuntCompensator, section, model.getAllSections().indexOf(section))); }); return NetworkDataframeMapperBuilder.ofStream(nonLinearShunts, NetworkDataframes::getShuntSectionNonlinear) - .stringsIndex("id", Triple::getLeft) + .stringsIndex("id", triple -> triple.getLeft().getId()) .intsIndex("section", Triple::getRight) - .doubles("g", p -> p.getMiddle().getG(), (p, g) -> p.getMiddle().setG(g)) - .doubles("b", p -> p.getMiddle().getB(), (p, b) -> p.getMiddle().setB(b)) + .doubles("g", (p, context) -> perUnitG(context, p.getMiddle(), p.getLeft()), + (p, g, context) -> p.getMiddle().setG(unPerUnitBG(context, p.getLeft(), g))) + .doubles("b", (p, context) -> perUnitB(context, p.getMiddle(), p.getLeft()), + (p, b, context) -> p.getMiddle().setB(unPerUnitBG(context, p.getLeft(), b))) .build(); } - static Triple getShuntSectionNonlinear(Network network, UpdatingDataframe dataframe, int index) { + static Triple getShuntSectionNonlinear(Network network, UpdatingDataframe dataframe, int index) { ShuntCompensator shuntCompensator = network.getShuntCompensator(dataframe.getStringValue("id", index) .orElseThrow(() -> new PowsyblException("id is missing"))); if (!(shuntCompensator.getModel() instanceof ShuntCompensatorNonLinearModel shuntNonLinear)) { @@ -392,7 +401,7 @@ static Triple getShuntS } else { int section = dataframe.getIntValue("section", index) .orElseThrow(() -> new PowsyblException("section is missing")); - return Triple.of(shuntCompensator.getId(), shuntNonLinear.getAllSections().get(section), section); + return Triple.of(shuntCompensator, shuntNonLinear.getAllSections().get(section), section); } } @@ -403,8 +412,10 @@ static NetworkDataframeMapper linearShuntsSections() { .map(shuntCompensator -> Pair.of(shuntCompensator, (ShuntCompensatorLinearModel) shuntCompensator.getModel())); return NetworkDataframeMapperBuilder.ofStream(linearShunts, (net, s) -> Pair.of(checkShuntNonNull(net, s), checkLinearModel(net, s))) .stringsIndex("id", p -> p.getLeft().getId()) - .doubles("g_per_section", p -> p.getRight().getGPerSection(), (p, g) -> p.getRight().setGPerSection(g)) - .doubles("b_per_section", p -> p.getRight().getBPerSection(), (p, b) -> p.getRight().setBPerSection(b)) + .doubles("g_per_section", (p, context) -> perUnitBG(context, p.getRight().getGPerSection(), p.getLeft().getTerminal().getVoltageLevel().getNominalV()), + (p, g, context) -> p.getRight().setGPerSection(unPerUnitBG(context, g, p.getLeft().getTerminal().getVoltageLevel().getNominalV()))) + .doubles("b_per_section", (p, context) -> perUnitBG(context, p.getRight().getBPerSection(), p.getLeft().getTerminal().getVoltageLevel().getNominalV()), + (p, b, context) -> p.getRight().setBPerSection(unPerUnitBG(context, b, p.getLeft().getTerminal().getVoltageLevel().getNominalV()))) .ints("max_section_count", p -> p.getLeft().getMaximumSectionCount(), (p, s) -> p.getRight().setMaximumSectionCount(s)) .build(); } @@ -429,18 +440,24 @@ static NetworkDataframeMapper lines() { return NetworkDataframeMapperBuilder.ofStream(Network::getLineStream, getOrThrow(Network::getLine, "Line")) .stringsIndex("id", Line::getId) .strings("name", l -> l.getOptionalName().orElse("")) - .doubles("r", Line::getR, Line::setR) - .doubles("x", Line::getX, Line::setX) - .doubles("g1", Line::getG1, Line::setG1) - .doubles("b1", Line::getB1, Line::setB1) - .doubles("g2", Line::getG2, Line::setG2) - .doubles("b2", Line::getB2, Line::setB2) - .doubles("p1", getP1(), setP1()) - .doubles("q1", getQ1(), setQ1()) - .doubles("i1", l -> l.getTerminal1().getI()) - .doubles("p2", getP2(), setP2()) - .doubles("q2", getQ2(), setQ2()) - .doubles("i2", l -> l.getTerminal2().getI()) + .doubles("r", (line, context) -> perUnitR(context, line), + (line, r, context) -> line.setR(unPerUnitRX(context, line, r))) + .doubles("x", (line, context) -> PerUnitUtil.perUnitX(context, line), + (line, x, context) -> line.setX(unPerUnitRX(context, line, x))) + .doubles("g1", (line, context) -> perUnitGSide1(context, line), + (line, g, context) -> line.setG1(unPerUnitGSide1(context, line, g))) + .doubles("b1", (line, context) -> perUnitBSide1(context, line), + (line, b, context) -> line.setB1(unPerUnitBSide1(context, line, b))) + .doubles("g2", (line, context) -> perUnitGSide2(context, line), + (line, g, context) -> line.setG2(unPerUnitGSide2(context, line, g))) + .doubles("b2", (line, context) -> perUnitBSide2(context, line), + (line, b, context) -> line.setB2(unPerUnitBSide2(context, line, b))) + .doubles("p1", getPerUnitP1(), setPerUnitP1()) + .doubles("q1", getPerUnitQ1(), setPerUnitQ1()) + .doubles("i1", (line, context) -> perUnitI(context, line.getTerminal1())) + .doubles("p2", getPerUnitP2(), setPerUnitP2()) + .doubles("q2", getPerUnitQ2(), setPerUnitQ2()) + .doubles("i2", (line, context) -> perUnitI(context, line.getTerminal2())) .strings("voltage_level1_id", l -> l.getTerminal1().getVoltageLevel().getId()) .strings("voltage_level2_id", l -> l.getTerminal2().getVoltageLevel().getId()) .strings("bus1_id", l -> getBusId(l.getTerminal1())) @@ -460,19 +477,21 @@ static NetworkDataframeMapper twoWindingTransformers() { return NetworkDataframeMapperBuilder.ofStream(Network::getTwoWindingsTransformerStream, getOrThrow(Network::getTwoWindingsTransformer, "Two windings transformer")) .stringsIndex("id", TwoWindingsTransformer::getId) .strings("name", twt -> twt.getOptionalName().orElse("")) - .doubles("r", TwoWindingsTransformer::getR, TwoWindingsTransformer::setR) - .doubles("x", TwoWindingsTransformer::getX, TwoWindingsTransformer::setX) - .doubles("g", TwoWindingsTransformer::getG, TwoWindingsTransformer::setG) - .doubles("b", TwoWindingsTransformer::getB, TwoWindingsTransformer::setB) - .doubles("rated_u1", TwoWindingsTransformer::getRatedU1, TwoWindingsTransformer::setRatedU1) - .doubles("rated_u2", TwoWindingsTransformer::getRatedU2, TwoWindingsTransformer::setRatedU2) - .doubles("rated_s", TwoWindingsTransformer::getRatedS, TwoWindingsTransformer::setRatedS) - .doubles("p1", getP1(), setP1()) - .doubles("q1", getQ1(), setQ1()) - .doubles("i1", twt -> twt.getTerminal1().getI()) - .doubles("p2", getP2(), setP2()) - .doubles("q2", getQ2(), setQ2()) - .doubles("i2", twt -> twt.getTerminal2().getI()) + .doubles("r", (twt, context) -> perUnitRX(context, twt.getR(), twt), (twt, r, context) -> twt.setR(unPerUnitRX(context, twt, r))) + .doubles("x", (twt, context) -> perUnitRX(context, twt.getX(), twt), (twt, x, context) -> twt.setX(unPerUnitRX(context, twt, x))) + .doubles("g", (twt, context) -> perUnitBG(context, twt, twt.getG()), (twt, g, context) -> twt.setG(unPerUnitBG(context, twt, g))) + .doubles("b", (twt, context) -> perUnitBG(context, twt, twt.getB()), (twt, b, context) -> twt.setB(unPerUnitBG(context, twt, b))) + .doubles("rated_u1", (twt, context) -> perUnitV(context, twt.getRatedU1(), twt.getTerminal1()), + (twt, ratedV1, context) -> twt.setRatedU1(unPerUnitV(context, ratedV1, twt.getTerminal1()))) + .doubles("rated_u2", (twt, context) -> perUnitV(context, twt.getRatedU2(), twt.getTerminal2()), + (twt, ratedV2, context) -> twt.setRatedU2(unPerUnitV(context, ratedV2, twt.getTerminal2()))) + .doubles("rated_s", (twt, context) -> twt.getRatedS(), (twt, ratedS, context) -> twt.setRatedS(ratedS)) + .doubles("p1", getPerUnitP1(), setPerUnitP1()) + .doubles("q1", getPerUnitQ1(), setPerUnitQ1()) + .doubles("i1", (twt, context) -> perUnitI(context, twt.getTerminal1())) + .doubles("p2", getPerUnitP2(), setPerUnitP2()) + .doubles("q2", getPerUnitQ2(), setPerUnitQ2()) + .doubles("i2", (twt, context) -> perUnitI(context, twt.getTerminal2())) .strings("voltage_level1_id", twt -> twt.getTerminal1().getVoltageLevel().getId()) .strings("voltage_level2_id", twt -> twt.getTerminal2().getVoltageLevel().getId()) .strings("bus1_id", twt -> getBusId(twt.getTerminal1())) @@ -492,50 +511,50 @@ static NetworkDataframeMapper threeWindingTransformers() { return NetworkDataframeMapperBuilder.ofStream(Network::getThreeWindingsTransformerStream, getOrThrow(Network::getThreeWindingsTransformer, "Three windings transformer")) .stringsIndex("id", ThreeWindingsTransformer::getId) .strings("name", twt -> twt.getOptionalName().orElse("")) - .doubles("rated_u0", ThreeWindingsTransformer::getRatedU0) - .doubles("r1", twt -> twt.getLeg1().getR(), (twt, v) -> twt.getLeg1().setR(v)) - .doubles("x1", twt -> twt.getLeg1().getX(), (twt, v) -> twt.getLeg1().setX(v)) - .doubles("g1", twt -> twt.getLeg1().getG(), (twt, v) -> twt.getLeg1().setG(v)) - .doubles("b1", twt -> twt.getLeg1().getB(), (twt, v) -> twt.getLeg1().setB(v)) - .doubles("rated_u1", twt -> twt.getLeg1().getRatedU(), (twt, v) -> twt.getLeg1().setRatedU(v)) - .doubles("rated_s1", twt -> twt.getLeg1().getRatedS(), (twt, v) -> twt.getLeg1().setRatedS(v)) + .doubles("rated_u0", (twt, context) -> context.isPerUnit() ? 1 : twt.getRatedU0()) + .doubles("r1", (twt, context) -> perUnitRX(context, twt.getLeg1().getR(), twt), (twt, r1, context) -> twt.getLeg1().setR(unPerUnitRX(context, twt, r1))) + .doubles("x1", (twt, context) -> perUnitRX(context, twt.getLeg1().getX(), twt), (twt, x1, context) -> twt.getLeg1().setX(unPerUnitRX(context, twt, x1))) + .doubles("g1", (twt, context) -> perUnitBG(context, twt.getLeg1().getG(), twt), (twt, g1, context) -> twt.getLeg1().setG(unPerUnitBG(context, twt, g1))) + .doubles("b1", (twt, context) -> perUnitBG(context, twt.getLeg1().getB(), twt), (twt, b1, context) -> twt.getLeg1().setB(unPerUnitBG(context, twt, b1))) + .doubles("rated_u1", (twt, context) -> perUnitV(context, twt.getLeg1()), (twt, ratedU1, context) -> twt.getLeg1().setRatedU(unPerUnitV(context, ratedU1, twt.getLeg1()))) + .doubles("rated_s1", (twt, context) -> twt.getLeg1().getRatedS(), (twt, ratedS1, context) -> twt.getLeg1().setRatedS(ratedS1)) .ints("ratio_tap_position1", getRatioTapPosition(ThreeWindingsTransformer::getLeg1), (t, v) -> setTapPosition(t.getLeg1().getRatioTapChanger(), v)) .ints("phase_tap_position1", getPhaseTapPosition(ThreeWindingsTransformer::getLeg1), (t, v) -> setTapPosition(t.getLeg1().getPhaseTapChanger(), v)) - .doubles("p1", twt -> twt.getLeg1().getTerminal().getP(), (twt, v) -> twt.getLeg1().getTerminal().setP(v)) - .doubles("q1", twt -> twt.getLeg1().getTerminal().getQ(), (twt, v) -> twt.getLeg1().getTerminal().setQ(v)) - .doubles("i1", twt -> twt.getLeg1().getTerminal().getI()) + .doubles("p1", (twt, context) -> perUnitP(context, twt.getLeg1()), (twt, p1, context) -> twt.getLeg1().getTerminal().setP(unPerUnitPQ(context, p1))) + .doubles("q1", (twt, context) -> perUnitQ(context, twt.getLeg1()), (twt, q1, context) -> twt.getLeg1().getTerminal().setQ(unPerUnitPQ(context, q1))) + .doubles("i1", (twt, context) -> perUnitI(context, twt.getLeg1().getTerminal())) .strings("voltage_level1_id", twt -> twt.getLeg1().getTerminal().getVoltageLevel().getId()) .strings("bus1_id", twt -> getBusId(twt.getLeg1().getTerminal())) .strings("bus_breaker_bus1_id", twt -> getBusBreakerViewBusId(twt.getLeg1().getTerminal()), false) .ints("node1", twt -> getNode(twt.getLeg1().getTerminal()), false) .booleans("connected1", g -> g.getLeg1().getTerminal().isConnected(), connectLeg1()) - .doubles("r2", twt -> twt.getLeg2().getR(), (twt, v) -> twt.getLeg2().setR(v)) - .doubles("x2", twt -> twt.getLeg2().getX(), (twt, v) -> twt.getLeg2().setX(v)) - .doubles("g2", twt -> twt.getLeg2().getG(), (twt, v) -> twt.getLeg2().setG(v)) - .doubles("b2", twt -> twt.getLeg2().getB(), (twt, v) -> twt.getLeg2().setB(v)) - .doubles("rated_u2", twt -> twt.getLeg2().getRatedU(), (twt, v) -> twt.getLeg2().setRatedU(v)) - .doubles("rated_s2", twt -> twt.getLeg2().getRatedS(), (twt, v) -> twt.getLeg2().setRatedS(v)) + .doubles("r2", (twt, context) -> perUnitRX(context, twt.getLeg2().getR(), twt), (twt, r2, context) -> twt.getLeg2().setR(unPerUnitRX(context, twt, r2))) + .doubles("x2", (twt, context) -> perUnitRX(context, twt.getLeg2().getX(), twt), (twt, x2, context) -> twt.getLeg2().setX(unPerUnitRX(context, twt, x2))) + .doubles("g2", (twt, context) -> perUnitBG(context, twt.getLeg2().getG(), twt), (twt, g2, context) -> twt.getLeg2().setG(unPerUnitBG(context, twt, g2))) + .doubles("b2", (twt, context) -> perUnitBG(context, twt.getLeg2().getB(), twt), (twt, b2, context) -> twt.getLeg2().setB(unPerUnitBG(context, twt, b2))) + .doubles("rated_u2", (twt, context) -> perUnitV(context, twt.getLeg2()), (twt, ratedU2, context) -> twt.getLeg2().setRatedU(unPerUnitV(context, ratedU2, twt.getLeg2()))) + .doubles("rated_s2", (twt, context) -> twt.getLeg2().getRatedS(), (twt, v, context) -> twt.getLeg2().setRatedS(v)) .ints("ratio_tap_position2", getRatioTapPosition(ThreeWindingsTransformer::getLeg2), (t, v) -> setTapPosition(t.getLeg2().getRatioTapChanger(), v)) .ints("phase_tap_position2", getPhaseTapPosition(ThreeWindingsTransformer::getLeg2), (t, v) -> setTapPosition(t.getLeg2().getPhaseTapChanger(), v)) - .doubles("p2", twt -> twt.getLeg2().getTerminal().getP(), (twt, v) -> twt.getLeg2().getTerminal().setP(v)) - .doubles("q2", twt -> twt.getLeg2().getTerminal().getQ(), (twt, v) -> twt.getLeg2().getTerminal().setQ(v)) - .doubles("i2", twt -> twt.getLeg2().getTerminal().getI()) + .doubles("p2", (twt, context) -> perUnitP(context, twt.getLeg2()), (twt, p2, context) -> twt.getLeg2().getTerminal().setP(unPerUnitPQ(context, p2))) + .doubles("q2", (twt, context) -> perUnitQ(context, twt.getLeg2()), (twt, q2, context) -> twt.getLeg2().getTerminal().setQ(unPerUnitPQ(context, q2))) + .doubles("i2", (twt, context) -> perUnitI(context, twt.getLeg2().getTerminal())) .strings("voltage_level2_id", twt -> twt.getLeg2().getTerminal().getVoltageLevel().getId()) .strings("bus2_id", twt -> getBusId(twt.getLeg2().getTerminal())) .strings("bus_breaker_bus2_id", twt -> getBusBreakerViewBusId(twt.getLeg2().getTerminal()), false) .ints("node2", twt -> getNode(twt.getLeg2().getTerminal()), false) .booleans("connected2", g -> g.getLeg2().getTerminal().isConnected(), connectLeg2()) - .doubles("r3", twt -> twt.getLeg3().getR(), (twt, v) -> twt.getLeg3().setR(v)) - .doubles("x3", twt -> twt.getLeg3().getX(), (twt, v) -> twt.getLeg3().setX(v)) - .doubles("g3", twt -> twt.getLeg3().getG(), (twt, v) -> twt.getLeg3().setG(v)) - .doubles("b3", twt -> twt.getLeg3().getB(), (twt, v) -> twt.getLeg3().setB(v)) - .doubles("rated_u3", twt -> twt.getLeg3().getRatedU(), (twt, v) -> twt.getLeg3().setRatedU(v)) - .doubles("rated_s3", twt -> twt.getLeg3().getRatedS(), (twt, v) -> twt.getLeg3().setRatedS(v)) + .doubles("r3", (twt, context) -> perUnitRX(context, twt.getLeg3().getR(), twt), (twt, r3, context) -> twt.getLeg3().setR(unPerUnitRX(context, twt, r3))) + .doubles("x3", (twt, context) -> perUnitRX(context, twt.getLeg3().getX(), twt), (twt, x3, context) -> twt.getLeg3().setX(unPerUnitRX(context, twt, x3))) + .doubles("g3", (twt, context) -> perUnitBG(context, twt.getLeg3().getG(), twt), (twt, g3, context) -> twt.getLeg3().setG(unPerUnitBG(context, twt, g3))) + .doubles("b3", (twt, context) -> perUnitBG(context, twt.getLeg3().getB(), twt), (twt, b3, context) -> twt.getLeg3().setB(unPerUnitBG(context, twt, b3))) + .doubles("rated_u3", (twt, context) -> perUnitV(context, twt.getLeg3()), (twt, ratedU3, context) -> twt.getLeg3().setRatedU(unPerUnitV(context, ratedU3, twt.getLeg3()))) + .doubles("rated_s3", (twt, context) -> twt.getLeg3().getRatedS(), (twt, v, context) -> twt.getLeg3().setRatedS(v)) .ints("ratio_tap_position3", getRatioTapPosition(ThreeWindingsTransformer::getLeg3), (t, v) -> setTapPosition(t.getLeg3().getRatioTapChanger(), v)) .ints("phase_tap_position3", getPhaseTapPosition(ThreeWindingsTransformer::getLeg3), (t, v) -> setTapPosition(t.getLeg3().getPhaseTapChanger(), v)) - .doubles("p3", twt -> twt.getLeg3().getTerminal().getP(), (twt, v) -> twt.getLeg3().getTerminal().setP(v)) - .doubles("q3", twt -> twt.getLeg3().getTerminal().getQ(), (twt, v) -> twt.getLeg3().getTerminal().setQ(v)) - .doubles("i3", twt -> twt.getLeg3().getTerminal().getI()) + .doubles("p3", (twt, context) -> perUnitP(context, twt.getLeg3()), (twt, p3, context) -> twt.getLeg3().getTerminal().setP(unPerUnitPQ(context, p3))) + .doubles("q3", (twt, context) -> perUnitQ(context, twt.getLeg3()), (twt, q3, context) -> twt.getLeg3().getTerminal().setQ(unPerUnitPQ(context, q3))) + .doubles("i3", (twt, context) -> perUnitI(context, twt.getLeg3().getTerminal())) .strings("voltage_level3_id", twt -> twt.getLeg3().getTerminal().getVoltageLevel().getId()) .strings("bus3_id", twt -> getBusId(twt.getLeg3().getTerminal())) .strings("bus_breaker_bus3_id", twt -> getBusBreakerViewBusId(twt.getLeg3().getTerminal()), false) @@ -550,15 +569,15 @@ static NetworkDataframeMapper danglingLines() { return NetworkDataframeMapperBuilder.ofStream(Network::getDanglingLineStream, getOrThrow(Network::getDanglingLine, "Dangling line")) .stringsIndex("id", DanglingLine::getId) .strings("name", dl -> dl.getOptionalName().orElse("")) - .doubles("r", DanglingLine::getR, DanglingLine::setR) - .doubles("x", DanglingLine::getX, DanglingLine::setX) - .doubles("g", DanglingLine::getG, DanglingLine::setG) - .doubles("b", DanglingLine::getB, DanglingLine::setB) - .doubles("p0", DanglingLine::getP0, DanglingLine::setP0) - .doubles("q0", DanglingLine::getQ0, DanglingLine::setQ0) - .doubles("p", getP(), setP()) - .doubles("q", getQ(), setQ()) - .doubles("i", dl -> dl.getTerminal().getI()) + .doubles("r", (dl, context) -> perUnitRX(context, dl.getR(), dl.getTerminal()), (dl, r, context) -> dl.setR(unPerUnitRX(context, dl.getTerminal(), r))) + .doubles("x", (dl, context) -> perUnitRX(context, dl.getX(), dl.getTerminal()), (dl, x, context) -> dl.setX(unPerUnitRX(context, dl.getTerminal(), x))) + .doubles("g", (dl, context) -> perUnitG(context, dl), (dl, g, context) -> dl.setG(unPerUnitG(context, dl, g))) + .doubles("b", (dl, context) -> perUnitB(context, dl), (dl, b, context) -> dl.setB(unPerUnitB(context, dl, b))) + .doubles("p0", (dl, context) -> perUnitPQ(context, dl.getP0()), (dl, p0, context) -> dl.setP0(unPerUnitPQ(context, p0))) + .doubles("q0", (dl, context) -> perUnitPQ(context, dl.getQ0()), (dl, q0, context) -> dl.setQ0(unPerUnitPQ(context, q0))) + .doubles("p", getPerUnitP(), setPerUnitP()) + .doubles("q", getPerUnitQ(), setPerUnitQ()) + .doubles("i", (dl, context) -> perUnitI(context, dl.getTerminal())) .strings("voltage_level_id", getVoltageLevelId()) .strings("bus_id", dl -> getBusId(dl.getTerminal())) .strings("bus_breaker_bus_id", busBreakerViewBusId(), false) @@ -589,11 +608,11 @@ static NetworkDataframeMapper lccs() { return NetworkDataframeMapperBuilder.ofStream(Network::getLccConverterStationStream, getOrThrow(Network::getLccConverterStation, "LCC converter station")) .stringsIndex("id", LccConverterStation::getId) .strings("name", st -> st.getOptionalName().orElse("")) - .doubles("power_factor", LccConverterStation::getPowerFactor, (lcc, v) -> lcc.setPowerFactor((float) v)) - .doubles("loss_factor", LccConverterStation::getLossFactor, (lcc, v) -> lcc.setLossFactor((float) v)) - .doubles("p", getP(), setP()) - .doubles("q", getQ(), setQ()) - .doubles("i", st -> st.getTerminal().getI()) + .doubles("power_factor", (st, context) -> st.getPowerFactor(), (lcc, v, context) -> lcc.setPowerFactor((float) v)) + .doubles("loss_factor", (st, context) -> st.getLossFactor(), (lcc, v, context) -> lcc.setLossFactor((float) v)) + .doubles("p", getPerUnitP(), setPerUnitP()) + .doubles("q", getPerUnitQ(), setPerUnitQ()) + .doubles("i", (st, context) -> perUnitI(context, st.getTerminal())) .strings("voltage_level_id", getVoltageLevelId()) .strings("bus_id", st -> getBusId(st.getTerminal())) .strings("bus_breaker_bus_id", busBreakerViewBusId(), false) @@ -608,20 +627,22 @@ static NetworkDataframeMapper vscs() { return NetworkDataframeMapperBuilder.ofStream(Network::getVscConverterStationStream, getOrThrow(Network::getVscConverterStation, "VSC converter station")) .stringsIndex("id", VscConverterStation::getId) .strings("name", st -> st.getOptionalName().orElse("")) - .doubles("loss_factor", VscConverterStation::getLossFactor, (vscConverterStation, lf) -> vscConverterStation.setLossFactor((float) lf)) - .doubles("min_q", ifExistsDouble(NetworkDataframes::getMinMaxReactiveLimits, MinMaxReactiveLimits::getMinQ), setMinQ()) - .doubles("max_q", ifExistsDouble(NetworkDataframes::getMinMaxReactiveLimits, MinMaxReactiveLimits::getMaxQ), setMaxQ()) - .doubles("min_q_at_p", getMinQ(getOppositeP()), false) - .doubles("max_q_at_p", getMaxQ(getOppositeP()), false) + .doubles("loss_factor", (vsc, context) -> vsc.getLossFactor(), (vscConverterStation, lf, context) -> vscConverterStation.setLossFactor((float) lf)) + .doubles("min_q", ifExistsDoublePerUnitPQ(NetworkDataframes::getMinMaxReactiveLimits, MinMaxReactiveLimits::getMinQ), setPerUnitMinQ()) + .doubles("max_q", ifExistsDoublePerUnitPQ(NetworkDataframes::getMinMaxReactiveLimits, MinMaxReactiveLimits::getMaxQ), setPerUnitMaxQ()) + .doubles("min_q_at_p", getPerUnitMinQ(getOppositeP()), false) + .doubles("max_q_at_p", getPerUnitMaxQ(getOppositeP()), false) .strings("reactive_limits_kind", NetworkDataframes::getReactiveLimitsKind) - .doubles("target_v", VscConverterStation::getVoltageSetpoint, VscConverterStation::setVoltageSetpoint) - .doubles("target_q", VscConverterStation::getReactivePowerSetpoint, VscConverterStation::setReactivePowerSetpoint) + .doubles("target_v", (vsc, context) -> perUnitV(context, vsc.getVoltageSetpoint(), vsc.getRegulatingTerminal()), + (vsc, targetV, context) -> vsc.setVoltageSetpoint(unPerUnitV(context, targetV, vsc.getRegulatingTerminal()))) + .doubles("target_q", (vsc, context) -> perUnitPQ(context, vsc.getReactivePowerSetpoint()), + (vsc, targetQ, context) -> vsc.setReactivePowerSetpoint(unPerUnitPQ(context, targetQ))) .booleans("voltage_regulator_on", VscConverterStation::isVoltageRegulatorOn, VscConverterStation::setVoltageRegulatorOn) .strings("regulated_element_id", vsc -> NetworkUtil.getRegulatedElementId(vsc::getRegulatingTerminal), (vsc, elementId) -> NetworkUtil.setRegulatingTerminal(vsc::setRegulatingTerminal, vsc.getNetwork(), elementId)) - .doubles("p", getP(), setP()) - .doubles("q", getQ(), setQ()) - .doubles("i", st -> st.getTerminal().getI()) + .doubles("p", getPerUnitP(), setPerUnitP()) + .doubles("q", getPerUnitQ(), setPerUnitQ()) + .doubles("i", (st, context) -> perUnitI(context, st.getTerminal())) .strings("voltage_level_id", getVoltageLevelId()) .strings("bus_id", st -> getBusId(st.getTerminal())) .strings("bus_breaker_bus_id", busBreakerViewBusId(), false) @@ -636,17 +657,19 @@ private static NetworkDataframeMapper svcs() { return NetworkDataframeMapperBuilder.ofStream(Network::getStaticVarCompensatorStream, getOrThrow(Network::getStaticVarCompensator, "Static var compensator")) .stringsIndex("id", StaticVarCompensator::getId) .strings("name", svc -> svc.getOptionalName().orElse("")) - .doubles("b_min", StaticVarCompensator::getBmin, StaticVarCompensator::setBmin) - .doubles("b_max", StaticVarCompensator::getBmax, StaticVarCompensator::setBmax) - .doubles("target_v", StaticVarCompensator::getVoltageSetpoint, StaticVarCompensator::setVoltageSetpoint) - .doubles("target_q", StaticVarCompensator::getReactivePowerSetpoint, StaticVarCompensator::setReactivePowerSetpoint) + .doubles("b_min", (svc, context) -> svc.getBmin(), (svc, bMin, context) -> svc.setBmin(bMin)) + .doubles("b_max", (svc, context) -> svc.getBmax(), (svc, bMax, context) -> svc.setBmax(bMax)) + .doubles("target_v", (svc, context) -> perUnitV(context, svc.getVoltageSetpoint(), svc.getRegulatingTerminal()), + (svc, targetV, context) -> svc.setVoltageSetpoint(unPerUnitV(context, targetV, svc.getRegulatingTerminal()))) + .doubles("target_q", (svc, context) -> perUnitPQ(context, svc.getReactivePowerSetpoint()), + (svc, targetQ, context) -> svc.setReactivePowerSetpoint(unPerUnitPQ(context, targetQ))) .enums("regulation_mode", StaticVarCompensator.RegulationMode.class, StaticVarCompensator::getRegulationMode, StaticVarCompensator::setRegulationMode) .strings("regulated_element_id", svc -> NetworkUtil.getRegulatedElementId(svc::getRegulatingTerminal), (svc, elementId) -> NetworkUtil.setRegulatingTerminal(svc::setRegulatingTerminal, svc.getNetwork(), elementId)) - .doubles("p", getP(), setP()) - .doubles("q", getQ(), setQ()) - .doubles("i", st -> st.getTerminal().getI()) + .doubles("p", getPerUnitP(), setPerUnitP()) + .doubles("q", getPerUnitQ(), setPerUnitQ()) + .doubles("i", (st, context) -> perUnitI(context, st.getTerminal())) .strings("voltage_level_id", getVoltageLevelId()) .strings("bus_id", svc -> getBusId(svc.getTerminal())) .strings("bus_breaker_bus_id", busBreakerViewBusId(), false) @@ -713,9 +736,11 @@ private static NetworkDataframeMapper voltageLevels() { .stringsIndex("id", VoltageLevel::getId) .strings("name", vl -> vl.getOptionalName().orElse("")) .strings("substation_id", vl -> vl.getSubstation().map(Identifiable::getId).orElse("")) - .doubles("nominal_v", VoltageLevel::getNominalV, VoltageLevel::setNominalV) - .doubles("high_voltage_limit", VoltageLevel::getHighVoltageLimit, VoltageLevel::setHighVoltageLimit) - .doubles("low_voltage_limit", VoltageLevel::getLowVoltageLimit, VoltageLevel::setLowVoltageLimit) + .doubles("nominal_v", (vl, context) -> vl.getNominalV(), (vl, nominalV, context) -> vl.setNominalV(nominalV)) + .doubles("high_voltage_limit", (vl, context) -> perUnitV(context, vl.getHighVoltageLimit(), vl.getNominalV()), + (vl, hvl, context) -> vl.setHighVoltageLimit(unPerUnitV(context, hvl, vl.getNominalV()))) + .doubles("low_voltage_limit", (vl, context) -> perUnitV(context, vl.getLowVoltageLimit(), vl.getNominalV()), + (vl, lvl, context) -> vl.setLowVoltageLimit(unPerUnitV(context, lvl, vl.getNominalV()))) .booleans("fictitious", Identifiable::isFictitious, Identifiable::setFictitious, false) .strings("topology_kind", vl -> vl.getTopologyKind().toString(), false) .addProperties() @@ -738,8 +763,8 @@ private static NetworkDataframeMapper busBars() { return NetworkDataframeMapperBuilder.ofStream(Network::getBusbarSectionStream, getOrThrow(Network::getBusbarSection, "Bus bar section")) .stringsIndex("id", BusbarSection::getId) .strings("name", bbs -> bbs.getOptionalName().orElse("")) - .doubles("v", BusbarSection::getV) - .doubles("angle", BusbarSection::getAngle) + .doubles("v", (busbar, context) -> perUnitV(context, busbar.getV(), busbar.getTerminal())) + .doubles("angle", (busbar, context) -> perUnitAngle(context, busbar.getAngle())) .strings("voltage_level_id", bbs -> bbs.getTerminal().getVoltageLevel().getId()) .strings("bus_id", bbs -> getBusId(bbs.getTerminal())) .booleans("connected", bbs -> bbs.getTerminal().isConnected(), connectInjection()) @@ -754,10 +779,12 @@ private static NetworkDataframeMapper hvdcs() { .stringsIndex("id", HvdcLine::getId) .strings("name", l -> l.getOptionalName().orElse("")) .enums("converters_mode", HvdcLine.ConvertersMode.class, HvdcLine::getConvertersMode, HvdcLine::setConvertersMode) - .doubles("target_p", HvdcLine::getActivePowerSetpoint, HvdcLine::setActivePowerSetpoint) - .doubles("max_p", HvdcLine::getMaxP, HvdcLine::setMaxP) - .doubles("nominal_v", HvdcLine::getNominalV, HvdcLine::setNominalV) - .doubles("r", HvdcLine::getR, HvdcLine::setR) + .doubles("target_p", (hvdc, context) -> perUnitPQ(context, hvdc.getActivePowerSetpoint()), + (hvdc, aps, context) -> hvdc.setActivePowerSetpoint(unPerUnitPQ(context, aps))) + .doubles("max_p", (hvdc, context) -> perUnitPQ(context, hvdc.getMaxP()), (hvdc, maxP, context) -> hvdc.setMaxP(unPerUnitPQ(context, maxP))) + .doubles("nominal_v", (hvdc, context) -> hvdc.getNominalV(), (hvdc, nominalV, context) -> hvdc.setNominalV(nominalV)) + .doubles("r", (hvdc, context) -> perUnitRX(context, hvdc.getR(), hvdc.getNominalV(), hvdc.getNominalV()), + (hvdc, r, context) -> hvdc.setR(unPerUnitRX(context, r, hvdc.getNominalV(), hvdc.getNominalV()))) .strings("converter_station1_id", l -> l.getConverterStation1().getId()) .strings("converter_station2_id", l -> l.getConverterStation2().getId()) .booleans("connected1", l -> l.getConverterStation1().getTerminal().isConnected(), connectHvdcStation1()) @@ -768,52 +795,66 @@ private static NetworkDataframeMapper hvdcs() { } private static NetworkDataframeMapper rtcSteps() { - Function>> ratioTapChangerSteps = network -> + Function>> ratioTapChangerSteps = network -> network.getTwoWindingsTransformerStream() .filter(twt -> twt.getRatioTapChanger() != null) - .flatMap(twt -> twt.getRatioTapChanger().getAllSteps().keySet().stream().map(position -> Triple.of(twt.getId(), twt.getRatioTapChanger(), position))); + .flatMap(twt -> twt.getRatioTapChanger().getAllSteps().keySet().stream().map(position -> Triple.of(twt, twt.getRatioTapChanger(), position))); return NetworkDataframeMapperBuilder.ofStream(ratioTapChangerSteps, NetworkDataframes::getRatioTapChangers) - .stringsIndex("id", Triple::getLeft) + .stringsIndex("id", triple -> triple.getLeft().getId()) .intsIndex("position", Triple::getRight) - .doubles("rho", p -> p.getMiddle().getStep(p.getRight()).getRho(), (p, rho) -> p.getMiddle().getStep(p.getRight()).setRho(rho)) - .doubles("r", p -> p.getMiddle().getStep(p.getRight()).getR(), (p, r) -> p.getMiddle().getStep(p.getRight()).setR(r)) - .doubles("x", p -> p.getMiddle().getStep(p.getRight()).getX(), (p, x) -> p.getMiddle().getStep(p.getRight()).setX(x)) - .doubles("g", p -> p.getMiddle().getStep(p.getRight()).getG(), (p, g) -> p.getMiddle().getStep(p.getRight()).setG(g)) - .doubles("b", p -> p.getMiddle().getStep(p.getRight()).getB(), (p, b) -> p.getMiddle().getStep(p.getRight()).setB(b)) + .doubles("rho", (p, context) -> perUnitRho(context, p.getLeft(), p.getMiddle().getStep(p.getRight()).getRho()), + (p, rho, context) -> p.getMiddle().getStep(p.getRight()).setRho(unPerUnitRho(context, p.getLeft(), rho))) + .doubles("r", (p, context) -> perUnitRX(context, p.getMiddle().getStep(p.getRight()).getR(), p.getLeft()), + (p, r, context) -> p.getMiddle().getStep(p.getRight()).setR(unPerUnitRX(context, p.getLeft(), r))) + .doubles("x", (p, context) -> perUnitRX(context, p.getMiddle().getStep(p.getRight()).getX(), p.getLeft()), + (p, x, context) -> p.getMiddle().getStep(p.getRight()).setX(unPerUnitRX(context, p.getLeft(), x))) + .doubles("g", (p, context) -> perUnitBG(context, p.getLeft(), p.getMiddle().getStep(p.getRight()).getG()), + (p, g, context) -> p.getMiddle().getStep(p.getRight()).setG(unPerUnitBG(context, p.getLeft(), g))) + .doubles("b", (p, context) -> perUnitBG(context, p.getLeft(), p.getMiddle().getStep(p.getRight()).getB()), + (p, b, context) -> p.getMiddle().getStep(p.getRight()).setB(unPerUnitBG(context, p.getLeft(), b))) .build(); } - static Triple getRatioTapChangers(Network network, UpdatingDataframe dataframe, int index) { + static Triple getRatioTapChangers(Network network, UpdatingDataframe dataframe, int index) { String id = dataframe.getStringValue("id", index) .orElseThrow(() -> new IllegalArgumentException("id column is missing")); int position = dataframe.getIntValue("position", index) .orElseThrow(() -> new IllegalArgumentException("position column is missing")); - return Triple.of(id, network.getTwoWindingsTransformer(id).getRatioTapChanger(), position); + TwoWindingsTransformer twt = network.getTwoWindingsTransformer(id); + return Triple.of(twt, twt.getRatioTapChanger(), position); } private static NetworkDataframeMapper ptcSteps() { - Function>> phaseTapChangerSteps = network -> + Function>> phaseTapChangerSteps = network -> network.getTwoWindingsTransformerStream() .filter(twt -> twt.getPhaseTapChanger() != null) - .flatMap(twt -> twt.getPhaseTapChanger().getAllSteps().keySet().stream().map(position -> Triple.of(twt.getId(), twt.getPhaseTapChanger(), position))); + .flatMap(twt -> twt.getPhaseTapChanger().getAllSteps().keySet() + .stream().map(position -> Triple.of(twt, twt.getPhaseTapChanger(), position))); return NetworkDataframeMapperBuilder.ofStream(phaseTapChangerSteps, NetworkDataframes::getPhaseTapChangers) - .stringsIndex("id", Triple::getLeft) + .stringsIndex("id", triple -> triple.getLeft().getId()) .intsIndex("position", Triple::getRight) - .doubles("rho", p -> p.getMiddle().getStep(p.getRight()).getRho(), (p, rho) -> p.getMiddle().getStep(p.getRight()).setRho(rho)) - .doubles("alpha", p -> p.getMiddle().getStep(p.getRight()).getAlpha(), (p, alpha) -> p.getMiddle().getStep(p.getRight()).setAlpha(alpha)) - .doubles("r", p -> p.getMiddle().getStep(p.getRight()).getR(), (p, r) -> p.getMiddle().getStep(p.getRight()).setR(r)) - .doubles("x", p -> p.getMiddle().getStep(p.getRight()).getX(), (p, x) -> p.getMiddle().getStep(p.getRight()).setX(x)) - .doubles("g", p -> p.getMiddle().getStep(p.getRight()).getG(), (p, g) -> p.getMiddle().getStep(p.getRight()).setG(g)) - .doubles("b", p -> p.getMiddle().getStep(p.getRight()).getB(), (p, b) -> p.getMiddle().getStep(p.getRight()).setB(b)) + .doubles("rho", (p, context) -> perUnitRho(context, p.getLeft(), p.getMiddle().getStep(p.getRight()).getRho()), + (p, rho, context) -> p.getMiddle().getStep(p.getRight()).setRho(unPerUnitRho(context, p.getLeft(), rho))) + .doubles("alpha", (p, context) -> perUnitAngle(context, p.getMiddle().getStep(p.getRight()).getAlpha()), + (p, alpha, context) -> p.getMiddle().getStep(p.getRight()).setAlpha(unPerUnitAngle(context, alpha))) + .doubles("r", (p, context) -> perUnitRX(context, p.getMiddle().getStep(p.getRight()).getR(), p.getLeft()), + (p, r, context) -> p.getMiddle().getStep(p.getRight()).setR(unPerUnitRX(context, p.getLeft(), r))) + .doubles("x", (p, context) -> perUnitRX(context, p.getMiddle().getStep(p.getRight()).getX(), p.getLeft()), + (p, x, context) -> p.getMiddle().getStep(p.getRight()).setX(unPerUnitRX(context, p.getLeft(), x))) + .doubles("g", (p, context) -> perUnitBG(context, p.getLeft(), p.getMiddle().getStep(p.getRight()).getG()), + (p, g, context) -> p.getMiddle().getStep(p.getRight()).setG(unPerUnitBG(context, p.getLeft(), g))) + .doubles("b", (p, context) -> perUnitBG(context, p.getLeft(), p.getMiddle().getStep(p.getRight()).getB()), + (p, b, context) -> p.getMiddle().getStep(p.getRight()).setB(unPerUnitBG(context, p.getLeft(), b))) .build(); } - static Triple getPhaseTapChangers(Network network, UpdatingDataframe dataframe, int index) { + static Triple getPhaseTapChangers(Network network, UpdatingDataframe dataframe, int index) { String id = dataframe.getStringValue("id", index) .orElseThrow(() -> new IllegalArgumentException("id column is missing")); int position = dataframe.getIntValue("position", index) .orElseThrow(() -> new IllegalArgumentException("position column is missing")); - return Triple.of(id, network.getTwoWindingsTransformer(id).getPhaseTapChanger(), position); + TwoWindingsTransformer twoWindingsTransformer = network.getTwoWindingsTransformer(id); + return Triple.of(twoWindingsTransformer, twoWindingsTransformer.getPhaseTapChanger(), position); } private static NetworkDataframeMapper rtcs() { @@ -826,11 +867,13 @@ private static NetworkDataframeMapper rtcs() { .ints("step_count", t -> t.getRatioTapChanger().getStepCount()) .booleans("on_load", t -> t.getRatioTapChanger().hasLoadTapChangingCapabilities(), (t, v) -> t.getRatioTapChanger().setLoadTapChangingCapabilities(v)) .booleans("regulating", t -> t.getRatioTapChanger().isRegulating(), (t, v) -> t.getRatioTapChanger().setRegulating(v)) - .doubles("target_v", t -> t.getRatioTapChanger().getTargetV(), (t, v) -> t.getRatioTapChanger().setTargetV(v)) - .doubles("target_deadband", t -> t.getRatioTapChanger().getTargetDeadband(), (t, v) -> t.getRatioTapChanger().setTargetDeadband(v)) + .doubles("target_v", (t, context) -> perUnitV(context, t.getRatioTapChanger().getTargetV(), t.getRatioTapChanger().getRegulationTerminal()), + (t, v, context) -> t.getRatioTapChanger().setTargetV(unPerUnitV(context, v, t.getRatioTapChanger().getRegulationTerminal()))) + .doubles("target_deadband", (t, context) -> t.getRatioTapChanger().getTargetDeadband(), + (t, v, context) -> t.getRatioTapChanger().setTargetDeadband(v)) .strings("regulating_bus_id", t -> getBusId(t.getRatioTapChanger().getRegulationTerminal())) - .doubles("rho", NetworkDataframes::computeRho) - .doubles("alpha", ifExistsDouble(TwoWindingsTransformer::getPhaseTapChanger, pc -> pc.getCurrentStep().getAlpha())) + .doubles("rho", (twt, context) -> perUnitRho(context, twt, NetworkDataframes.computeRho(twt))) + .doubles("alpha", ifExistsDoublePerUnitAngle(TwoWindingsTransformer::getPhaseTapChanger, pc -> pc.getCurrentStep().getAlpha())) .booleans("fictitious", Identifiable::isFictitious, Identifiable::setFictitious, false) .strings("regulated_side", NetworkDataframes::getRatioTapChangerRegulatedSide, NetworkDataframes::setRatioTapChangerRegulatedSide, false) .build(); @@ -860,8 +903,10 @@ private static NetworkDataframeMapper ptcs() { .ints("step_count", t -> t.getPhaseTapChanger().getStepCount()) .booleans("regulating", t -> t.getPhaseTapChanger().isRegulating(), (t, v) -> t.getPhaseTapChanger().setRegulating(v)) .enums("regulation_mode", PhaseTapChanger.RegulationMode.class, t -> t.getPhaseTapChanger().getRegulationMode(), (t, v) -> t.getPhaseTapChanger().setRegulationMode(v)) - .doubles("regulation_value", t -> t.getPhaseTapChanger().getRegulationValue(), (t, v) -> t.getPhaseTapChanger().setRegulationValue(v)) - .doubles("target_deadband", t -> t.getPhaseTapChanger().getTargetDeadband(), (t, v) -> t.getPhaseTapChanger().setTargetDeadband(v)) + .doubles("regulation_value", (t, context) -> t.getPhaseTapChanger().getRegulationValue(), + (t, v, context) -> t.getPhaseTapChanger().setRegulationValue(v)) + .doubles("target_deadband", (t, context) -> t.getPhaseTapChanger().getTargetDeadband(), + (t, v, context) -> t.getPhaseTapChanger().setTargetDeadband(v)) .strings("regulating_bus_id", t -> getBusId(t.getPhaseTapChanger().getRegulationTerminal())) .strings("regulated_side", NetworkDataframes::getPhaseTapChangerRegulatedSide, NetworkDataframes::setPhaseTapChangerRegulatedSide, false) .booleans("fictitious", Identifiable::isFictitious, Identifiable::setFictitious, false) @@ -1042,9 +1087,9 @@ private static NetworkDataframeMapper reactiveCapabilityCurves() { return NetworkDataframeMapperBuilder.ofStream(NetworkDataframes::streamPoints) .stringsIndex("id", Triple::getLeft) .intsIndex("num", Triple::getRight) - .doubles("p", t -> t.getMiddle().getP()) - .doubles("min_q", t -> t.getMiddle().getMinQ()) - .doubles("max_q", t -> t.getMiddle().getMaxQ()) + .doubles("p", (t, context) -> perUnitPQ(context, t.getMiddle().getP())) + .doubles("min_q", (t, context) -> perUnitPQ(context, t.getMiddle().getMinQ())) + .doubles("max_q", (t, context) -> perUnitPQ(context, t.getMiddle().getMaxQ())) .build(); } diff --git a/java/src/main/java/com/powsybl/dataframe/network/PerUnitUtil.java b/java/src/main/java/com/powsybl/dataframe/network/PerUnitUtil.java new file mode 100644 index 0000000000..fad41c94fa --- /dev/null +++ b/java/src/main/java/com/powsybl/dataframe/network/PerUnitUtil.java @@ -0,0 +1,268 @@ +/** + * Copyright (c) 2024, 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.dataframe.network; + +import com.powsybl.commons.PowsyblException; +import com.powsybl.iidm.network.*; +import org.apache.commons.math3.complex.Complex; + +import static java.lang.Math.*; + +/** + * @author Etienne Lesot {@literal } + */ +public final class PerUnitUtil { + + private PerUnitUtil() { + + } + + public static double perUnitP(DataframeContext dataframeContext, ThreeWindingsTransformer.Leg leg) { + return perUnitPQ(dataframeContext, leg.getTerminal().getP()); + } + + public static double perUnitQ(DataframeContext dataframeContext, ThreeWindingsTransformer.Leg leg) { + return perUnitPQ(dataframeContext, leg.getTerminal().getQ()); + } + + public static double perUnitPQ(DataframeContext dataframeContext, double p) { + return dataframeContext.isPerUnit() ? p / dataframeContext.getNominalApparentPower() : p; + } + + public static double unPerUnitPQ(DataframeContext dataframeContext, double p) { + return dataframeContext.isPerUnit() ? p * dataframeContext.getNominalApparentPower() : p; + } + + public static double perUnitI(DataframeContext dataframeContext, Terminal terminal) { + return perUnitI(dataframeContext, terminal.getI(), terminal.getVoltageLevel().getNominalV()); + } + + public static double perUnitI(DataframeContext dataframeContext, double i, double nominalV) { + return dataframeContext.isPerUnit() ? ((sqrt(3) * nominalV) / (dataframeContext.getNominalApparentPower() * pow(10, 3))) * i : i; + } + + public static double perUnitGSide1(DataframeContext dataframeContext, Line line) { + return perUnitG(dataframeContext, line.getG1(), line.getR(), line.getX(), + line.getTerminal1().getVoltageLevel().getNominalV(), line.getTerminal2().getVoltageLevel().getNominalV()); + } + + public static double perUnitGSide2(DataframeContext dataframeContext, Line line) { + return perUnitG(dataframeContext, line.getG2(), line.getR(), line.getX(), + line.getTerminal2().getVoltageLevel().getNominalV(), line.getTerminal1().getVoltageLevel().getNominalV()); + } + + public static double perUnitG(DataframeContext dataframeContext, DanglingLine dl) { + return perUnitG(dataframeContext, dl.getG(), dl.getR(), dl.getX(), + dl.getTerminal().getVoltageLevel().getNominalV(), dl.getTerminal().getVoltageLevel().getNominalV()); + } + + public static double perUnitG(DataframeContext dataframeContext, ShuntCompensator shuntCompensator) { + return perUnitBG(dataframeContext, shuntCompensator.getG(), shuntCompensator.getTerminal().getVoltageLevel().getNominalV()); + } + + public static double perUnitG(DataframeContext dataframeContext, ShuntCompensatorNonLinearModel.Section section, ShuntCompensator shuntCompensator) { + return perUnitBG(dataframeContext, section.getG(), shuntCompensator.getTerminal().getVoltageLevel().getNominalV()); + } + + public static double perUnitBG(DataframeContext dataframeContext, double bg, ThreeWindingsTransformer twt) { + return perUnitBG(dataframeContext, bg, twt.getRatedU0()); + } + + public static double perUnitBG(DataframeContext dataframeContext, double bg, double nominalV) { + return dataframeContext.isPerUnit() ? bg * pow(nominalV, 2) / dataframeContext.getNominalApparentPower() : bg; + } + + public static double perUnitG(DataframeContext dataframeContext, double g, double r, double x, double nominalV1, double nominalV2) { + return dataframeContext.isPerUnit() ? (g * pow(nominalV1, 2) + (nominalV1 - nominalV2) * nominalV1 * computeY(r, x).getReal()) / dataframeContext.getNominalApparentPower() : g; + } + + public static double unPerUnitGSide1(DataframeContext dataframeContext, Line line, double g) { + return unPerUnitG(dataframeContext, g, line.getR(), line.getX(), + line.getTerminal1().getVoltageLevel().getNominalV(), line.getTerminal2().getVoltageLevel().getNominalV()); + } + + public static double unPerUnitGSide2(DataframeContext dataframeContext, Line line, double g) { + return unPerUnitG(dataframeContext, g, line.getR(), line.getX(), + line.getTerminal2().getVoltageLevel().getNominalV(), line.getTerminal1().getVoltageLevel().getNominalV()); + } + + public static double unPerUnitBG(DataframeContext dataframeContext, TwoWindingsTransformer twt, double g) { + return unPerUnitBG(dataframeContext, g, twt.getTerminal2().getVoltageLevel().getNominalV()); + } + + public static double unPerUnitG(DataframeContext dataframeContext, DanglingLine danglingLine, double g) { + return unPerUnitG(dataframeContext, g, danglingLine.getR(), danglingLine.getX(), + danglingLine.getTerminal().getVoltageLevel().getNominalV(), danglingLine.getTerminal().getVoltageLevel().getNominalV()); + } + + public static double unPerUnitBG(DataframeContext dataframeContext, ShuntCompensator shuntCompensator, double bg) { + return unPerUnitBG(dataframeContext, bg, shuntCompensator.getTerminal().getVoltageLevel().getNominalV()); + } + + public static double unPerUnitBG(DataframeContext dataframeContext, ThreeWindingsTransformer twt, double bg) { + return unPerUnitBG(dataframeContext, bg, twt.getRatedU0()); + } + + public static double unPerUnitBG(DataframeContext dataframeContext, double bg, double nominalV) { + return dataframeContext.isPerUnit() ? bg * dataframeContext.getNominalApparentPower() / pow(nominalV, 2) : bg; + } + + public static double unPerUnitG(DataframeContext dataframeContext, double g, double r, double x, double nominalV1, double nominalV2) { + return dataframeContext.isPerUnit() ? (g * dataframeContext.getNominalApparentPower() - (nominalV1 - nominalV2) * nominalV1 * computeY(r, x).getReal()) / pow(nominalV1, 2) : g; + } + + public static double perUnitBSide1(DataframeContext dataframeContext, Line line) { + return perUnitB(dataframeContext, line.getB1(), line.getR(), line.getX(), + line.getTerminal1().getVoltageLevel().getNominalV(), line.getTerminal2().getVoltageLevel().getNominalV()); + } + + public static double perUnitBSide2(DataframeContext dataframeContext, Line line) { + return perUnitB(dataframeContext, line.getB2(), line.getR(), line.getX(), + line.getTerminal2().getVoltageLevel().getNominalV(), line.getTerminal1().getVoltageLevel().getNominalV()); + } + + public static double perUnitBG(DataframeContext dataframeContext, TwoWindingsTransformer twt, double bg) { + return perUnitBG(dataframeContext, bg, twt.getTerminal2().getVoltageLevel().getNominalV()); + } + + public static double perUnitB(DataframeContext dataframeContext, DanglingLine dl) { + return perUnitBG(dataframeContext, dl.getB(), dl.getTerminal().getVoltageLevel().getNominalV()); + } + + public static double perUnitB(DataframeContext dataframeContext, ShuntCompensatorNonLinearModel.Section section, ShuntCompensator shuntCompensator) { + return perUnitBG(dataframeContext, section.getB(), shuntCompensator.getTerminal().getVoltageLevel().getNominalV()); + } + + public static double perUnitB(DataframeContext dataframeContext, ShuntCompensator shuntCompensator) { + return perUnitBG(dataframeContext, shuntCompensator.getB(), shuntCompensator.getTerminal().getVoltageLevel().getNominalV()); + } + + public static double perUnitB(DataframeContext dataframeContext, double b, double r, double x, double nominalV1, double nominalV2) { + return dataframeContext.isPerUnit() ? (b * pow(nominalV1, 2) + (nominalV1 - nominalV2) * nominalV1 * computeY(r, x).getImaginary()) / dataframeContext.getNominalApparentPower() : b; + } + + public static double unPerUnitBSide2(DataframeContext dataframeContext, Line line, double b) { + return unPerUnitB(dataframeContext, b, line.getR(), line.getX(), + line.getTerminal2().getVoltageLevel().getNominalV(), line.getTerminal1().getVoltageLevel().getNominalV()); + } + + public static double unPerUnitBSide1(DataframeContext dataframeContext, Line line, double b) { + return unPerUnitB(dataframeContext, b, line.getR(), line.getX(), + line.getTerminal1().getVoltageLevel().getNominalV(), line.getTerminal2().getVoltageLevel().getNominalV()); + } + + public static double unPerUnitB(DataframeContext dataframeContext, DanglingLine danglingLine, double b) { + return unPerUnitBG(dataframeContext, b, danglingLine.getTerminal().getVoltageLevel().getNominalV()); + } + + public static double unPerUnitB(DataframeContext dataframeContext, double b, double r, double x, double nominalV1, double nominalV2) { + return dataframeContext.isPerUnit() ? (b * dataframeContext.getNominalApparentPower() - (nominalV1 - nominalV2) * nominalV1 * computeY(r, x).getImaginary()) / pow(nominalV1, 2) : b; + } + + public static double perUnitR(DataframeContext dataframeContext, Line line) { + return perUnitRX(dataframeContext, line.getR(), line.getTerminal1().getVoltageLevel().getNominalV(), line.getTerminal2().getVoltageLevel().getNominalV()); + } + + public static double perUnitRX(DataframeContext dataframeContext, double rx, Terminal terminal) { + return perUnitRX(dataframeContext, rx, terminal.getVoltageLevel().getNominalV(), terminal.getVoltageLevel().getNominalV()); + } + + public static double perUnitX(DataframeContext dataframeContext, Line line) { + return perUnitRX(dataframeContext, line.getX(), line.getTerminal1().getVoltageLevel().getNominalV(), line.getTerminal2().getVoltageLevel().getNominalV()); + } + + public static double unPerUnitRX(DataframeContext dataframeContext, Line line, double r) { + return unPerUnitRX(dataframeContext, r, line.getTerminal1().getVoltageLevel().getNominalV(), line.getTerminal2().getVoltageLevel().getNominalV()); + } + + public static double unPerUnitRX(DataframeContext dataframeContext, Terminal terminal, double r) { + return unPerUnitRX(dataframeContext, r, terminal.getVoltageLevel().getNominalV(), terminal.getVoltageLevel().getNominalV()); + } + + public static double unPerUnitRX(DataframeContext dataframeContext, TwoWindingsTransformer twt, double rx) { + return unPerUnitRX(dataframeContext, rx, twt.getTerminal2().getVoltageLevel().getNominalV(), twt.getTerminal2().getVoltageLevel().getNominalV()); + } + + public static double unPerUnitRX(DataframeContext dataframeContext, ThreeWindingsTransformer twt, double rx) { + return unPerUnitRX(dataframeContext, rx, twt.getRatedU0(), twt.getRatedU0()); + } + + public static double unPerUnitRX(DataframeContext dataframeContext, double r, double nominalV1, double nominalV2) { + return dataframeContext.isPerUnit() ? (nominalV1 * nominalV2) / dataframeContext.getNominalApparentPower() * r : r; + } + + public static double perUnitRX(DataframeContext dataframeContext, double rx, TwoWindingsTransformer twt) { + return perUnitRX(dataframeContext, rx, twt.getTerminal2().getVoltageLevel().getNominalV(), twt.getTerminal2().getVoltageLevel().getNominalV()); + } + + public static double perUnitRX(DataframeContext dataframeContext, double rx, ThreeWindingsTransformer twt) { + return perUnitRX(dataframeContext, rx, twt.getRatedU0(), twt.getRatedU0()); + } + + public static double perUnitRX(DataframeContext dataframeContext, double rx, double nominalV1, double nominalV2) { + return dataframeContext.isPerUnit() ? dataframeContext.getNominalApparentPower() / (nominalV1 * nominalV2) * rx : rx; + } + + public static double unPerUnitV(DataframeContext dataframeContext, double v, Bus bus) { + return unPerUnitV(dataframeContext, v, bus.getVoltageLevel().getNominalV()); + } + + public static double unPerUnitV(DataframeContext dataframeContext, double v, Terminal terminal) { + if (terminal == null) { + throw new PowsyblException("terminal not found for un per unit V"); + } + return unPerUnitV(dataframeContext, v, terminal.getVoltageLevel().getNominalV()); + } + + public static double unPerUnitV(DataframeContext dataframeContext, double v, ThreeWindingsTransformer.Leg leg) { + return unPerUnitV(dataframeContext, v, leg.getTerminal()); + } + + public static double unPerUnitV(DataframeContext dataframeContext, double v, double nominalV) { + return dataframeContext.isPerUnit() ? v * nominalV : v; + } + + public static double perUnitV(DataframeContext dataframeContext, ThreeWindingsTransformer.Leg leg) { + return perUnitV(dataframeContext, leg.getRatedU(), leg.getTerminal()); + } + + public static double perUnitV(DataframeContext dataframeContext, double v, Terminal terminal) { + if (terminal == null) { + throw new PowsyblException("terminal not found for per unit V"); + } + return perUnitV(dataframeContext, v, terminal.getVoltageLevel().getNominalV()); + } + + public static double perUnitV(DataframeContext dataframeContext, double v, Bus bus) { + return perUnitV(dataframeContext, v, bus.getVoltageLevel().getNominalV()); + } + + public static double perUnitV(DataframeContext dataframeContext, double v, double nominalV) { + return dataframeContext.isPerUnit() ? v / nominalV : v; + } + + public static double perUnitRho(DataframeContext dataframeContext, TwoWindingsTransformer twt, double rho) { + return dataframeContext.isPerUnit() ? rho * twt.getTerminal1().getVoltageLevel().getNominalV() / twt.getTerminal2().getVoltageLevel().getNominalV() : rho; + } + + public static double unPerUnitRho(DataframeContext dataframeContext, TwoWindingsTransformer twt, double rho) { + return dataframeContext.isPerUnit() ? rho * twt.getTerminal2().getVoltageLevel().getNominalV() / twt.getTerminal1().getVoltageLevel().getNominalV() : rho; + } + + public static double perUnitAngle(DataframeContext dataframeContext, double angle) { + return dataframeContext.isPerUnit() ? toRadians(angle) : angle; + } + + public static double unPerUnitAngle(DataframeContext dataframeContext, double angle) { + return dataframeContext.isPerUnit() ? toDegrees(angle) : angle; + } + + private static Complex computeY(double r, double x) { + return Complex.valueOf(r, x).reciprocal(); + } +} diff --git a/java/src/main/java/com/powsybl/dataframe/network/extensions/ActivePowerControlDataframeProvider.java b/java/src/main/java/com/powsybl/dataframe/network/extensions/ActivePowerControlDataframeProvider.java index 285b4b9286..433de19505 100644 --- a/java/src/main/java/com/powsybl/dataframe/network/extensions/ActivePowerControlDataframeProvider.java +++ b/java/src/main/java/com/powsybl/dataframe/network/extensions/ActivePowerControlDataframeProvider.java @@ -61,7 +61,7 @@ private ActivePowerControl getOrThrow(Network network, String id) { public NetworkDataframeMapper createMapper() { return NetworkDataframeMapperBuilder.ofStream(this::itemsStream, this::getOrThrow) .stringsIndex("id", ext -> ((Identifiable) ext.getExtendable()).getId()) - .doubles("droop", ActivePowerControl::getDroop, (c, d) -> c.setDroop((float) d)) + .doubles("droop", (apc, context) -> apc.getDroop(), (apc, droop, context) -> apc.setDroop((float) droop)) .booleans("participate", ActivePowerControl::isParticipate, ActivePowerControl::setParticipate) .build(); } diff --git a/java/src/main/java/com/powsybl/dataframe/network/extensions/BranchObservabilityDataframeProvider.java b/java/src/main/java/com/powsybl/dataframe/network/extensions/BranchObservabilityDataframeProvider.java index 52e86d9e45..b33c3ee45a 100644 --- a/java/src/main/java/com/powsybl/dataframe/network/extensions/BranchObservabilityDataframeProvider.java +++ b/java/src/main/java/com/powsybl/dataframe/network/extensions/BranchObservabilityDataframeProvider.java @@ -58,31 +58,31 @@ public NetworkDataframeMapper createMapper() { return NetworkDataframeMapperBuilder.ofStream(this::itemsStream, this::getOrThrow) .stringsIndex("id", branchObservability -> ((Branch) branchObservability.getExtendable()).getId()) .booleans("observable", BranchObservability::isObservable) - .doubles("p1_standard_deviation", branchObservability -> branchObservability.getQualityP1() != null ? + .doubles("p1_standard_deviation", (branchObservability, context) -> branchObservability.getQualityP1() != null ? branchObservability.getQualityP1().getStandardDeviation() : Double.NaN, - (branchObservability, standardDeviation) -> branchObservability.getQualityP1().setStandardDeviation(standardDeviation)) + (branchObservability, standardDeviation, context) -> branchObservability.getQualityP1().setStandardDeviation(standardDeviation)) .booleans("p1_redundant", branchObservability -> branchObservability.getQualityP1() != null && (boolean) branchObservability.getQualityP1().isRedundant().orElse(false), (branchObservability, redundant) -> branchObservability.getQualityP1().setRedundant(redundant)) .booleans("p1_redundant_null", branchObservability -> branchObservability.getQualityP1() == null || branchObservability.getQualityP1().isRedundant().isEmpty()) - .doubles("p2_standard_deviation", branchObservability -> branchObservability.getQualityP2() != null ? branchObservability.getQualityP2().getStandardDeviation() : Double.NaN, - (branchObservability, standardDeviation) -> branchObservability.getQualityP2().setStandardDeviation(standardDeviation)) + .doubles("p2_standard_deviation", (branchObservability, context) -> branchObservability.getQualityP2() != null ? branchObservability.getQualityP2().getStandardDeviation() : Double.NaN, + (branchObservability, standardDeviation, context) -> branchObservability.getQualityP2().setStandardDeviation(standardDeviation)) .booleans("p2_redundant", branchObservability -> branchObservability.getQualityP2() != null && (boolean) branchObservability.getQualityP2().isRedundant().orElse(false), (branchObservability, redundant) -> branchObservability.getQualityP2().setRedundant(redundant)) .booleans("p2_redundant_null", branchObservability -> branchObservability.getQualityP2() == null || branchObservability.getQualityP2().isRedundant().isEmpty()) - .doubles("q1_standard_deviation", branchObservability -> branchObservability.getQualityQ1() != null ? + .doubles("q1_standard_deviation", (branchObservability, context) -> branchObservability.getQualityQ1() != null ? branchObservability.getQualityQ1().getStandardDeviation() : Double.NaN, - (branchObservability, standardDeviation) -> branchObservability.getQualityQ1().setStandardDeviation(standardDeviation)) + (branchObservability, standardDeviation, context) -> branchObservability.getQualityQ1().setStandardDeviation(standardDeviation)) .booleans("q1_redundant", branchObservability -> branchObservability.getQualityQ1() != null && (boolean) branchObservability.getQualityQ1().isRedundant().orElse(false), (branchObservability, redundant) -> branchObservability.getQualityQ1().setRedundant(redundant)) .booleans("q1_redundant_null", branchObservability -> branchObservability.getQualityQ1() == null || branchObservability.getQualityQ1().isRedundant().isEmpty()) - .doubles("q2_standard_deviation", branchObservability -> branchObservability.getQualityQ2() != null ? + .doubles("q2_standard_deviation", (branchObservability, context) -> branchObservability.getQualityQ2() != null ? branchObservability.getQualityQ2().getStandardDeviation() : Double.NaN, - (branchObservability, standardDeviation) -> branchObservability.getQualityQ2().setStandardDeviation(standardDeviation)) + (branchObservability, standardDeviation, context) -> branchObservability.getQualityQ2().setStandardDeviation(standardDeviation)) .booleans("q2_redundant", branchObservability -> branchObservability.getQualityQ2() != null && (boolean) branchObservability.getQualityQ2().isRedundant().orElse(false), (branchObservability, redundant) -> branchObservability.getQualityQ2().setRedundant(redundant)) diff --git a/java/src/main/java/com/powsybl/dataframe/network/extensions/CoordinatedReactiveControlDataframeProvider.java b/java/src/main/java/com/powsybl/dataframe/network/extensions/CoordinatedReactiveControlDataframeProvider.java index 294e1a3af4..da78bc608d 100644 --- a/java/src/main/java/com/powsybl/dataframe/network/extensions/CoordinatedReactiveControlDataframeProvider.java +++ b/java/src/main/java/com/powsybl/dataframe/network/extensions/CoordinatedReactiveControlDataframeProvider.java @@ -56,7 +56,7 @@ private CoordinatedReactiveControl getOrThrow(Network network, String id) { public NetworkDataframeMapper createMapper() { return NetworkDataframeMapperBuilder.ofStream(this::itemsStream, this::getOrThrow) .stringsIndex("generator_id", coordinatedReactiveControl -> coordinatedReactiveControl.getExtendable().getId()) - .doubles("q_percent", CoordinatedReactiveControl::getQPercent, CoordinatedReactiveControl::setQPercent) + .doubles("q_percent", (crc, context) -> crc.getQPercent(), (crc, qPercent, context) -> crc.setQPercent(qPercent)) .build(); } diff --git a/java/src/main/java/com/powsybl/dataframe/network/extensions/GeneratorShortCircuitDataframeProvider.java b/java/src/main/java/com/powsybl/dataframe/network/extensions/GeneratorShortCircuitDataframeProvider.java index 498a85da76..0b27033cb6 100644 --- a/java/src/main/java/com/powsybl/dataframe/network/extensions/GeneratorShortCircuitDataframeProvider.java +++ b/java/src/main/java/com/powsybl/dataframe/network/extensions/GeneratorShortCircuitDataframeProvider.java @@ -13,7 +13,6 @@ import com.powsybl.dataframe.network.NetworkDataframeMapperBuilder; import com.powsybl.dataframe.network.adders.NetworkElementAdder; import com.powsybl.iidm.network.Generator; -import com.powsybl.iidm.network.Identifiable; import com.powsybl.iidm.network.Network; import com.powsybl.iidm.network.extensions.GeneratorShortCircuit; @@ -59,10 +58,10 @@ private GeneratorShortCircuit getOrThrow(Network network, String id) { @Override public NetworkDataframeMapper createMapper() { return NetworkDataframeMapperBuilder.ofStream(this::itemsStream, this::getOrThrow) - .stringsIndex("id", ext -> ((Identifiable) ext.getExtendable()).getId()) - .doubles("direct_sub_trans_x", GeneratorShortCircuit::getDirectSubtransX, GeneratorShortCircuit::setDirectSubtransX) - .doubles("direct_trans_x", GeneratorShortCircuit::getDirectTransX, GeneratorShortCircuit::setDirectTransX) - .doubles("step_up_transformer_x", GeneratorShortCircuit::getStepUpTransformerX, GeneratorShortCircuit::setStepUpTransformerX) + .stringsIndex("id", ext -> (ext.getExtendable()).getId()) + .doubles("direct_sub_trans_x", (gsc, context) -> gsc.getDirectSubtransX(), (gsc, dsx, context) -> gsc.setDirectSubtransX(dsx)) + .doubles("direct_trans_x", (gsc, context) -> gsc.getDirectTransX(), (gsc, dtx, context) -> gsc.setDirectTransX(dtx)) + .doubles("step_up_transformer_x", (gsc, context) -> gsc.getStepUpTransformerX(), (gsc, sut, context) -> gsc.setStepUpTransformerX(sut)) .build(); } diff --git a/java/src/main/java/com/powsybl/dataframe/network/extensions/HvdcAngleDroopActivePowerControlDataframeProvider.java b/java/src/main/java/com/powsybl/dataframe/network/extensions/HvdcAngleDroopActivePowerControlDataframeProvider.java index 206dd8e665..6ca11fefa8 100644 --- a/java/src/main/java/com/powsybl/dataframe/network/extensions/HvdcAngleDroopActivePowerControlDataframeProvider.java +++ b/java/src/main/java/com/powsybl/dataframe/network/extensions/HvdcAngleDroopActivePowerControlDataframeProvider.java @@ -34,14 +34,14 @@ public String getExtensionName() { @Override public ExtensionInformation getExtensionInformation() { return new ExtensionInformation(HvdcAngleDroopActivePowerControl.NAME, - "Active power control mode based on an offset in MW and a droop in MW/degree", - "index : id (str), droop (float), p0 (float), enabled (bool)"); + "Active power control mode based on an offset in MW and a droop in MW/degree", + "index : id (str), droop (float), p0 (float), enabled (bool)"); } private Stream itemsStream(Network network) { return network.getHvdcLineStream() - .map(g -> (HvdcAngleDroopActivePowerControl) g.getExtension(HvdcAngleDroopActivePowerControl.class)) - .filter(Objects::nonNull); + .map(g -> (HvdcAngleDroopActivePowerControl) g.getExtension(HvdcAngleDroopActivePowerControl.class)) + .filter(Objects::nonNull); } private HvdcAngleDroopActivePowerControl getOrThrow(Network network, String id) { @@ -59,19 +59,19 @@ private HvdcAngleDroopActivePowerControl getOrThrow(Network network, String id) @Override public NetworkDataframeMapper createMapper() { return NetworkDataframeMapperBuilder.ofStream(this::itemsStream, this::getOrThrow) - .stringsIndex("id", ext -> ext.getExtendable().getId()) - .doubles("droop", HvdcAngleDroopActivePowerControl::getDroop, (c, d) -> c.setDroop((float) d)) - .doubles("p0", HvdcAngleDroopActivePowerControl::getP0, (c, d) -> c.setP0((float) d)) - .booleans("enabled", HvdcAngleDroopActivePowerControl::isEnabled, HvdcAngleDroopActivePowerControl::setEnabled) - .build(); + .stringsIndex("id", ext -> ext.getExtendable().getId()) + .doubles("droop", (hvdcadapc, context) -> hvdcadapc.getDroop(), (c, d, context) -> c.setDroop((float) d)) + .doubles("p0", (hvdcadapc, context) -> hvdcadapc.getP0(), (c, d, context) -> c.setP0((float) d)) + .booleans("enabled", HvdcAngleDroopActivePowerControl::isEnabled, HvdcAngleDroopActivePowerControl::setEnabled) + .build(); } @Override public void removeExtensions(Network network, List ids) { ids.stream().filter(Objects::nonNull) - .map(id -> network.getHvdcLine(id)) - .filter(Objects::nonNull) - .forEach(g -> g.removeExtension(HvdcAngleDroopActivePowerControl.class)); + .map(network::getHvdcLine) + .filter(Objects::nonNull) + .forEach(g -> g.removeExtension(HvdcAngleDroopActivePowerControl.class)); } @Override diff --git a/java/src/main/java/com/powsybl/dataframe/network/extensions/HvdcOperatorActivePowerRangeDataframeProvider.java b/java/src/main/java/com/powsybl/dataframe/network/extensions/HvdcOperatorActivePowerRangeDataframeProvider.java index 67a20adc15..3431292200 100644 --- a/java/src/main/java/com/powsybl/dataframe/network/extensions/HvdcOperatorActivePowerRangeDataframeProvider.java +++ b/java/src/main/java/com/powsybl/dataframe/network/extensions/HvdcOperatorActivePowerRangeDataframeProvider.java @@ -59,8 +59,8 @@ private HvdcOperatorActivePowerRange getOrThrow(Network network, String id) { public NetworkDataframeMapper createMapper() { return NetworkDataframeMapperBuilder.ofStream(this::itemsStream, this::getOrThrow) .stringsIndex("id", ext -> ext.getExtendable().getId()) - .doubles("opr_from_cs1_to_cs2", HvdcOperatorActivePowerRange::getOprFromCS1toCS2, (r, d) -> r.setOprFromCS1toCS2((float) d)) - .doubles("opr_from_cs2_to_cs1", HvdcOperatorActivePowerRange::getOprFromCS2toCS1, (r, d) -> r.setOprFromCS2toCS1((float) d)) + .doubles("opr_from_cs1_to_cs2", (hvdcOAPR, context) -> hvdcOAPR.getOprFromCS1toCS2(), (r, d, context) -> r.setOprFromCS1toCS2((float) d)) + .doubles("opr_from_cs2_to_cs1", (hvdcOAPR, context) -> hvdcOAPR.getOprFromCS2toCS1(), (r, d, context) -> r.setOprFromCS2toCS1((float) d)) .build(); } diff --git a/java/src/main/java/com/powsybl/dataframe/network/extensions/IdentifiableShortCircuitDataframeProvider.java b/java/src/main/java/com/powsybl/dataframe/network/extensions/IdentifiableShortCircuitDataframeProvider.java index 5909226193..90e88a4638 100644 --- a/java/src/main/java/com/powsybl/dataframe/network/extensions/IdentifiableShortCircuitDataframeProvider.java +++ b/java/src/main/java/com/powsybl/dataframe/network/extensions/IdentifiableShortCircuitDataframeProvider.java @@ -60,8 +60,8 @@ public NetworkDataframeMapper createMapper() { return NetworkDataframeMapperBuilder.ofStream(this::itemsStream, this::getOrThrow) .stringsIndex("id", ext -> ((Identifiable) ext.getExtendable()).getId()) .strings("equipment_type", ext -> ((Identifiable) ext.getExtendable()).getType().toString()) - .doubles("ip_min", IdentifiableShortCircuit::getIpMin, IdentifiableShortCircuit::setIpMin) - .doubles("ip_max", IdentifiableShortCircuit::getIpMax, IdentifiableShortCircuit::setIpMax) + .doubles("ip_min", (ist, context) -> ist.getIpMin(), (ist, ipMin, context) -> ist.setIpMin(ipMin)) + .doubles("ip_max", (ist, context) -> ist.getIpMax(), (ist, ipMax, context) -> ist.setIpMax(ipMax)) .build(); } diff --git a/java/src/main/java/com/powsybl/dataframe/network/extensions/InjectionObservabilityDataframeProvider.java b/java/src/main/java/com/powsybl/dataframe/network/extensions/InjectionObservabilityDataframeProvider.java index 36b3878d8e..8ac7e0ca86 100644 --- a/java/src/main/java/com/powsybl/dataframe/network/extensions/InjectionObservabilityDataframeProvider.java +++ b/java/src/main/java/com/powsybl/dataframe/network/extensions/InjectionObservabilityDataframeProvider.java @@ -63,9 +63,9 @@ public NetworkDataframeMapper createMapper() { return NetworkDataframeMapperBuilder.ofStream(this::itemsStream, this::getOrThrow) .stringsIndex("id", injectionObservability -> ((Injection) injectionObservability.getExtendable()).getId()) .booleans("observable", InjectionObservability::isObservable) - .doubles("p_standard_deviation", injectionObservability -> injectionObservability.getQualityP() != null ? + .doubles("p_standard_deviation", (injectionObservability, context) -> injectionObservability.getQualityP() != null ? injectionObservability.getQualityP().getStandardDeviation() : Double.NaN, - (injectionObservability, standardDeviation) -> { + (injectionObservability, standardDeviation, context) -> { if (injectionObservability.getQualityP() != null) { injectionObservability.getQualityP().setStandardDeviation(standardDeviation); } else { @@ -77,9 +77,9 @@ public NetworkDataframeMapper createMapper() { (injectionObservability, redundant) -> injectionObservability.getQualityP().setRedundant(redundant)) .booleans("p_redundant_null", injectionObservability -> injectionObservability.getQualityP() == null || injectionObservability.getQualityP().isRedundant().isEmpty()) - .doubles("q_standard_deviation", injectionObservability -> injectionObservability.getQualityQ() != null ? + .doubles("q_standard_deviation", (injectionObservability, context) -> injectionObservability.getQualityQ() != null ? injectionObservability.getQualityQ().getStandardDeviation() : Double.NaN, - (injectionObservability, standardDeviation) -> { + (injectionObservability, standardDeviation, context) -> { if (injectionObservability.getQualityQ() != null) { injectionObservability.getQualityQ().setStandardDeviation(standardDeviation); } else { @@ -91,9 +91,9 @@ public NetworkDataframeMapper createMapper() { (injectionObservability, redundant) -> injectionObservability.getQualityQ().setRedundant(redundant)) .booleans("q_redundant_null", injectionObservability -> injectionObservability.getQualityQ() == null || injectionObservability.getQualityQ().isRedundant().isEmpty()) - .doubles("v_standard_deviation", injectionObservability -> injectionObservability.getQualityV() != null ? + .doubles("v_standard_deviation", (injectionObservability, context) -> injectionObservability.getQualityV() != null ? injectionObservability.getQualityV().getStandardDeviation() : Double.NaN, - (injectionObservability, standardDeviation) -> { + (injectionObservability, standardDeviation, context) -> { if (injectionObservability.getQualityV() != null) { injectionObservability.getQualityV().setStandardDeviation(standardDeviation); } else { diff --git a/java/src/main/java/com/powsybl/dataframe/network/extensions/LoadDetailDataframeProvider.java b/java/src/main/java/com/powsybl/dataframe/network/extensions/LoadDetailDataframeProvider.java index ee9fe4d3df..a09ed024b3 100644 --- a/java/src/main/java/com/powsybl/dataframe/network/extensions/LoadDetailDataframeProvider.java +++ b/java/src/main/java/com/powsybl/dataframe/network/extensions/LoadDetailDataframeProvider.java @@ -64,10 +64,10 @@ private LoadDetail getOrThrow(Network network, String id) { public NetworkDataframeMapper createMapper() { return NetworkDataframeMapperBuilder.ofStream(this::itemsStream, this::getOrThrow) .stringsIndex("id", ext -> ext.getExtendable().getId()) - .doubles(FIXED_P, LoadDetail::getFixedActivePower, LoadDetail::setFixedActivePower) - .doubles(VARIABLE_P, LoadDetail::getVariableActivePower, LoadDetail::setVariableActivePower) - .doubles(FIXED_Q, LoadDetail::getFixedReactivePower, LoadDetail::setFixedReactivePower) - .doubles(VARIABLE_Q, LoadDetail::getVariableReactivePower, LoadDetail::setVariableReactivePower) + .doubles(FIXED_P, (ld, context) -> ld.getFixedActivePower(), (ld, fap, context) -> ld.setFixedActivePower(fap)) + .doubles(VARIABLE_P, (ld, context) -> ld.getVariableActivePower(), (ld, vap, context) -> ld.setVariableActivePower(vap)) + .doubles(FIXED_Q, (ld, context) -> ld.getFixedReactivePower(), (ld, frp, context) -> ld.setFixedReactivePower(frp)) + .doubles(VARIABLE_Q, (ld, context) -> ld.getVariableReactivePower(), (ld, vrp, context) -> ld.setVariableReactivePower(vrp)) .build(); } diff --git a/java/src/main/java/com/powsybl/dataframe/network/extensions/NetworkExtensions.java b/java/src/main/java/com/powsybl/dataframe/network/extensions/NetworkExtensions.java index 4414963ec8..fe552efb53 100644 --- a/java/src/main/java/com/powsybl/dataframe/network/extensions/NetworkExtensions.java +++ b/java/src/main/java/com/powsybl/dataframe/network/extensions/NetworkExtensions.java @@ -14,6 +14,7 @@ import com.powsybl.dataframe.DataframeMapperBuilder; import com.powsybl.dataframe.network.ExtensionInformation; import com.powsybl.dataframe.network.NetworkDataframeMapper; +import com.powsybl.dataframe.network.DataframeContext; import com.powsybl.dataframe.network.adders.NetworkElementAdder; import com.powsybl.iidm.network.Network; import com.powsybl.python.commons.PyPowsyblApiHeader; @@ -88,7 +89,7 @@ List getExtensionInformation() { } } - public static PyPowsyblApiHeader.ArrayPointer getExtensionInformation() { + public static PyPowsyblApiHeader.ArrayPointer getExtensionInformation(DataframeContext dataframeContext) { DataframeMapper mapper = new DataframeMapperBuilder() .itemsProvider(Container::getExtensionInformation) .stringsIndex("id", ExtensionInformation::getId) @@ -102,7 +103,7 @@ public static PyPowsyblApiHeader.ArrayPointer .collect(Collectors.toList()) ); CDataframeHandler handler = new CDataframeHandler(); - mapper.createDataframe(container, handler, new DataframeFilter()); + mapper.createDataframe(container, handler, new DataframeFilter(), dataframeContext); return handler.getDataframePtr(); } } diff --git a/java/src/main/java/com/powsybl/dataframe/network/extensions/SecondaryVoltageControlDataframeProvider.java b/java/src/main/java/com/powsybl/dataframe/network/extensions/SecondaryVoltageControlDataframeProvider.java index 8eb278fd72..608e88343c 100644 --- a/java/src/main/java/com/powsybl/dataframe/network/extensions/SecondaryVoltageControlDataframeProvider.java +++ b/java/src/main/java/com/powsybl/dataframe/network/extensions/SecondaryVoltageControlDataframeProvider.java @@ -126,7 +126,7 @@ public Map createMappers() { mappers.put("zones", NetworkDataframeMapperBuilder.ofStream(this::zonesStream, new ControlZoneGetter()) .stringsIndex("name", ControlZone::getName) - .doubles("target_v", zone -> zone.getPilotPoint().getTargetV(), (zone, v) -> zone.getPilotPoint().setTargetV(v)) + .doubles("target_v", (zone, context) -> zone.getPilotPoint().getTargetV(), (zone, v, context) -> zone.getPilotPoint().setTargetV(v)) .strings("bus_ids", zone -> String.join(",", zone.getPilotPoint().getBusbarSectionsOrBusesIds())) .build() ); diff --git a/java/src/main/java/com/powsybl/dataframe/network/extensions/StandByAutomatonDataframeProvider.java b/java/src/main/java/com/powsybl/dataframe/network/extensions/StandByAutomatonDataframeProvider.java index a97c8f57b2..176de812e9 100644 --- a/java/src/main/java/com/powsybl/dataframe/network/extensions/StandByAutomatonDataframeProvider.java +++ b/java/src/main/java/com/powsybl/dataframe/network/extensions/StandByAutomatonDataframeProvider.java @@ -35,38 +35,38 @@ public String getExtensionName() { @Override public ExtensionInformation getExtensionInformation() { return new ExtensionInformation("standbyAutomaton", "allow to manage standby mode for static var compensator", - "index : id (str), standby (boolean), b0 (double), low_voltage_threshold (double), low_voltage_setpoint (double)," + - " high_voltage_threshold (double), high_voltage_setpoint (double)"); + "index : id (str), standby (boolean), b0 (double), low_voltage_threshold (double), low_voltage_setpoint (double)," + + " high_voltage_threshold (double), high_voltage_setpoint (double)"); } private Stream itemsStream(Network network) { return network.getStaticVarCompensatorStream() - .map(svc -> (StandbyAutomaton) svc.getExtension(StandbyAutomaton.class)) - .filter(Objects::nonNull); + .map(svc -> (StandbyAutomaton) svc.getExtension(StandbyAutomaton.class)) + .filter(Objects::nonNull); } @Override public NetworkDataframeMapper createMapper() { return NetworkDataframeMapperBuilder.ofStream(this::itemsStream, this::getOrThrow) - .stringsIndex("id", ext -> ext.getExtendable().getId()) - .booleans("standby", StandbyAutomaton::isStandby, StandbyAutomaton::setStandby) - .doubles("b0", StandbyAutomaton::getB0, StandbyAutomaton::setB0) - .doubles("low_voltage_threshold", StandbyAutomaton::getLowVoltageThreshold, - StandbyAutomaton::setLowVoltageThreshold) - .doubles("low_voltage_setpoint", StandbyAutomaton::getLowVoltageSetpoint, - StandbyAutomaton::setLowVoltageSetpoint) - .doubles("high_voltage_threshold", StandbyAutomaton::getHighVoltageThreshold, - StandbyAutomaton::setHighVoltageThreshold) - .doubles("high_voltage_setpoint", StandbyAutomaton::getHighVoltageSetpoint, - StandbyAutomaton::setHighVoltageSetpoint) - .build(); + .stringsIndex("id", ext -> ext.getExtendable().getId()) + .booleans("standby", StandbyAutomaton::isStandby, StandbyAutomaton::setStandby) + .doubles("b0", (sa, context) -> sa.getB0(), (sa, b0, context) -> sa.setB0(b0)) + .doubles("low_voltage_threshold", (sa, context) -> sa.getLowVoltageThreshold(), + (sa, lvt, context) -> sa.setLowVoltageThreshold(lvt)) + .doubles("low_voltage_setpoint", (sa, context) -> sa.getLowVoltageSetpoint(), + (sa, lvs, context) -> sa.setLowVoltageSetpoint(lvs)) + .doubles("high_voltage_threshold", (sa, context) -> sa.getHighVoltageThreshold(), + (sa, hvt, context) -> sa.setHighVoltageThreshold(hvt)) + .doubles("high_voltage_setpoint", (sa, context) -> sa.getHighVoltageSetpoint(), + (sa, hvs, context) -> sa.setHighVoltageSetpoint(hvs)) + .build(); } @Override public void removeExtensions(Network network, List ids) { ids.stream().map(network::getStaticVarCompensator) - .filter(Objects::nonNull) - .forEach(staticVarCompensator -> staticVarCompensator.removeExtension(StandbyAutomaton.class)); + .filter(Objects::nonNull) + .forEach(staticVarCompensator -> staticVarCompensator.removeExtension(StandbyAutomaton.class)); } @Override diff --git a/java/src/main/java/com/powsybl/python/network/Dataframes.java b/java/src/main/java/com/powsybl/python/network/Dataframes.java index 8f64bd90c7..7b93b1263b 100644 --- a/java/src/main/java/com/powsybl/python/network/Dataframes.java +++ b/java/src/main/java/com/powsybl/python/network/Dataframes.java @@ -13,6 +13,7 @@ import com.powsybl.dataframe.DataframeMapperBuilder; import com.powsybl.dataframe.impl.DefaultDataframeHandler; import com.powsybl.dataframe.impl.Series; +import com.powsybl.dataframe.network.DataframeContext; import com.powsybl.flow_decomposition.FlowDecompositionResults; import com.powsybl.iidm.network.*; import com.powsybl.iidm.network.extensions.ConnectablePosition; @@ -82,22 +83,26 @@ private Dataframes() { /** * Maps an object to a C struct using the provided mapper. */ + public static ArrayPointer createCDataframe(DataframeMapper mapper, T object, DataframeContext dataframeContext) { + return createCDataframe(mapper, object, new DataframeFilter(), dataframeContext); + } + public static ArrayPointer createCDataframe(DataframeMapper mapper, T object) { - return createCDataframe(mapper, object, new DataframeFilter()); + return createCDataframe(mapper, object, new DataframeFilter(), DataframeContext.deactivate()); } - public static ArrayPointer createCDataframe(DataframeMapper mapper, T object, DataframeFilter dataframeFilter) { + public static ArrayPointer createCDataframe(DataframeMapper mapper, T object, DataframeFilter dataframeFilter, DataframeContext dataframeContext) { CDataframeHandler handler = new CDataframeHandler(); - mapper.createDataframe(object, handler, dataframeFilter); + mapper.createDataframe(object, handler, dataframeFilter, dataframeContext); return handler.getDataframePtr(); } /** * Maps an object to java series */ - public static List createSeries(DataframeMapper mapper, T object) { + public static List createSeries(DataframeMapper mapper, T object, DataframeContext dataframeContext) { List series = new ArrayList<>(); - mapper.createDataframe(object, new DefaultDataframeHandler(series::add), new DataframeFilter()); + mapper.createDataframe(object, new DefaultDataframeHandler(series::add), new DataframeFilter(), dataframeContext); return List.copyOf(series); } diff --git a/java/src/main/java/com/powsybl/python/network/NetworkCFunctions.java b/java/src/main/java/com/powsybl/python/network/NetworkCFunctions.java index b8bac475ba..73de78da96 100644 --- a/java/src/main/java/com/powsybl/python/network/NetworkCFunctions.java +++ b/java/src/main/java/com/powsybl/python/network/NetworkCFunctions.java @@ -18,6 +18,7 @@ import com.powsybl.dataframe.SeriesMetadata; import com.powsybl.dataframe.network.NetworkDataframeMapper; import com.powsybl.dataframe.network.NetworkDataframes; +import com.powsybl.dataframe.network.DataframeContext; import com.powsybl.dataframe.network.adders.AliasDataframeAdder; import com.powsybl.dataframe.network.adders.NetworkElementAdders; import com.powsybl.dataframe.network.extensions.NetworkExtensions; @@ -407,12 +408,14 @@ public static ArrayPointer createNetworkElementsSeriesArray(Isola FilterAttributesType filterAttributesType, CCharPointerPointer attributesPtrPtr, int attributesCount, DataframePointer selectedElementsDataframe, + boolean perUnit, + double nominalApparentPower, ExceptionHandlerPointer exceptionHandlerPtr) { return Util.doCatch(exceptionHandlerPtr, () -> { NetworkDataframeMapper mapper = NetworkDataframes.getDataframeMapper(convert(elementType)); Network network = ObjectHandles.getGlobal().get(networkHandle); DataframeFilter dataframeFilter = createDataframeFilter(filterAttributesType, attributesPtrPtr, attributesCount, selectedElementsDataframe); - return Dataframes.createCDataframe(mapper, network, dataframeFilter); + return Dataframes.createCDataframe(mapper, network, dataframeFilter, new DataframeContext(perUnit, nominalApparentPower)); }); } @@ -428,7 +431,7 @@ public static ArrayPointer createNetworkElemen NetworkDataframeMapper mapper = NetworkDataframes.getExtensionDataframeMapper(name, tableName); if (mapper != null) { Network network = ObjectHandles.getGlobal().get(networkHandle); - return Dataframes.createCDataframe(mapper, network); + return Dataframes.createCDataframe(mapper, network, DataframeContext.deactivate()); } else { throw new PowsyblException("extension " + name + " not found"); } @@ -442,7 +445,7 @@ public static ArrayPointer getExtensionsNames(IsolateThread @CEntryPoint(name = "getExtensionsInformation") public static ArrayPointer getExtensionsInformation(IsolateThread thread, ExceptionHandlerPointer exceptionHandlerPtr) { - return doCatch(exceptionHandlerPtr, NetworkExtensions::getExtensionInformation); + return doCatch(exceptionHandlerPtr, () -> NetworkExtensions.getExtensionInformation(DataframeContext.deactivate())); } @CEntryPoint(name = "createElement") @@ -463,12 +466,14 @@ public static void createElement(IsolateThread thread, ObjectHandle networkHandl @CEntryPoint(name = "updateNetworkElementsWithSeries") public static void updateNetworkElementsWithSeries(IsolateThread thread, ObjectHandle networkHandle, ElementType elementType, - DataframePointer dataframe, + DataframePointer dataframe, boolean perUnit, + double nominalApparentPower, PyPowsyblApiHeader.ExceptionHandlerPointer exceptionHandlerPtr) { doCatch(exceptionHandlerPtr, () -> { Network network = ObjectHandles.getGlobal().get(networkHandle); UpdatingDataframe updatingDataframe = createDataframe(dataframe); - NetworkDataframes.getDataframeMapper(convert(elementType)).updateSeries(network, updatingDataframe); + NetworkDataframes.getDataframeMapper(convert(elementType)) + .updateSeries(network, updatingDataframe, new DataframeContext(perUnit, nominalApparentPower)); }); } @@ -731,12 +736,12 @@ public static void updateNetworkElementsExtensionsWithSeries(IsolateThread threa doCatch(exceptionHandlerPtr, () -> { String name = CTypeUtil.toString(namePtr); String tmpName = CTypeUtil.toString(tableNamePtr); - String tableName = tmpName.equals("") ? null : tmpName; + String tableName = tmpName.isEmpty() ? null : tmpName; NetworkDataframeMapper mapper = NetworkDataframes.getExtensionDataframeMapper(name, tableName); if (mapper != null) { Network network = ObjectHandles.getGlobal().get(networkHandle); UpdatingDataframe updatingDataframe = createDataframe(dataframe); - mapper.updateSeries(network, updatingDataframe); + mapper.updateSeries(network, updatingDataframe, DataframeContext.deactivate()); } else { if (tableName != null) { throw new PowsyblException("table " + tableName + " of extension " + name + " not found"); diff --git a/java/src/test/java/com/powsybl/dataframe/DataframeMapperBuilderTest.java b/java/src/test/java/com/powsybl/dataframe/DataframeMapperBuilderTest.java index 7582c7054d..ebad62f2f3 100644 --- a/java/src/test/java/com/powsybl/dataframe/DataframeMapperBuilderTest.java +++ b/java/src/test/java/com/powsybl/dataframe/DataframeMapperBuilderTest.java @@ -9,6 +9,7 @@ import com.google.common.base.Functions; import com.powsybl.dataframe.DataframeFilter.AttributeFilterType; import com.powsybl.dataframe.impl.DefaultDataframeHandler; +import com.powsybl.dataframe.network.DataframeContext; import com.powsybl.dataframe.update.DefaultUpdatingDataframe; import com.powsybl.dataframe.update.TestDoubleSeries; import com.powsybl.dataframe.update.TestStringSeries; @@ -137,13 +138,13 @@ List getElements() { Element getElement(UpdatingDataframe dataframe, int index) { List result = elements.stream().filter(element -> dataframe.getStringValue("id", index).get().equals(element.getId()) - && dataframe.getIntValue("id2", index).getAsInt() == element.getId2()).collect(Collectors.toList()); + && dataframe.getIntValue("id2", index).getAsInt() == element.getId2()).collect(Collectors.toList()); return result.get(0); } Element getElement(String id, int id2) { List result = elements.stream().filter(element -> id.equals(element.getId()) - && id2 == element.getId2()).collect(Collectors.toList()); + && id2 == element.getId2()).collect(Collectors.toList()); return result.get(0); } } @@ -153,38 +154,38 @@ Element getElement(String id, int id2) { @BeforeEach void setUp() { mapper = new DataframeMapperBuilder() - .itemsProvider(Container::getElements) - .itemGetter(Container::getElement) - .stringsIndex("id", Element::getId) - .strings("str", Element::getStrValue, Element::setStrValue) - .ints("int", Element::getIntValue, Element::setIntValue) - .doubles("double", Element::getDoubleValue, Element::setDoubleValue) - .enums("color", Color.class, Element::getColorValue, Element::setColorValue) - .build(); + .itemsProvider(Container::getElements) + .itemGetter(Container::getElement) + .stringsIndex("id", Element::getId) + .strings("str", Element::getStrValue, Element::setStrValue) + .ints("int", Element::getIntValue, Element::setIntValue) + .doubles("double", (element, context) -> element.getDoubleValue(), (element, dv, context) -> element.setDoubleValue(dv)) + .enums("color", Color.class, Element::getColorValue, Element::setColorValue) + .build(); } @Test void test() { DataframeMapper mapper = new DataframeMapperBuilder() - .itemsProvider(Container::getElements) - .stringsIndex("id", Element::getId) - .strings("str", Element::getStrValue) - .ints("int", Element::getIntValue) - .doubles("double", Element::getDoubleValue) - .enums("color", Color.class, Element::getColorValue) - .build(); + .itemsProvider(Container::getElements) + .stringsIndex("id", Element::getId) + .strings("str", Element::getStrValue) + .ints("int", Element::getIntValue) + .doubles("double", Element::getDoubleValue) + .enums("color", Color.class, Element::getColorValue) + .build(); Container container = new Container( - new Element("el1", "val1", 1, 10, Color.RED), - new Element("el2", "val2", 2, 20, Color.BLUE) + new Element("el1", "val1", 1, 10, Color.RED), + new Element("el2", "val2", 2, 20, Color.BLUE) ); List series = new ArrayList<>(); - mapper.createDataframe(container, new DefaultDataframeHandler(series::add), new DataframeFilter()); + mapper.createDataframe(container, new DefaultDataframeHandler(series::add), new DataframeFilter(), DataframeContext.deactivate()); assertThat(series) - .extracting(com.powsybl.dataframe.impl.Series::getName) - .containsExactly("id", "str", "int", "double", "color"); + .extracting(com.powsybl.dataframe.impl.Series::getName) + .containsExactly("id", "str", "int", "double", "color"); } UpdatingDataframe createDataframe(int size) { @@ -199,19 +200,19 @@ UpdatingDataframe createDataframe(int size) { void updateMonoIndex() { Container container = new Container( - new Element("el1", "val1", 1.0, 10, Color.RED), - new Element("el2", "val2", 2.0, 20, Color.BLUE) + new Element("el1", "val1", 1.0, 10, Color.RED), + new Element("el2", "val2", 2.0, 20, Color.BLUE) ); mapper = new DataframeMapperBuilder() - .itemsProvider(Container::getElements) - .itemGetter(Container::getElement) - .stringsIndex("id", Element::getId) - .strings("str", Element::getStrValue, Element::setStrValue) - .ints("int", Element::getIntValue, Element::setIntValue) - .doubles("double", Element::getDoubleValue, Element::setDoubleValue) - .enums("color", Color.class, Element::getColorValue, Element::setColorValue) - .build(); - mapper.updateSeries(container, createDataframe(2)); + .itemsProvider(Container::getElements) + .itemGetter(Container::getElement) + .stringsIndex("id", Element::getId) + .strings("str", Element::getStrValue, Element::setStrValue) + .ints("int", Element::getIntValue, Element::setIntValue) + .doubles("double", (element, context) -> element.getDoubleValue(), (element, dv, context) -> element.setDoubleValue(dv)) + .enums("color", Color.class, Element::getColorValue, Element::setColorValue) + .build(); + mapper.updateSeries(container, createDataframe(2), DataframeContext.deactivate()); assertEquals(1.2, container.elements.get("el1").getDoubleValue()); assertEquals(2.2, container.elements.get("el2").getDoubleValue()); } @@ -229,21 +230,21 @@ UpdatingDataframe createDataframeMultiIndex(int size) { @Test void updateMultiIndex() { MultiIndexContainer container = new MultiIndexContainer( - new Element("el1", 0, "val1", 1.0, 10, Color.RED), - new Element("el1", 1, "val2", 2.0, 20, Color.BLUE), - new Element("el2", 0, "val2", 2.0, 20, Color.BLUE) + new Element("el1", 0, "val1", 1.0, 10, Color.RED), + new Element("el1", 1, "val2", 2.0, 20, Color.BLUE), + new Element("el2", 0, "val2", 2.0, 20, Color.BLUE) ); DataframeMapper multiIndexMapper = new DataframeMapperBuilder() - .itemsProvider(MultiIndexContainer::getElements) - .itemMultiIndexGetter(MultiIndexContainer::getElement) - .stringsIndex("id", Element::getId) - .intsIndex("id2", Element::getId2) - .strings("str", Element::getStrValue, Element::setStrValue) - .ints("int", Element::getIntValue, Element::setIntValue) - .doubles("double", Element::getDoubleValue, Element::setDoubleValue) - .enums("color", Color.class, Element::getColorValue, Element::setColorValue) - .build(); - multiIndexMapper.updateSeries(container, createDataframeMultiIndex(2)); + .itemsProvider(MultiIndexContainer::getElements) + .itemMultiIndexGetter(MultiIndexContainer::getElement) + .stringsIndex("id", Element::getId) + .intsIndex("id2", Element::getId2) + .strings("str", Element::getStrValue, Element::setStrValue) + .ints("int", Element::getIntValue, Element::setIntValue) + .doubles("double", (element, context) -> element.getDoubleValue(), (element, dv, context) -> element.setDoubleValue(dv)) + .enums("color", Color.class, Element::getColorValue, Element::setColorValue) + .build(); + multiIndexMapper.updateSeries(container, createDataframeMultiIndex(2), DataframeContext.deactivate()); assertEquals(1.0, container.getElement("el1", 0).getDoubleValue()); assertEquals(1.2, container.getElement("el1", 1).getDoubleValue()); assertEquals(2.2, container.getElement("el2", 0).getDoubleValue()); @@ -255,50 +256,50 @@ void updateMultiIndex() { @Test void testDefaults() { DataframeMapper mapper = new DataframeMapperBuilder() - .itemsProvider(Container::getElements) - .stringsIndex("id", Element::getId) - .strings("str", Element::getStrValue, false) - .ints("int", Element::getIntValue) - .doubles("double", Element::getDoubleValue, false) - .enums("color", Color.class, Element::getColorValue) - .build(); + .itemsProvider(Container::getElements) + .stringsIndex("id", Element::getId) + .strings("str", Element::getStrValue, false) + .ints("int", Element::getIntValue) + .doubles("double", Element::getDoubleValue, false) + .enums("color", Color.class, Element::getColorValue) + .build(); Container container = new Container( - new Element("el1", "val1", 1, 10, Color.RED), - new Element("el2", "val2", 2, 20, Color.BLUE) + new Element("el1", "val1", 1, 10, Color.RED), + new Element("el2", "val2", 2, 20, Color.BLUE) ); List series = new ArrayList<>(); - mapper.createDataframe(container, new DefaultDataframeHandler(series::add), new DataframeFilter()); + mapper.createDataframe(container, new DefaultDataframeHandler(series::add), new DataframeFilter(), DataframeContext.deactivate()); assertThat(series) - .extracting(com.powsybl.dataframe.impl.Series::getName) - .containsExactly("id", "int", "color"); + .extracting(com.powsybl.dataframe.impl.Series::getName) + .containsExactly("id", "int", "color"); } @Test void testFilterAttributes() { DataframeMapper mapper = new DataframeMapperBuilder() - .itemsProvider(Container::getElements) - .stringsIndex("id", Element::getId) - .strings("str", Element::getStrValue, false) - .ints("int", Element::getIntValue) - .doubles("double", Element::getDoubleValue, false) - .enums("color", Color.class, Element::getColorValue) - .build(); + .itemsProvider(Container::getElements) + .stringsIndex("id", Element::getId) + .strings("str", Element::getStrValue, false) + .ints("int", Element::getIntValue) + .doubles("double", Element::getDoubleValue, false) + .enums("color", Color.class, Element::getColorValue) + .build(); Container container = new Container( - new Element("el1", "val1", 1, 10, Color.RED), - new Element("el2", "val2", 2, 20, Color.BLUE) + new Element("el1", "val1", 1, 10, Color.RED), + new Element("el2", "val2", 2, 20, Color.BLUE) ); List series = new ArrayList<>(); - mapper.createDataframe(container, new DefaultDataframeHandler(series::add), new DataframeFilter(AttributeFilterType.INPUT_ATTRIBUTES, Arrays.asList("str", "color"))); + mapper.createDataframe(container, new DefaultDataframeHandler(series::add), new DataframeFilter(AttributeFilterType.INPUT_ATTRIBUTES, Arrays.asList("str", "color")), DataframeContext.deactivate()); assertThat(series) - .extracting(com.powsybl.dataframe.impl.Series::getName) - .containsExactly("id", "str", "color"); + .extracting(com.powsybl.dataframe.impl.Series::getName) + .containsExactly("id", "str", "color"); } } diff --git a/java/src/test/java/com/powsybl/dataframe/network/NetworkDataframesTest.java b/java/src/test/java/com/powsybl/dataframe/network/NetworkDataframesTest.java index 5498a21f98..0a500e2342 100644 --- a/java/src/test/java/com/powsybl/dataframe/network/NetworkDataframesTest.java +++ b/java/src/test/java/com/powsybl/dataframe/network/NetworkDataframesTest.java @@ -54,7 +54,7 @@ private static List createDataFrame(DataframeElementType type, Network n List series = new ArrayList<>(); NetworkDataframeMapper mapper = NetworkDataframes.getDataframeMapper(type); assertNotNull(mapper); - mapper.createDataframe(network, new DefaultDataframeHandler(series::add), dataframeFilter); + mapper.createDataframe(network, new DefaultDataframeHandler(series::add), dataframeFilter, DataframeContext.deactivate()); return series; } @@ -66,14 +66,14 @@ private static List createExtensionDataFrame(String name, String tableNa List series = new ArrayList<>(); NetworkDataframeMapper mapper = NetworkDataframes.getExtensionDataframeMapper(name, tableName); assertNotNull(mapper); - mapper.createDataframe(network, new DefaultDataframeHandler(series::add), new DataframeFilter()); + mapper.createDataframe(network, new DefaultDataframeHandler(series::add), new DataframeFilter(), DataframeContext.deactivate()); return series; } private static void updateExtension(String name, Network network, UpdatingDataframe updatingDataframe) { NetworkDataframeMapper mapper = NetworkDataframes.getExtensionDataframeMapper(name, null); assertNotNull(mapper); - mapper.updateSeries(network, updatingDataframe); + mapper.updateSeries(network, updatingDataframe, DataframeContext.deactivate()); } private DoubleIndexedSeries createInput(List names, double... values) { diff --git a/java/src/test/java/com/powsybl/python/loadflow/validation/LoadFlowValidationTest.java b/java/src/test/java/com/powsybl/python/loadflow/validation/LoadFlowValidationTest.java index ff15708921..af856d93da 100644 --- a/java/src/test/java/com/powsybl/python/loadflow/validation/LoadFlowValidationTest.java +++ b/java/src/test/java/com/powsybl/python/loadflow/validation/LoadFlowValidationTest.java @@ -9,6 +9,7 @@ import com.powsybl.dataframe.impl.Series; import com.powsybl.dataframe.loadflow.validation.InMemoryValidationWriter; import com.powsybl.dataframe.loadflow.validation.Validations; +import com.powsybl.dataframe.network.DataframeContext; import com.powsybl.ieeecdf.converter.IeeeCdfNetworkFactory; import com.powsybl.iidm.network.Network; import com.powsybl.loadflow.LoadFlow; @@ -36,12 +37,12 @@ void test() { runner.run(network, new LoadFlowParameters()); ValidationConfig validationConfig = LoadFlowValidationCFunctions.createValidationConfig(); InMemoryValidationWriter busWriter = LoadFlowValidationCFunctions.createLoadFlowValidationWriter(network, PyPowsyblApiHeader.ValidationType.BUSES, validationConfig); - Assertions.assertThat(Dataframes.createSeries(Validations.busValidationsMapper(), busWriter.getBusData())) + Assertions.assertThat(Dataframes.createSeries(Validations.busValidationsMapper(), busWriter.getBusData(), DataframeContext.deactivate())) .extracting(Series::getName) .contains("id", "incoming_p"); InMemoryValidationWriter genWriter = LoadFlowValidationCFunctions.createLoadFlowValidationWriter(network, PyPowsyblApiHeader.ValidationType.GENERATORS); - Assertions.assertThat(Dataframes.createSeries(Validations.generatorValidationsMapper(), genWriter.getGeneratorData())) + Assertions.assertThat(Dataframes.createSeries(Validations.generatorValidationsMapper(), genWriter.getGeneratorData(), DataframeContext.deactivate())) .extracting(Series::getName) .contains("id", "p"); } diff --git a/java/src/test/java/com/powsybl/python/security/SecurityAnalysisTest.java b/java/src/test/java/com/powsybl/python/security/SecurityAnalysisTest.java index c877fb82fc..73484f9e91 100644 --- a/java/src/test/java/com/powsybl/python/security/SecurityAnalysisTest.java +++ b/java/src/test/java/com/powsybl/python/security/SecurityAnalysisTest.java @@ -10,6 +10,7 @@ import com.powsybl.contingency.ContingencyContext; import com.powsybl.contingency.ContingencyContextType; import com.powsybl.dataframe.impl.Series; +import com.powsybl.dataframe.network.DataframeContext; import com.powsybl.iidm.network.Network; import com.powsybl.iidm.network.test.EurostagTutorialExample1Factory; import com.powsybl.python.network.Dataframes; @@ -69,7 +70,7 @@ void testSecurityAnalysis() { analysisContext.addContingency("First contingency", Collections.singletonList("NHV1_NHV2_1")); SecurityAnalysisResult result = analysisContext.run(network, new SecurityAnalysisParameters(), "OpenLoadFlow", ReportNode.NO_OP); - List series = Dataframes.createSeries(Dataframes.limitViolationsMapper(), result); + List series = Dataframes.createSeries(Dataframes.limitViolationsMapper(), result, DataframeContext.deactivate()); Assertions.assertThat(series) .extracting(Series::getName) .containsExactly("contingency_id", "subject_id", "subject_name", "limit_type", "limit_name", "limit", diff --git a/java/src/test/java/com/powsybl/python/shortcircuit/ShortCircuitAnalysisTest.java b/java/src/test/java/com/powsybl/python/shortcircuit/ShortCircuitAnalysisTest.java index 336b012079..29bb46c5d2 100644 --- a/java/src/test/java/com/powsybl/python/shortcircuit/ShortCircuitAnalysisTest.java +++ b/java/src/test/java/com/powsybl/python/shortcircuit/ShortCircuitAnalysisTest.java @@ -8,6 +8,7 @@ package com.powsybl.python.shortcircuit; import com.powsybl.dataframe.impl.Series; +import com.powsybl.dataframe.network.DataframeContext; import com.powsybl.python.network.Dataframes; import com.powsybl.security.LimitViolation; import com.powsybl.security.LimitViolationType; @@ -71,7 +72,7 @@ void testShortCircuitAnalysisResults() { ShortCircuitAnalysisResult fakeResults = new ShortCircuitAnalysisResult(List.of(fr1, fr2)); - List faultResultsSeries = Dataframes.createSeries(Dataframes.shortCircuitAnalysisFaultResultsMapper(false), fakeResults); + List faultResultsSeries = Dataframes.createSeries(Dataframes.shortCircuitAnalysisFaultResultsMapper(false), fakeResults, DataframeContext.deactivate()); Assertions.assertThat(faultResultsSeries) .extracting(Series::getName) .containsExactly("id", "status", "short_circuit_power", "time_constant", "current", "voltage"); @@ -88,7 +89,7 @@ void testShortCircuitAnalysisResults() { Assertions.assertThat(faultResultsSeries.get(5).getDoubles()) .containsExactly(NaN, NaN); - List feederResultsSeries = Dataframes.createSeries(Dataframes.shortCircuitAnalysisMagnitudeFeederResultsMapper(false), fakeResults); + List feederResultsSeries = Dataframes.createSeries(Dataframes.shortCircuitAnalysisMagnitudeFeederResultsMapper(false), fakeResults, DataframeContext.deactivate()); Assertions.assertThat(feederResultsSeries) .extracting(Series::getName) .containsExactly("id", "connectable_id", "current"); @@ -99,7 +100,7 @@ void testShortCircuitAnalysisResults() { Assertions.assertThat(feederResultsSeries.get(2).getDoubles()) .containsExactly(1.1, 1.2); - List limitViolationsSeries = Dataframes.createSeries(Dataframes.shortCircuitAnalysisLimitViolationsResultsMapper(), fakeResults); + List limitViolationsSeries = Dataframes.createSeries(Dataframes.shortCircuitAnalysisLimitViolationsResultsMapper(), fakeResults, DataframeContext.deactivate()); Assertions.assertThat(limitViolationsSeries) .extracting(Series::getName) .containsExactly("id", "subject_id", "subject_name", "limit_type", "limit_name", "limit", @@ -109,7 +110,7 @@ void testShortCircuitAnalysisResults() { Assertions.assertThat(limitViolationsSeries.get(1).getStrings()) .containsExactly("subj1", "subj2", "subj3", "subj4", "subj5"); - List busResultsSeries = Dataframes.createSeries(Dataframes.shortCircuitAnalysisMagnitudeBusResultsMapper(false), fakeResults); + List busResultsSeries = Dataframes.createSeries(Dataframes.shortCircuitAnalysisMagnitudeBusResultsMapper(false), fakeResults, DataframeContext.deactivate()); Assertions.assertThat(busResultsSeries) .extracting(Series::getName) .containsExactly("id", "voltage_level_id", "bus_id", "initial_voltage_magnitude", "voltage_drop_proportional", "voltage"); diff --git a/pypowsybl/_pypowsybl.pyi b/pypowsybl/_pypowsybl.pyi index bc5711cc82..34033cb612 100644 --- a/pypowsybl/_pypowsybl.pyi +++ b/pypowsybl/_pypowsybl.pyi @@ -668,7 +668,7 @@ def create_element(network: JavaHandle, dataframes: List[Optional[Dataframe]], e def create_exporter_parameters_series_array(format: str) -> SeriesArray: ... def create_importer_parameters_series_array(format: str) -> SeriesArray: ... def create_network(name: str, id: str) -> JavaHandle: ... -def create_network_elements_series_array(network: JavaHandle, element_type: ElementType, filter_attributes_type: FilterAttributesType, attributes: List[str], array: Optional[Dataframe]) -> SeriesArray: ... +def create_network_elements_series_array(network: JavaHandle, element_type: ElementType, filter_attributes_type: FilterAttributesType, attributes: List[str], array: Optional[Dataframe], per_unit: bool, nominal_apparent_power: float) -> SeriesArray: ... def create_network_elements_extension_series_array(network: JavaHandle, extension_name: str, table_name: str) -> SeriesArray: ... def get_extensions_names() -> List[str]: ... def get_extensions_information() -> SeriesArray: ... @@ -742,7 +742,7 @@ def set_working_variant(network: JavaHandle, variant: str) -> None: ... def set_zones(sensitivity_analysis_context: JavaHandle, zones: List[Zone]) -> None: ... def get_logger() -> Logger: ... def update_connectable_status(arg0: JavaHandle, arg1: str, arg2: bool) -> bool: ... -def update_network_elements_with_series(network: JavaHandle, array: Dataframe, element_type: ElementType) -> None: ... +def update_network_elements_with_series(network: JavaHandle, array: Dataframe, element_type: ElementType, per_unit: bool, nominal_apparent_power: float) -> None: ... def update_switch_position(arg0: JavaHandle, arg1: str, arg2: bool) -> bool: ... def validate(network: JavaHandle) -> ValidationLevel: ... def write_network_area_diagram_svg(network: JavaHandle, svg_file: str, voltage_level_ids: Union[str, List[str]], depth: int, high_nominal_voltage_bound: float, low_nominal_voltage_bound: float, nad_parameters: NadParameters) -> None: ... diff --git a/pypowsybl/network/impl/network.py b/pypowsybl/network/impl/network.py index 112fd64c40..244bd86e8c 100644 --- a/pypowsybl/network/impl/network.py +++ b/pypowsybl/network/impl/network.py @@ -52,6 +52,8 @@ class Network: # pylint: disable=too-many-public-methods def __init__(self, handle: _pp.JavaHandle): self._handle = handle self.__init_from_handle() + self._nominal_apparent_power = 100.0 + self._per_unit = False @property def id(self) -> str: @@ -88,6 +90,28 @@ def forecast_distance(self) -> datetime.timedelta: """ return self._forecast_distance + @property + def nominal_apparent_power(self) -> float: + """ + The nominal power to per unit the network (kVA) + """ + return self._nominal_apparent_power + + @nominal_apparent_power.setter + def nominal_apparent_power(self, value: float) -> None: + self._nominal_apparent_power = value + + @property + def per_unit(self) -> bool: + """ + The nominal power to per unit the network (kVA) + """ + return self._per_unit + + @per_unit.setter + def per_unit(self, value: bool) -> None: + self._per_unit = value + def __str__(self) -> str: return f'Network(id={self.id}, name={self.name}, case_date={self.case_date}, ' \ f'forecast_distance={self.forecast_distance}, source_format={self.source_format})' @@ -385,8 +409,7 @@ def get_elements_ids(self, element_type: ElementType, nominal_voltages: Set[floa main_connected_component, main_synchronous_component, not_connected_to_same_bus_at_both_sides) - def get_elements(self, element_type: ElementType, all_attributes: bool = False, attributes: List[str] = None, - **kwargs: ArrayLike) -> DataFrame: + def get_elements(self, element_type: ElementType, all_attributes: bool = False, attributes: List[str] = None, **kwargs: ArrayLike) -> DataFrame: """ Get network elements as a :class:`~pandas.DataFrame` for a specified element type. @@ -419,9 +442,9 @@ def get_elements(self, element_type: ElementType, all_attributes: bool = False, else: elements_array = None - series_array = _pp.create_network_elements_series_array(self._handle, element_type, filter_attributes, - attributes, elements_array) + attributes, elements_array, self._per_unit, + self._nominal_apparent_power) result = create_data_frame_from_series_array(series_array) if attributes: result = result[attributes] @@ -764,8 +787,7 @@ def get_loads(self, all_attributes: bool = False, attributes: List[str] = None, """ return self.get_elements(ElementType.LOAD, all_attributes, attributes, **kwargs) - def get_batteries(self, all_attributes: bool = False, attributes: List[str] = None, - **kwargs: ArrayLike) -> DataFrame: + def get_batteries(self, all_attributes: bool = False, attributes: List[str] = None, **kwargs: ArrayLike) -> DataFrame: r""" Get a dataframe of batteries. @@ -802,8 +824,7 @@ def get_batteries(self, all_attributes: bool = False, attributes: List[str] = No """ return self.get_elements(ElementType.BATTERY, all_attributes, attributes, **kwargs) - def get_lines(self, all_attributes: bool = False, attributes: List[str] = None, - **kwargs: ArrayLike) -> DataFrame: + def get_lines(self, all_attributes: bool = False, attributes: List[str] = None, **kwargs: ArrayLike) -> DataFrame: r""" Get a dataframe of lines data. @@ -2329,7 +2350,7 @@ def _update_elements(self, element_type: ElementType, df: DataFrame = None, **kw metadata = _pp.get_network_elements_dataframe_metadata(element_type) df = _adapt_df_or_kwargs(metadata, df, **kwargs) c_df = _create_c_dataframe(df, metadata) - _pp.update_network_elements_with_series(self._handle, c_df, element_type) + _pp.update_network_elements_with_series(self._handle, c_df, element_type, self._per_unit, self._nominal_apparent_power) def update_buses(self, df: DataFrame = None, **kwargs: ArrayLike) -> None: """ diff --git a/tests/test_java_perunit.py b/tests/test_java_perunit.py new file mode 100644 index 0000000000..89119f2b14 --- /dev/null +++ b/tests/test_java_perunit.py @@ -0,0 +1,493 @@ +# +# Copyright (c) 2024, 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 +# +import pytest + +import pypowsybl as pp +import pandas as pd +from numpy import NaN + +import util + + +def test_per_unit_line(): + net = pp.network.create_four_substations_node_breaker_network() + net.nominal_apparent_power = 250 + net.per_unit = True + per_unit_lines = net.get_lines() + line_s2s3 = per_unit_lines.loc['LINE_S2S3'] + assert 1.56e-5 == pytest.approx(line_s2s3.r, abs=1e-6) + assert 0.0298 == pytest.approx(line_s2s3.x, abs=1e-2) + assert 0.44 == pytest.approx(line_s2s3.p1, abs=1e-1) + assert 0.76 == pytest.approx(line_s2s3.q1, abs=1e-1) + assert 0.86 == pytest.approx(line_s2s3.i1, abs=1e-1) + assert -0.44 == pytest.approx(line_s2s3.p2, abs=1e-1) + assert -0.74 == pytest.approx(line_s2s3.q2, abs=1e-1) + assert 0.86 == pytest.approx(line_s2s3.i2, abs=1e-1) + net.update_lines(id='LINE_S2S3', r=9e-6, x=2e-2) + per_unit_lines = net.get_lines() + line_s2s3 = per_unit_lines.loc['LINE_S2S3'] + assert 9e-6 == pytest.approx(line_s2s3.r, abs=1e-6) + assert 2e-2 == pytest.approx(line_s2s3.x, abs=1e-2) + + +def test_lines_per_unit(): + n = pp.network.create_four_substations_node_breaker_network() + n.per_unit = True + lines = n.get_lines() + expected = pd.DataFrame(index=pd.Series(name='id', data=['LINE_S2S3', 'LINE_S3S4']), + columns=['name', 'r', 'x', 'g1', 'b1', 'g2', 'b2', 'p1', 'q1', 'i1', 'p2', 'q2', 'i2', + 'voltage_level1_id', 'voltage_level2_id', 'bus1_id', 'bus2_id', 'connected1', + 'connected2'], + data=[['', 0, 0.0119, 0, 0, 0, 0, 1.0989, 1.9002, 2.1476, -1.0989, -1.8451, + 2.1476, 'S2VL1', 'S3VL1', 'S2VL1_0', 'S3VL1_0', True, True], + ['', 0, 0.0082, 0, 0, 0, 0, 2.4, 0.0218, 2.4001, -2.4, 0.0254, 2.4001, + 'S3VL1', 'S4VL1', 'S3VL1_0', 'S4VL1_0', True, True]]) + pd.testing.assert_frame_equal(expected, lines, check_dtype=False, atol=10 ** -4) + n.update_lines(pd.DataFrame(data=[[1, 9, 0.1, 0.01, 0.1, 0.1, 1.2, 2, -1.2, -2]], + columns=['r', 'x', 'g1', 'b1', 'g2', 'b2', 'p1', 'q1', 'p2', 'q2'], + index=['LINE_S2S3'])) + expected = pd.DataFrame(index=pd.Series(name='id', data=['LINE_S2S3', 'LINE_S3S4']), + columns=['name', 'r', 'x', 'g1', 'b1', 'g2', 'b2', 'p1', 'q1', 'i1', 'p2', 'q2', 'i2', + 'voltage_level1_id', 'voltage_level2_id', 'bus1_id', 'bus2_id', 'connected1', + 'connected2'], + data=[['', 1, 9, 0.1, 0.01, 0.1, 0.1, 1.2, 2, 2.2819, -1.2, -2, + 2.3324, 'S2VL1', 'S3VL1', 'S2VL1_0', 'S3VL1_0', True, True], + ['', 0, 0.0082, 0, 0, 0, 0, 2.40, 0.0218, 2.4001, -2.4, 0.0254, 2.4001, + 'S3VL1', 'S4VL1', 'S3VL1_0', 'S4VL1_0', True, True]]) + pd.testing.assert_frame_equal(expected, n.get_lines(), check_dtype=False, atol=10 ** -4) + + +def test_generator_per_unit(): + n = pp.network.create_eurostag_tutorial_example1_network() + n.per_unit = True + pp.loadflow.run_ac(n) + expected = pd.DataFrame.from_records( + index='id', + columns=['id', 'name', 'energy_source', 'target_p', 'min_p', 'max_p', 'min_q', 'max_q', 'rated_s', + 'reactive_limits_kind', + 'target_v', + 'target_q', 'voltage_regulator_on', 'regulated_element_id', 'p', 'q', 'i', 'voltage_level_id', + 'bus_id', 'connected'], + data=[['GEN', '', 'OTHER', 6.07, -100, 49.99, -100, 100, None, 'MIN_MAX', 1.02, 3.01, True, 'GEN', -3.03, + -1.12641, 3.16461, 'VLGEN', 'VLGEN_0', True], + ['GEN2', '', 'OTHER', 6.07, -100, 49.99, -1.79769e+306, 1.79769e+306, None, 'MIN_MAX', 1.02, 3.01, True, + 'GEN2', -3.03, -1.13, 3.16, 'VLGEN', 'VLGEN_0', True]]) + pd.testing.assert_frame_equal(expected, n.get_generators(), check_dtype=False, atol=1e-2) + generators2 = pd.DataFrame(data=[[6.080, 3.02, 1.1, False, False]], + columns=['target_p', 'target_q', 'target_v', 'voltage_regulator_on', 'connected'], + index=['GEN']) + n.update_generators(generators2) + expected = pd.DataFrame.from_records( + index='id', + columns=['id', 'name', 'energy_source', 'target_p', 'min_p', 'max_p', 'min_q', 'max_q', 'rated_s', + 'reactive_limits_kind', + 'target_v', + 'target_q', 'voltage_regulator_on', 'regulated_element_id', 'p', 'q', 'i', 'voltage_level_id', + 'bus_id', 'connected'], + data=[['GEN', '', 'OTHER', 6.08, -100, 49.99, -100, 100, None, 'MIN_MAX', 1.1, 3.02, False, 'GEN', -3.03, + -1.12641, NaN, 'VLGEN', '', False], + ['GEN2', '', 'OTHER', 6.07, -100, 49.99, -1.79769e+306, 1.79769e+306, None, 'MIN_MAX', 1.02, 3.01, True, + 'GEN2', -3.03, -1.13, 3.16, 'VLGEN', 'VLGEN_0', True]]) + pd.testing.assert_frame_equal(expected, n.get_generators(), check_dtype=False, atol=1e-2) + + +def test_two_windings_transformers_per_unit(): + n = pp.network.create_four_substations_node_breaker_network() + n.per_unit = True + expected = pd.DataFrame(index=pd.Series(name='id', data=['TWT']), + columns=['name', 'r', 'x', 'g', 'b', 'rated_u1', 'rated_u2', 'rated_s', 'p1', 'q1', 'i1', + 'p2', + 'q2', 'i2', 'voltage_level1_id', 'voltage_level2_id', 'bus1_id', 'bus2_id', + 'connected1', 'connected2'], + data=[['', 0.0013, 0.0092, 0, 0.0512, 1, 1, NaN, -0.8, -0.1, 0.8076, 0.8008, + 0.05486, 0.8027, 'S1VL1', 'S1VL2', 'S1VL1_0', 'S1VL2_0', True, True]]) + pd.testing.assert_frame_equal(expected, n.get_2_windings_transformers(), check_dtype=False, atol=10 ** -4) + n.update_2_windings_transformers( + pd.DataFrame(data=[[1, 9, 0, 0, 1, 1, 100, -1, -0.2, 1, + 0.04]], + columns=['r', 'x', 'g', 'b', 'rated_u1', 'rated_u2', 'rated_s', 'p1', 'q1', 'p2', 'q2'], + index=['TWT'])) + expected = pd.DataFrame(index=pd.Series(name='id', data=['TWT']), + columns=['name', 'r', 'x', 'g', 'b', 'rated_u1', 'rated_u2', 'rated_s', 'p1', 'q1', 'i1', + 'p2', + 'q2', 'i2', 'voltage_level1_id', 'voltage_level2_id', 'bus1_id', 'bus2_id', + 'connected1', 'connected2'], + data=[ + ['', 1, 9, 0, 0, 1, 1, 100, -1, -0.2, 1.02, 1, + 0.04, 1, 'S1VL1', 'S1VL2', 'S1VL1_0', 'S1VL2_0', + True, True]]) + pd.testing.assert_frame_equal(expected, n.get_2_windings_transformers(), check_dtype=False, atol=1e-2) + + +def test_shunt_compensators_per_unit(): + n = pp.network.create_four_substations_node_breaker_network() + n.per_unit = True + expected = pd.DataFrame(index=pd.Series(name='id', data=['SHUNT']), + columns=['name', 'g', 'b', 'model_type', 'max_section_count', 'section_count', + 'voltage_regulation_on', 'target_v', 'target_deadband', 'regulating_bus_id', + 'p', 'q', 'i', + 'voltage_level_id', 'bus_id', 'connected'], + data=[['', 0, -19.2, 'LINEAR', 1, 1, False, NaN, NaN, 'S1VL2_0', NaN, 19.2, NaN, 'S1VL2', + 'S1VL2_0', True]]) + pd.testing.assert_frame_equal(expected, n.get_shunt_compensators(), check_dtype=False, atol=1e-2) + + +def test_dangling_lines_per_unit(): + n = util.create_dangling_lines_network() + pp.loadflow.run_ac(n) + n.per_unit = True + + expected = pd.DataFrame(index=pd.Series(name='id', data=['DL']), + columns=['name', 'r', 'x', 'g', 'b', 'p0', 'q0', 'p', 'q', 'i', 'voltage_level_id', + 'bus_id', 'connected', 'pairing_key', 'ucte_xnode_code', 'tie_line_id'], + data=[['', 0.1, 0.01, 0.01, 0.001, 0.5, 0.3, 0.5482, 0.3029, 0.6263, 'VL', 'VL_0', + True, '', '', '']]) + dangling_lines = n.get_dangling_lines() + pd.testing.assert_frame_equal(expected, dangling_lines, check_dtype=False, atol=10 ** -4) + n.update_dangling_lines(pd.DataFrame(index=['DL'], columns=['p0', 'q0'], data=[[0.75, 0.25]])) + expected = pd.DataFrame(index=pd.Series(name='id', data=['DL']), + columns=['name', 'r', 'x', 'g', 'b', 'p0', 'q0', 'p', 'q', 'i', 'voltage_level_id', + 'bus_id', 'connected', 'pairing_key', 'ucte_xnode_code', 'tie_line_id'], + data=[['', 0.1, 0.01, 0.01, 0.001, 0.75, 0.25, 0.5482, 0.3029, 0.6263, 'VL', 'VL_0', + True, '', '', '']]) + dangling_lines = n.get_dangling_lines() + pd.testing.assert_frame_equal(expected, dangling_lines, check_dtype=False, atol=10 ** -4) + + +def test_lcc_converter_stations_per_unit(): + n = pp.network.create_four_substations_node_breaker_network() + pp.loadflow.run_ac(n) + n.per_unit = True + expected = pd.DataFrame(index=pd.Series(name='id', data=['LCC1', 'LCC2']), + columns=['name', 'power_factor', 'loss_factor', 'p', 'q', 'i', 'voltage_level_id', 'bus_id', + 'connected'], + data=[['LCC1', 0.6, 1.1, 0.8, 1.07, 1.33, 'S1VL2', 'S1VL2_0', True], + ['LCC2', 0.6, 1.1, -0.78, 1.04, 1.30, 'S3VL1', 'S3VL1_0', True]]) + pd.testing.assert_frame_equal(expected, n.get_lcc_converter_stations(), check_dtype=False, atol=1e-2) + n.update_lcc_converter_stations(pd.DataFrame(data=[[3.0, 4.0], [1.0, 2.0]], + columns=['p', 'q'], + index=['LCC1', 'LCC2'])) + expected = pd.DataFrame(index=pd.Series(name='id', data=['LCC1', 'LCC2']), + columns=['name', 'power_factor', 'loss_factor', 'p', 'q', 'i', 'voltage_level_id', 'bus_id', + 'connected'], + data=[['LCC1', 0.6, 1.1, 3.0, 4.0, 5.0, 'S1VL2', 'S1VL2_0', True], + ['LCC2', 0.6, 1.1, 1.0, 2.0, 2.24, 'S3VL1', 'S3VL1_0', True]]) + pd.testing.assert_frame_equal(expected, n.get_lcc_converter_stations(), check_dtype=False, atol=1e-2) + + +def test_vsc_converter_stations_per_unit(): + n = pp.network.create_four_substations_node_breaker_network() + n.per_unit = True + expected = pd.DataFrame.from_records( + index='id', + columns=['id', 'name', 'loss_factor', 'min_q', 'max_q', 'reactive_limits_kind', 'target_v', 'target_q', + 'voltage_regulator_on', + 'regulated_element_id', + 'p', 'q', 'i', 'voltage_level_id', 'bus_id', 'connected'], + + data=[['VSC1', 'VSC1', 1.1, NaN, NaN, 'CURVE', 1, 5, True, 'VSC1', 0.10, -5.12, 5.12, 'S1VL2', 'S1VL2_0', True], + ['VSC2', 'VSC2', 1.1, -4, 5, 'MIN_MAX', 0, 1.2, False, 'VSC2', -0.1, -1.2, 1.18, 'S2VL1', 'S2VL1_0', + True]]) + pd.testing.assert_frame_equal(expected, n.get_vsc_converter_stations(), check_dtype=False, atol=1e-2) + n.update_vsc_converter_stations(pd.DataFrame(data=[[3.0, 4.0], [1.0, 2.0]], + columns=['target_v', 'target_q'], + index=['VSC1', 'VSC2'])) + expected = pd.DataFrame.from_records( + index='id', + columns=['id', 'name', 'loss_factor', 'min_q', 'max_q', 'reactive_limits_kind', 'target_v', 'target_q', + 'voltage_regulator_on', 'regulated_element_id', + 'p', 'q', 'i', 'voltage_level_id', 'bus_id', 'connected'], + data=[['VSC1', 'VSC1', 1.1, NaN, NaN, 'CURVE', 3, 4, True, 'VSC1', 0.10, -5.12, 5.12, 'S1VL2', 'S1VL2_0', True], + ['VSC2', 'VSC2', 1.1, -4, 5, 'MIN_MAX', 1, 2, False, 'VSC2', -0.1, -1.2, 1.18, 'S2VL1', 'S2VL1_0', True]]) + pd.testing.assert_frame_equal(expected, n.get_vsc_converter_stations(), check_dtype=False, atol=1e-2) + + +def test_get_static_var_compensators_per_unit(): + n = pp.network.create_four_substations_node_breaker_network() + pp.loadflow.run_ac(n) + n.per_unit = True + expected = pd.DataFrame.from_records( + index='id', + columns=['id', 'name', 'b_min', 'b_max', 'target_v', 'target_q', 'regulation_mode', 'regulated_element_id', + 'p', 'q', 'i', 'voltage_level_id', 'bus_id', 'connected'], + data=[['SVC', '', -0.05, 0.05, 1.0, NaN, 'VOLTAGE', 'SVC', 0, -0.13, 0.13, 'S4VL1', 'S4VL1_0', True]]) + pd.testing.assert_frame_equal(expected, n.get_static_var_compensators(), check_dtype=False, atol=1e-2) + + n.update_static_var_compensators(pd.DataFrame.from_records( + index=['id'], columns=['id', 'target_v', 'target_q'], + data=[('SVC', 3.0, 4.0)])) + + expected = pd.DataFrame.from_records( + index='id', + columns=['id', 'name', 'b_min', 'b_max', 'target_v', 'target_q', 'regulation_mode', 'regulated_element_id', + 'p', 'q', 'i', 'voltage_level_id', 'bus_id', 'connected'], + data=[['SVC', '', -0.05, 0.05, 3, 4, 'VOLTAGE', 'SVC', 0, -0.13, 0.13, 'S4VL1', 'S4VL1_0', True]]) + pd.testing.assert_frame_equal(expected, n.get_static_var_compensators(), check_dtype=False, atol=1e-2) + + +def test_voltage_level_per_unit(): + n = pp.network.create_four_substations_node_breaker_network() + n.per_unit = True + expected = pd.DataFrame(index=pd.Series(name='id', data=['S1VL1', 'S1VL2', 'S2VL1', 'S3VL1', 'S4VL1']), + columns=['name', 'substation_id', 'nominal_v', 'high_voltage_limit', 'low_voltage_limit'], + data=[['', 'S1', 225, 1.07, 0.98], ['', 'S1', 400, 1.1, 0.98], ['', 'S2', 400, 1.1, 0.98], + ['', 'S3', 400, 1.1, 0.98], ['', 'S4', 400, 1.1, 0.98]]) + pd.testing.assert_frame_equal(expected, n.get_voltage_levels(), check_dtype=False, atol=1e-2) + + +def test_reactive_capability_curve_points_per_unit(): + n = pp.network.create_four_substations_node_breaker_network() + n.per_unit = True + reactive_capability_curve_points = n.get_reactive_capability_curve_points() + pd.testing.assert_series_equal(reactive_capability_curve_points.loc[('GH1', 0)], + pd.Series(data={'p': 0, 'min_q': -7.69, 'max_q': 8.6}, + name=('GH1', 0)), check_dtype=False, atol=True) + pd.testing.assert_series_equal(reactive_capability_curve_points.loc[('GH1', 1)], + pd.Series(data={'p': 1, 'min_q': -8.65, 'max_q': 9.46}, + name=('GH1', 1)), check_dtype=False, atol=True) + pd.testing.assert_series_equal(reactive_capability_curve_points.loc[('GH2', 0)], + pd.Series(data={'p': 0, 'min_q': -5.57, 'max_q': 5.57}, + name=('GH2', 0)), check_dtype=False, atol=True) + pd.testing.assert_series_equal(reactive_capability_curve_points.loc[('GH2', 1)], + pd.Series(data={'p': 2, 'min_q': -5.53, 'max_q': 5.36}, + name=('GH2', 1)), check_dtype=False, atol=True) + pd.testing.assert_series_equal(reactive_capability_curve_points.loc[('GH3', 0)], + pd.Series(data={'p': 0, 'min_q': -6.81, 'max_q': 6.88}, + name=('GH3', 0)), check_dtype=False, atol=True) + pd.testing.assert_series_equal(reactive_capability_curve_points.loc[('GH3', 1)], + pd.Series(data={'p': 2, 'min_q': -6.82, 'max_q': 7.16}, + name=('GH3', 1)), check_dtype=False, atol=True) + + pd.testing.assert_series_equal(reactive_capability_curve_points.loc[('GTH1', 0)], + pd.Series(data={'p': 0, 'min_q': -0.77, 'max_q': 0.77}, + name=('GTH1', 0)), check_dtype=False, atol=True) + pd.testing.assert_series_equal(reactive_capability_curve_points.loc[('GTH1', 1)], + pd.Series(data={'p': 0, 'min_q': -0.74, 'max_q': 0.76}, + name=('GTH1', 1)), check_dtype=False, atol=True) + + pd.testing.assert_series_equal(reactive_capability_curve_points.loc[('GTH2', 0)], + pd.Series(data={'p': 0, 'min_q': -1.69, 'max_q': 2}, + name=('GTH2', 0)), check_dtype=False, atol=True) + pd.testing.assert_series_equal(reactive_capability_curve_points.loc[('GTH2', 1)], + pd.Series(data={'p': 4, 'min_q': -1.75, 'max_q': 1.76}, + name=('GTH2', 1)), check_dtype=False, atol=True) + pd.testing.assert_series_equal(reactive_capability_curve_points.loc[('VSC1', 0)], + pd.Series(data={'p': -1, 'min_q': -5.5, 'max_q': 5.7}, + name=('VSC1', 0)), check_dtype=False, atol=1e-2) + pd.testing.assert_series_equal(reactive_capability_curve_points.loc[('VSC1', 1)], + pd.Series(data={'p': 1, 'min_q': -5.5, 'max_q': 5.7}, + name=('VSC1', 1)), check_dtype=False, atol=1e-2) + + +def test_three_windings_transformer_per_unit(): + n = util.create_three_windings_transformer_network() + n.per_unit = True + expected = pd.DataFrame( + index=pd.Series(name='id', data=['3WT']), + columns=['name', 'rated_u0', 'r1', 'x1', 'g1', 'b1', 'rated_u1', 'rated_s1', 'ratio_tap_position1', + 'phase_tap_position1', 'p1', 'q1', 'i1', 'voltage_level1_id', 'bus1_id', 'connected1', 'r2', 'x2', + 'g2', 'b2', 'rated_u2', 'rated_s2', 'ratio_tap_position2', 'phase_tap_position2', 'p2', 'q2', 'i2', + 'voltage_level2_id', 'bus2_id', 'connected2', 'r3', 'x3', 'g3', 'b3', 'rated_u3', 'rated_s3', + 'ratio_tap_position3', 'phase_tap_position3', 'p3', 'q3', 'i3', 'voltage_level3_id', 'bus3_id', + 'connected3'], + data=[ + ['', 1, 0.1, 0.01, 1, 0.1, 1, NaN, -99999, -99999, NaN, NaN, NaN, 'VL_132', 'VL_132_0', True, 0.00625, + 0.000625, + 0, 0, + 1, NaN, 2, -99999, NaN, NaN, NaN, 'VL_33', 'VL_33_0', True, 0.001, 6.94444e-05, 0, 0, 1, NaN, 0, -99999, + NaN, + NaN, NaN, 'VL_11', 'VL_11_0', True]]) + pd.testing.assert_frame_equal(expected, n.get_3_windings_transformers(), check_dtype=False, + atol=1e-2) + n.update_3_windings_transformers(pd.DataFrame(data=[ + [99, 9, 0, 0, 1, 100, 1, 2, 0.5, 0.1, 99, 9, 0, 0, 1, 100, 1, 2, 0.5, 0.1, 99, 9, 0, 0, 1, 100, 1, 2, 0.5, + 0.1]], + columns=['r1', 'x1', 'g1', 'b1', 'rated_u1', 'rated_s1', + 'ratio_tap_position1', 'phase_tap_position1', 'p1', 'q1', + 'r2', 'x2', 'g2', 'b2', 'rated_u2', 'rated_s2', + 'ratio_tap_position2', 'phase_tap_position2', 'p2', 'q2', + 'r3', 'x3', 'g3', 'b3', 'rated_u3', 'rated_s3', + 'ratio_tap_position3', 'phase_tap_position3', 'p3', + 'q3'], + index=['3WT'])) + expected = pd.DataFrame( + index=pd.Series(name='id', data=['3WT']), + columns=['name', 'rated_u0', 'r1', 'x1', 'g1', 'b1', 'rated_u1', 'rated_s1', 'ratio_tap_position1', + 'phase_tap_position1', 'p1', 'q1', 'i1', 'voltage_level1_id', 'bus1_id', 'connected1', 'r2', 'x2', + 'g2', 'b2', 'rated_u2', 'rated_s2', 'ratio_tap_position2', 'phase_tap_position2', 'p2', 'q2', 'i2', + 'voltage_level2_id', 'bus2_id', 'connected2', 'r3', 'x3', 'g3', 'b3', 'rated_u3', 'rated_s3', + 'ratio_tap_position3', 'phase_tap_position3', 'p3', 'q3', 'i3', 'voltage_level3_id', 'bus3_id', + 'connected3'], + data=[['', 1, 99, 9, 0, 0, 1, 100, -99999, -99999, 0.5, 0.1, 0.5, 'VL_132', 'VL_132_0', True, 99, 9, 0, 0, + 1, 100, 1, -99999, 0.5, 0.1, 0.48, 'VL_33', 'VL_33_0', True, 99, 9, 0, 0, 1, 100, 1, -99999, 0.5, + 0.1, 0.48, 'VL_11', 'VL_11_0', True]]) + pd.testing.assert_frame_equal(expected, n.get_3_windings_transformers(), check_dtype=False, + atol=1e-2) + + +def test_batteries(): + n = util.create_battery_network() + n.per_unit = True + expected = pd.DataFrame(index=pd.Series(name='id', data=['BAT', 'BAT2']), + columns=['name', 'max_p', 'min_p', 'min_q', 'max_q', 'reactive_limits_kind', 'target_p', + 'target_q', + 'p', 'q', 'i', 'voltage_level_id', 'bus_id', 'connected'], + data=[['', 100, -100, -100, 100, 'MIN_MAX', 100, 100, -6.05, -2.25, NaN, 'VLBAT', 'VLBAT_0', + True], + ['', 2, -2, NaN, NaN, 'CURVE', 1, 2, -6.05, -2.25, NaN, 'VLBAT', 'VLBAT_0', True]]) + pd.testing.assert_frame_equal(expected, n.get_batteries(), check_dtype=False, atol=1e-2) + n.update_batteries(pd.DataFrame(data=[[50, -50, 50, 50, -7, -3]], + columns=['max_p', 'min_p', 'target_p', 'target_q', 'p', 'q'], + index=['BAT'])) + expected = pd.DataFrame(index=pd.Series(name='id', data=['BAT', 'BAT2']), + columns=['name', 'max_p', 'min_p', 'min_q', 'max_q', 'reactive_limits_kind', 'target_p', + 'target_q', + 'p', 'q', 'i', 'voltage_level_id', 'bus_id', 'connected'], + data=[['', 50, -50, -100, 100, 'MIN_MAX', 50, 50, -7, -3, NaN, 'VLBAT', 'VLBAT_0', True], + ['', 2, -2, NaN, NaN, 'CURVE', 1, 2, -6.05, -2.25, NaN, 'VLBAT', 'VLBAT_0', True]]) + pd.testing.assert_frame_equal(expected, n.get_batteries(), check_dtype=False, atol=1e-2) + + +def test_ratio_tap_changers_per_unit(): + n = pp.network.create_eurostag_tutorial_example1_network() + n.per_unit = True + expected = pd.DataFrame(index=pd.Series(name='id', data=['NHV2_NLOAD']), + columns=['tap', 'low_tap', 'high_tap', 'step_count', 'on_load', 'regulating', + 'target_v', 'target_deadband', 'regulating_bus_id', 'rho', + 'alpha'], + data=[[1, 0, 2, 3, True, True, 1.05, 0.0, 'VLLOAD_0', 1.00, NaN]]) + pd.testing.assert_frame_equal(expected, n.get_ratio_tap_changers(), check_dtype=False, atol=1e-2) + + +def test_lines_not_same_nominal_voltage_per_unit(): + n = pp.network.create_ieee14() + n.per_unit = True + lines = n.get_lines() + assert lines.loc['L7-8-1']['r'] == 0 + assert lines.loc['L7-8-1']['x'] == pytest.approx(0.17615, rel=1e-5) + assert lines.loc['L7-8-1']['g1'] == pytest.approx(0, rel=1e-16) + assert lines.loc['L7-8-1']['g2'] == pytest.approx(0, rel=1e-16) + assert lines.loc['L7-8-1']['b1'] == pytest.approx(0, rel=1e-16) + assert lines.loc['L7-8-1']['b2'] == pytest.approx(0, rel=1e-16) + + +def test_bus_per_unit(): + n = pp.network.create_eurostag_tutorial_example1_network() + pp.loadflow.run_ac(n) + n.per_unit = True + buses = n.get_buses() + expected = pd.DataFrame(index=pd.Series(name='id', data=['VLGEN_0', 'VLHV1_0', 'VLHV2_0', 'VLLOAD_0']), + columns=['name', 'v_mag', 'v_angle', 'connected_component', 'synchronous_component', + 'voltage_level_id'], + data=[['', 1.02, 0.04059612739187691, 0, 0, 'VLGEN'], + ['', 1.06, 0.0, 0, 0, 'VLHV1'], + ['', 1.03, -0.06119749366730875, 0, 0, 'VLHV2'], + ['', 0.98, -0.16780443393265765, 0, 0, 'VLLOAD']]) + pd.testing.assert_frame_equal(expected, buses, check_dtype=False, atol=1e-2) + n.update_buses(id='VLGEN_0', v_mag=1, v_angle=0.1) + buses = n.get_buses() + expected = pd.DataFrame(index=pd.Series(name='id', data=['VLGEN_0', 'VLHV1_0', 'VLHV2_0', 'VLLOAD_0']), + columns=['name', 'v_mag', 'v_angle', 'connected_component', 'synchronous_component', + 'voltage_level_id'], + data=[['', 1, 0.1, 0, 0, 'VLGEN'], + ['', 1.06, 0, 0, 0, 'VLHV1'], + ['', 1.03, -0.06119749366730875, 0, 0, 'VLHV2'], + ['', 0.98, -0.16780443393265765, 0, 0, 'VLLOAD']]) + pd.testing.assert_frame_equal(expected, buses, check_dtype=False, atol=1e-2) + + +def test_loads_per_unit(): + n = pp.network.create_eurostag_tutorial_example1_network() + pp.loadflow.run_ac(n) + n.per_unit = True + expected = pd.DataFrame(index=pd.Series(name='id', data=['LOAD']), + columns=['name', 'type', 'p0', 'q0', 'p', 'q', 'i', 'voltage_level_id', 'bus_id', + 'connected'], + data=[['', 'UNDEFINED', 6, 2, 6, 2, 6.43, 'VLLOAD', 'VLLOAD_0', True]]) + pd.testing.assert_frame_equal(expected, n.get_loads(), check_dtype=False, atol=1e-2) + n.update_loads(pd.DataFrame(data=[[5, 3, False]], columns=['p0', 'q0', 'connected'], index=['LOAD'])) + expected = pd.DataFrame(index=pd.Series(name='id', data=['LOAD']), + columns=['name', 'type', 'p0', 'q0', 'p', 'q', 'i', 'voltage_level_id', 'bus_id', + 'connected'], + data=[['', 'UNDEFINED', 5, 3, 6, 2, NaN, 'VLLOAD', '', False]]) + pd.testing.assert_frame_equal(expected, n.get_loads(), check_dtype=False, atol=1e-2) + + +def test_busbar_per_unit(): + n = pp.network.create_four_substations_node_breaker_network() + pp.loadflow.run_ac(n) + n.per_unit = True + expected = pd.DataFrame(index=pd.Series(name='id', + data=['S1VL1_BBS', 'S1VL2_BBS1', 'S1VL2_BBS2', 'S2VL1_BBS', 'S3VL1_BBS', + 'S4VL1_BBS']), + columns=['name', 'v', 'angle', 'voltage_level_id', 'bus_id', 'connected'], + data=[['S1VL1_BBS', 1.00, 0.04193194938608592, 'S1VL1', 'S1VL1_0', True], + ['S1VL2_BBS1', 1, 0, 'S1VL2', 'S1VL2_0', True], + ['S1VL2_BBS2', 1, 0, 'S1VL2', 'S1VL2_0', True], + ['S2VL1_BBS', 1.02, 0.01282301263193765, 'S2VL1', 'S2VL1_0', True], + ['S3VL1_BBS', 1, 0, 'S3VL1', 'S3VL1_0', True], + ['S4VL1_BBS', 1, -0.019651423679619508, 'S4VL1', 'S4VL1_0', True]]) + pd.testing.assert_frame_equal(expected, n.get_busbar_sections(), check_dtype=False, atol=1e-2) + + +def test_hvdc_per_unit(): + n = pp.network.create_four_substations_node_breaker_network() + pp.loadflow.run_ac(n) + n.per_unit = True + expected = pd.DataFrame.from_records( + index='id', + columns=['id', 'name', 'converters_mode', 'target_p', 'max_p', 'nominal_v', 'r', + 'converter_station1_id', 'converter_station2_id', 'connected1', 'connected2'], + data=[('HVDC1', 'HVDC1', 'SIDE_1_RECTIFIER_SIDE_2_INVERTER', 0.1, 3, 400, 0, 'VSC1', 'VSC2', True, True), + ('HVDC2', 'HVDC2', 'SIDE_1_RECTIFIER_SIDE_2_INVERTER', 0.8, 3, 400, 0, 'LCC1', 'LCC2', True, True)]) + + pd.testing.assert_frame_equal(expected, n.get_hvdc_lines(), check_dtype=False, atol=1e-2) + n.update_hvdc_lines(id='HVDC1', target_p=[0.11]) + expected = pd.DataFrame.from_records( + index='id', + columns=['id', 'name', 'converters_mode', 'target_p', 'max_p', 'nominal_v', 'r', + 'converter_station1_id', 'converter_station2_id', 'connected1', 'connected2'], + data=[('HVDC1', 'HVDC1', 'SIDE_1_RECTIFIER_SIDE_2_INVERTER', 0.11, 3, 400, 0, 'VSC1', 'VSC2', True, True), + ('HVDC2', 'HVDC2', 'SIDE_1_RECTIFIER_SIDE_2_INVERTER', 0.8, 3, 400, 0, 'LCC1', 'LCC2', True, True)]) + pd.testing.assert_frame_equal(expected, n.get_hvdc_lines(), check_dtype=False, atol=1e-2) + + +def test_ratio_tap_changer_steps_per_unit(): + n = pp.network.create_eurostag_tutorial_example1_network() + n.per_unit = True + steps = n.get_ratio_tap_changer_steps() + assert steps.loc['NHV2_NLOAD', 0]['rho'] == pytest.approx(2.15, rel=1e-1) + assert steps.loc['NHV2_NLOAD', 1]['rho'] == pytest.approx(2.54, rel=1e-1) + assert steps.loc['NHV2_NLOAD', 2]['rho'] == pytest.approx(2.92, rel=1e-1) + n.update_ratio_tap_changer_steps(id='NHV2_NLOAD', position=0, r=1, x=3, g=4, b=2) + steps = n.get_ratio_tap_changer_steps() + assert steps.loc['NHV2_NLOAD', 0]['rho'] == pytest.approx(2.15, rel=1e-1) + assert steps.loc['NHV2_NLOAD', 0]['r'] == pytest.approx(1, rel=1e-1) + assert steps.loc['NHV2_NLOAD', 0]['x'] == pytest.approx(3, rel=1e-1) + assert steps.loc['NHV2_NLOAD', 0]['g'] == pytest.approx(4, rel=1e-1) + assert steps.loc['NHV2_NLOAD', 0]['b'] == pytest.approx(2, rel=1e-1) + + +def test_phase_tap_changer_steps_per_unit(): + n = pp.network.create_ieee300() + n.per_unit = True + steps = n.get_phase_tap_changer_steps() + assert steps.loc['T196-2040-1', 0]['rho'] == 1.0 + assert steps.loc['T196-2040-1', 0]['alpha'] == pytest.approx(0.2, rel=1e-1) + assert steps.loc['T196-2040-1', 0]['r'] == 0.0 + assert steps.loc['T196-2040-1', 0]['x'] == 0.0 + assert steps.loc['T196-2040-1', 0]['g'] == 0.0 + assert steps.loc['T196-2040-1', 0]['b'] == 0.0 + n.update_phase_tap_changer_steps(id='T196-2040-1', position=0, r=1, x=3, g=4, b=2) + steps = n.get_phase_tap_changer_steps() + assert steps.loc['T196-2040-1', 0]['rho'] == 1.0 + assert steps.loc['T196-2040-1', 0]['alpha'] == pytest.approx(0.2, rel=1e-1) + assert steps.loc['T196-2040-1', 0]['r'] == 1 + assert steps.loc['T196-2040-1', 0]['x'] == 3 + assert steps.loc['T196-2040-1', 0]['g'] == 4 + assert steps.loc['T196-2040-1', 0]['b'] == 2 diff --git a/tests/test_network.py b/tests/test_network.py index b615e80e44..e5db820231 100644 --- a/tests/test_network.py +++ b/tests/test_network.py @@ -1828,7 +1828,7 @@ def test_ratio_tap_changer_regulated_side(): assert not tap_changer.regulating assert tap_changer.regulated_side == '' - with pytest.raises(pp.PyPowsyblError, match='a regulation terminal has to be set'): + with pytest.raises(pp.PyPowsyblError, match='terminal not found for un per unit V'): n.update_ratio_tap_changers(id='T4-1-0', target_v=100, regulating=True) with pytest.raises(pp.PyPowsyblError, match='must be ONE or TWO'): diff --git a/tests/test_per_unit.py b/tests/test_per_unit.py index c92f0adb4d..850cab9ae3 100644 --- a/tests/test_per_unit.py +++ b/tests/test_per_unit.py @@ -412,6 +412,7 @@ def test_ratio_tap_changers_per_unit(): data=[[1, 0, 2, 3, True, True, 158.0, 0.0, 'VLLOAD_0', 1.00, NaN]]) pd.testing.assert_frame_equal(expected, n.get_ratio_tap_changers(), check_dtype=False, atol=1e-2) + def test_lines_not_same_nominal_voltage_per_unit(): n = pp.network.create_ieee14() n = per_unit_view(n, 100) @@ -422,3 +423,39 @@ def test_lines_not_same_nominal_voltage_per_unit(): assert lines.loc['L7-8-1']['g2'] == pytest.approx(0, rel=1e-16) assert lines.loc['L7-8-1']['b1'] == pytest.approx(0, rel=1e-16) assert lines.loc['L7-8-1']['b2'] == pytest.approx(0, rel=1e-16) + + +def test_ratio_tap_changer_steps_per_unit(): + n = pp.network.create_eurostag_tutorial_example1_network() + n.per_unit = True + steps = n.get_ratio_tap_changer_steps() + assert steps.loc['NHV2_NLOAD', 0]['rho'] == pytest.approx(2.15, rel=1e-1) + assert steps.loc['NHV2_NLOAD', 1]['rho'] == pytest.approx(2.54, rel=1e-1) + assert steps.loc['NHV2_NLOAD', 2]['rho'] == pytest.approx(2.92, rel=1e-1) + n.update_ratio_tap_changer_steps(id='NHV2_NLOAD', position=0, r=1, x=3, g=4, b=2) + steps = n.get_ratio_tap_changer_steps() + assert steps.loc['NHV2_NLOAD', 0]['rho'] == pytest.approx(2.15, rel=1e-1) + assert steps.loc['NHV2_NLOAD', 0]['r'] == pytest.approx(1, rel=1e-1) + assert steps.loc['NHV2_NLOAD', 0]['x'] == pytest.approx(3, rel=1e-1) + assert steps.loc['NHV2_NLOAD', 0]['g'] == pytest.approx(4, rel=1e-1) + assert steps.loc['NHV2_NLOAD', 0]['b'] == pytest.approx(2, rel=1e-1) + + +def test_phase_tap_changer_steps_per_unit(): + n = pp.network.create_ieee300() + n.per_unit = True + steps = n.get_phase_tap_changer_steps() + assert steps.loc['T196-2040-1', 0]['rho'] == 1.0 + assert steps.loc['T196-2040-1', 0]['alpha'] == pytest.approx(0.2, rel=1e-1) + assert steps.loc['T196-2040-1', 0]['r'] == 0.0 + assert steps.loc['T196-2040-1', 0]['x'] == 0.0 + assert steps.loc['T196-2040-1', 0]['g'] == 0.0 + assert steps.loc['T196-2040-1', 0]['b'] == 0.0 + n.update_phase_tap_changer_steps(id='T196-2040-1', position=0, r=1, x=3, g=4, b=2) + steps = n.get_phase_tap_changer_steps() + assert steps.loc['T196-2040-1', 0]['rho'] == 1.0 + assert steps.loc['T196-2040-1', 0]['alpha'] == pytest.approx(0.2, rel=1e-1) + assert steps.loc['T196-2040-1', 0]['r'] == 1 + assert steps.loc['T196-2040-1', 0]['x'] == 3 + assert steps.loc['T196-2040-1', 0]['g'] == 4 + assert steps.loc['T196-2040-1', 0]['b'] == 2