diff --git a/java/pypowsybl/src/main/java/com/powsybl/dataframe/network/NetworkDataframes.java b/java/pypowsybl/src/main/java/com/powsybl/dataframe/network/NetworkDataframes.java index 3cd08bd02..5a47c116c 100644 --- a/java/pypowsybl/src/main/java/com/powsybl/dataframe/network/NetworkDataframes.java +++ b/java/pypowsybl/src/main/java/com/powsybl/dataframe/network/NetworkDataframes.java @@ -45,6 +45,10 @@ public final class NetworkDataframes { private static final Map EXTENSIONS_MAPPERS = NetworkExtensions.createExtensionsMappers(); private static final String DEFAULT_OPERATIONAL_LIMIT_GROUP_ID = "DEFAULT"; + private static final String MIN_Q_AT_TARGET_P = "min_q_at_target_p"; + private static final String MAX_Q_AT_TARGET_P = "max_q_at_target_p"; + private static final String MIN_Q_AT_P = "min_q_at_p"; + private static final String MAX_Q_AT_P = "max_q_at_p"; private NetworkDataframes() { } @@ -156,7 +160,7 @@ static > Function getVoltageLevelId() { private static MinMaxReactiveLimits getMinMaxReactiveLimits(ReactiveLimitsHolder holder) { ReactiveLimits reactiveLimits = holder.getReactiveLimits(); - return reactiveLimits instanceof MinMaxReactiveLimits ? (MinMaxReactiveLimits) reactiveLimits : null; + return reactiveLimits instanceof MinMaxReactiveLimits minMaxReactiveLimits ? minMaxReactiveLimits : null; } static ToDoubleBiFunction getPerUnitMinQ(ToDoubleFunction pGetter) { @@ -280,10 +284,10 @@ static NetworkDataframeMapper generators() { 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(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", (g, context) -> perUnitTargetV(context, g.getTargetV(), g.getRegulatingTerminal(), g.getTerminal()), @@ -377,6 +381,10 @@ static NetworkDataframeMapper batteries() { setPerUnitMinQ()) .doubles("max_q", ifExistsDoublePerUnitPQ(NetworkDataframes::getMinMaxReactiveLimits, MinMaxReactiveLimits::getMaxQ), setPerUnitMaxQ()) + .doubles(MIN_Q_AT_TARGET_P, getPerUnitMinQ(Battery::getTargetP), false) + .doubles(MAX_Q_AT_TARGET_P, getPerUnitMaxQ(Battery::getTargetP), false) + .doubles(MIN_Q_AT_P, getPerUnitMinQ(getOppositeP()), false) + .doubles(MAX_Q_AT_P, getPerUnitMaxQ(getOppositeP()), false) .strings("reactive_limits_kind", NetworkDataframes::getReactiveLimitsKind) .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))) @@ -745,8 +753,10 @@ static NetworkDataframeMapper vscs() { .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) + .doubles(MIN_Q_AT_TARGET_P, getPerUnitMinQ(vsc -> vsc.getHvdcLine().getActivePowerSetpoint()), false) + .doubles(MAX_Q_AT_TARGET_P, getPerUnitMaxQ(vsc -> vsc.getHvdcLine().getActivePowerSetpoint()), false) + .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", (vsc, context) -> perUnitTargetV(context, vsc.getVoltageSetpoint(), vsc.getRegulatingTerminal(), vsc.getTerminal()), (vsc, targetV, context) -> vsc.setVoltageSetpoint(unPerUnitTargetV(context, targetV, vsc.getRegulatingTerminal(), vsc.getTerminal()))) diff --git a/java/pypowsybl/src/test/java/com/powsybl/dataframe/network/NetworkDataframesTest.java b/java/pypowsybl/src/test/java/com/powsybl/dataframe/network/NetworkDataframesTest.java index c84ade41b..e112dfd4f 100644 --- a/java/pypowsybl/src/test/java/com/powsybl/dataframe/network/NetworkDataframesTest.java +++ b/java/pypowsybl/src/test/java/com/powsybl/dataframe/network/NetworkDataframesTest.java @@ -299,8 +299,9 @@ void batteries() { List allAttributeSeries = createDataFrame(BATTERY, network, new DataframeFilter(ALL_ATTRIBUTES, Collections.emptyList())); assertThat(allAttributeSeries) .extracting(Series::getName) - .containsExactly("id", "name", "max_p", "min_p", "min_q", "max_q", "reactive_limits_kind", "target_p", "target_q", "p", "q", "i", "voltage_level_id", - "bus_id", "bus_breaker_bus_id", "node", "connected", "fictitious"); + .containsExactly("id", "name", "max_p", "min_p", "min_q", "max_q", "min_q_at_target_p", + "max_q_at_target_p", "min_q_at_p", "max_q_at_p", "reactive_limits_kind", "target_p", "target_q", + "p", "q", "i", "voltage_level_id", "bus_id", "bus_breaker_bus_id", "node", "connected", "fictitious"); } @Test @@ -424,7 +425,8 @@ void vscs() { List allAttributeSeries = createDataFrame(VSC_CONVERTER_STATION, network, new DataframeFilter(ALL_ATTRIBUTES, Collections.emptyList())); assertThat(allAttributeSeries) .extracting(Series::getName) - .containsExactly("id", "name", "loss_factor", "min_q", "max_q", "min_q_at_p", "max_q_at_p", "reactive_limits_kind", "target_v", "target_q", "voltage_regulator_on", "regulated_element_id", + .containsExactly("id", "name", "loss_factor", "min_q", "max_q", "min_q_at_target_p", "max_q_at_target_p", + "min_q_at_p", "max_q_at_p", "reactive_limits_kind", "target_v", "target_q", "voltage_regulator_on", "regulated_element_id", "p", "q", "i", "voltage_level_id", "bus_id", "bus_breaker_bus_id", "node", "connected", "fictitious"); } diff --git a/tests/test_network.py b/tests/test_network.py index f57ac4536..943446f74 100644 --- a/tests/test_network.py +++ b/tests/test_network.py @@ -2715,5 +2715,28 @@ def test_alpha_rho_transfo3(): pd.testing.assert_frame_equal(expected_transfo3, transfo3, check_dtype=False, atol=1e-6) +def test_q_min_max_at_target_p(): + network = pp.network.create_four_substations_node_breaker_network() + pp.loadflow.run_ac(network) + attributes = ['min_q', 'max_q', 'min_q_at_p', 'max_q_at_p', 'min_q_at_target_p', 'max_q_at_target_p'] + generators = network.get_generators(attributes=attributes).tail(1) + expected_generators = pd.DataFrame(index=pd.Series(name='id', data=['GTH2']), + columns=attributes, + data=[[nan, nan, -172.607678, 185.036696, -172.594302, 185.097207]]) + pd.testing.assert_frame_equal(expected_generators, generators, check_dtype=False) + vscs = network.get_vsc_converter_stations(attributes=attributes).tail(1) + expected_vscs = pd.DataFrame(index=pd.Series(name='id', data=['VSC2']), + columns=attributes, + data=[[-400.0, 500.0, -400.0, 500.0, -400.0, 500.0]]) + pd.testing.assert_frame_equal(expected_vscs, vscs, check_dtype=False) + + network_with_batteries = pp.network.load(TEST_DIR.joinpath('battery.xiidm')) + batteries = network_with_batteries.get_batteries(attributes=attributes).head(1) + expected_batteries = pd.DataFrame(index=pd.Series(name='id', data=['BAT']), + columns=attributes, + data=[[-9999.99, 9999.99, -9999.99, 9999.99, -9999.99, 9999.99]]) + pd.testing.assert_frame_equal(expected_batteries, batteries, check_dtype=False) + + if __name__ == '__main__': unittest.main()