diff --git a/open-reac/README.md b/open-reac/README.md index 5270bc66..7db33830 100644 --- a/open-reac/README.md +++ b/open-reac/README.md @@ -81,30 +81,33 @@ The user can configure the run with the dedicated Java interface Specifically, the user can set various parameters and thresholds used in the preprocessing and modeling of the reactive OPF. These are specified in the file `param_algo.txt`: -| Parameter | Description | Default value | Domain | -|---------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------------|-----------------------------------------------| -| `log_level_ampl` | Level of display for AMPL prints | INFO | {DEBUG, INFO, WARNING, ERROR} | -| `log_level_knitro` | Level of display for solver prints (see [AMPL documentation](https://dev.ampl.com/ampl/options.html)) | $1$ | {0, 1, 2} | -| `objective_choice` | Choice of the objective function for the ACOPF (see [7](#7-alternative-current-optimal-power-flow)) | $0$ | {0, 1, 2} | -| `ratio_voltage_target` | Ratio to calculate target V of buses when `objective_choice` is set to $1$ (see [7](#7-alternative-current-optimal-power-flow)) | $0.5$ | $\[0; 1\]$ | -| `coeff_alpha` | Weight to favor more/less minimization of active power produced by generators or deviation between them and target values (see [6.2](#62-alternative-current-optimal-power-flow)) | $1$ | $\[0; 1\]$ | -| `Pnull` | Threshold of active and reactive powers considered as null | $0.01$ (MW) | $\[0; 1\]$ | -| `Znull` | Threshold of impedance considered as null (see [4.2](#)) | $10^{-5}$ (p.u.) | $\[0; 0.1\]$ | - | `epsilon_nominal_voltage` | Threshold to ignore voltage levels with nominal voltage lower than it | $1$ (kV) | $\mathbb{R}^{+}$ | -| `min_plausible_low_voltage_limit` | Consistency bound for low voltage limit of voltage levels (see [4.1](#41-voltage-level-limits-computation)) | $0.5$ (p.u.) | $\mathbb{R}^{+}$ | -| `max_plausible_high_voltage_limit` | Consistency bound for high voltage limit of voltage levels (see [4.1](#41-voltage-level-limits-computation)) | $1.5$ (p.u.) | [`min_plausible_low_voltage_limit`; $\infty$] | -| `ignore_voltage_bounds` | Threshold to replace voltage limits of voltage levels with nominal voltage lower than it, by [min_plausible_low_voltage_limit; max_plausible_high_voltage_limit] | $0$ (p.u.) | $\mathbb{R}^{+}$ | -| `buses_with_reactive_slacks` | Choice of which buses will have reactive slacks attached in ACOPF solving (see [7](#7-alternative-current-optimal-power-flow)) | ALL | {CONFIGURED, NO_GENERATION, ALL} | -| `PQmax` | Threshold for maximum active and reactive power considered in correction of generator limits (see [4.4](#44-pq-units-domain)) | $9000$ (MW, MVAr) | $\mathbb{R}$ | -| `defaultPmax` | Threshold for correction of high active power limit produced by generators (see [4.4](#44-pq-units-domain)) | $1000$ (MW) | $\mathbb{R}$ | -| `defaultPmin` | Threshold for correction of low active power limit produced by generators (see [4.4](#44-pq-units-domain)) | $0$ (MW) | $\mathbb{R}$ | -| `defaultQmaxPmaxRatio` | Ratio used to calculate threshold for corrections of high/low reactive power limits (see [4.4](#44-pq-units-domain)) | $0.3$ (MVAr/MW) | $\mathbb{R}$ | -| `minimalQPrange` | Threshold to fix active (resp. reactive) power of generators with active (resp. reactive) power limits that are closer than it (see [4.4](#44-pq-units-domain)) | $1$ (MW, MVAr) | $\mathbb{R}$ | -| `default_variable_scaling_factor` | Default scaling factor applied to all the variables (except reactive slacks and transformer ratios) before ACOPF solving | $1$ | $\mathbb{R}^{*,+}$ | -| `default_constraint_scaling_factor` | Default scaling factor applied to all the constraints before ACOPF solving | $1$ | $\mathbb{R}^{+}$ | -| `reactive_slack_variable_scaling_factor` | Scaling factor applied to all reactive slacks variables before ACOPF solving (see [7](#7-alternative-current-optimal-power-flow)) | $0.1$ | $\mathbb{R}^{*,+}$ | -| `transformer_ratio_variable_scaling_factor` | Scaling factor applied to all transformer ratio variables before ACOPF solving (see [7](#7-alternative-current-optimal-power-flow)) | $0.001$ | $\mathbb{R}^{*,+}$ | - +| Parameter | Description | Java default value | Domain | +|---------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------------|-----------------------------------------------| +| `log_level_ampl` | Level of display for AMPL prints | INFO | {DEBUG, INFO, WARNING, ERROR} | +| `log_level_knitro` | Level of display for solver prints (see [AMPL documentation](https://dev.ampl.com/ampl/options.html)) | $1$ | {0, 1, 2} | +| `objective_choice` | Choice of the objective function for the ACOPF (see [7](#7-alternative-current-optimal-power-flow)) | $0$ | {0, 1, 2} | +| `ratio_voltage_target` | Ratio to calculate target V of buses when `objective_choice` is set to $1$ (see [7](#7-alternative-current-optimal-power-flow)) | $0.5$ | $\[0; 1\]$ | +| `coeff_alpha` | Weight to favor more/less minimization of active power produced by generators or deviation between them and target values (see [6.2](#62-alternative-current-optimal-power-flow)) | $1$ | $\[0; 1\]$ | +| `Pnull` | Threshold of active and reactive powers considered as null | $0.01$ (MW) | $\[0; 1\]$ | +| `Znull` | Threshold of impedance considered as null (see [4.2](#)) | $10^{-5}$ (p.u.) | $\[0; 0.1\]$ | + | `epsilon_nominal_voltage` | Threshold to ignore voltage levels with nominal voltage lower than it | $1$ (kV) | $\mathbb{R}^{+}$ | +| `min_plausible_low_voltage_limit` | Consistency bound for low voltage limit of voltage levels (see [4.1](#41-voltage-level-limits-computation)) | $0.5$ (p.u.) | $\mathbb{R}^{+}$ | +| `max_plausible_high_voltage_limit` | Consistency bound for high voltage limit of voltage levels (see [4.1](#41-voltage-level-limits-computation)) | $1.5$ (p.u.) | [`min_plausible_low_voltage_limit`; $\infty$] | +| `ignore_voltage_bounds` | Threshold to replace voltage limits of voltage levels with nominal voltage lower than it, by [min_plausible_low_voltage_limit; max_plausible_high_voltage_limit] | $0$ (p.u.) | $\mathbb{R}^{+}$ | +| `buses_with_reactive_slacks` | Choice of which buses will have reactive slacks attached in ACOPF solving (see [7](#7-alternative-current-optimal-power-flow)) | ALL | {CONFIGURED, NO_GENERATION, ALL} | +| `PQmax` | Threshold for maximum active and reactive power considered in correction of generator limits (see [4.4](#44-pq-units-domain)) | $9000$ (MW, MVAr) | $\mathbb{R}$ | +| `defaultPmax` | Threshold for correction of high active power limit produced by generators (see [4.4](#44-pq-units-domain)) | $1000$ (MW) | $\mathbb{R}$ | +| `defaultPmin` | Threshold for correction of low active power limit produced by generators (see [4.4](#44-pq-units-domain)) | $0$ (MW) | $\mathbb{R}$ | +| `defaultQmaxPmaxRatio` | Ratio used to calculate threshold for corrections of high/low reactive power limits (see [4.4](#44-pq-units-domain)) | $0.3$ (MVAr/MW) | $\mathbb{R}$ | +| `minimalQPrange` | Threshold to fix active (resp. reactive) power of generators with active (resp. reactive) power limits that are closer than it (see [4.4](#44-pq-units-domain)) | $1$ (MW, MVAr) | $\mathbb{R}$ | +| `default_variable_scaling_factor` | Default scaling factor applied to all the variables (except reactive slacks and transformer ratios) before ACOPF solving | $1$ | $\mathbb{R}^{*,+}$ | +| `default_constraint_scaling_factor` | Default scaling factor applied to all the constraints before ACOPF solving | $1$ | $\mathbb{R}^{+}$ | +| `reactive_slack_variable_scaling_factor` | Scaling factor applied to all reactive slacks variables before ACOPF solving (see [7](#7-alternative-current-optimal-power-flow)) | $0.1$ | $\mathbb{R}^{*,+}$ | +| `transformer_ratio_variable_scaling_factor` | Scaling factor applied to all transformer ratio variables before ACOPF solving (see [7](#7-alternative-current-optimal-power-flow)) | $0.001$ | $\mathbb{R}^{*,+}$ | + + +Please note that for these parameters, the AMPL code defines default values which may be different from those in Java (for example, for the +scaling values). This allows a user to use the AMPL code without going through the Java interface, and without providing the file `param_algo.txt`. In addition to the previous parameters, the user can specify which parameters will be variable or fixed in the ACOPF solving (see [7](#7-alternative-current-optimal-power-flow)). @@ -119,6 +122,9 @@ This is done using the following files: All of these files share the same format: 2 columns #"num" "id". +Once again, the user can directly execute the AMPL code without passing these parameters files as input. +If so, empty files will be created during execution. + #### 3.3 New voltage limits In addition to the elements specified in section [3.2](#32-configuration-of-the-run), the user may choose to override diff --git a/open-reac/src/main/java/com/powsybl/openreac/parameters/output/network/ShuntCompensatorNetworkOutput.java b/open-reac/src/main/java/com/powsybl/openreac/parameters/output/network/ShuntCompensatorNetworkOutput.java index 9f11e018..43429d90 100644 --- a/open-reac/src/main/java/com/powsybl/openreac/parameters/output/network/ShuntCompensatorNetworkOutput.java +++ b/open-reac/src/main/java/com/powsybl/openreac/parameters/output/network/ShuntCompensatorNetworkOutput.java @@ -6,19 +6,25 @@ */ package com.powsybl.openreac.parameters.output.network; +import com.powsybl.ampl.converter.AmplConstants; import com.powsybl.ampl.converter.AmplSubset; import com.powsybl.commons.util.StringToIntMapper; import com.powsybl.iidm.modification.ShuntCompensatorModification; import com.powsybl.iidm.network.Network; import com.powsybl.iidm.network.ShuntCompensator; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.util.ArrayList; import java.util.List; +import java.util.Objects; /** * @author Nicolas Pierre {@literal } */ public class ShuntCompensatorNetworkOutput extends AbstractNetworkOutput { + + private static final Logger LOGGER = LoggerFactory.getLogger(ShuntCompensatorNetworkOutput.class); private static final String ELEMENT = "shunts"; public static final int EXPECTED_COLS = 6; private static final int ID_COLUMN_INDEX = 1; @@ -47,15 +53,18 @@ public int getExpectedColumns() { @Override protected void readLine(String[] tokens, StringToIntMapper stringToIntMapper) { String id = stringToIntMapper.getId(AmplSubset.SHUNT, Integer.parseInt(tokens[ID_COLUMN_INDEX])); - double b = readDouble(tokens[B_COLUMN_INDEX]); - String busId = stringToIntMapper.getId(AmplSubset.BUS, Integer.parseInt(tokens[BUS_COLUMN_INDEX])); - Boolean reconnect = null; ShuntCompensator shuntCompensator = network.getShuntCompensator(id); - if (busId != null && shuntCompensator != null && busId.equals( - shuntCompensator.getTerminal().getBusView().getConnectableBus().getId())) { - reconnect = true; + if (!Objects.isNull(shuntCompensator)) { + double b = readDouble(tokens[B_COLUMN_INDEX]) * AmplConstants.SB / Math.pow(shuntCompensator.getTerminal().getVoltageLevel().getNominalV(), 2); + String busId = stringToIntMapper.getId(AmplSubset.BUS, Integer.parseInt(tokens[BUS_COLUMN_INDEX])); + Boolean reconnect = null; + if (busId != null && busId.equals(shuntCompensator.getTerminal().getBusView().getConnectableBus().getId())) { + reconnect = true; + } + modifications.add(new ShuntCompensatorModification(id, reconnect, findSectionCount(shuntCompensator, b))); + } else { + LOGGER.warn("Shunt compensator with id {} not found in the network", id); } - modifications.add(new ShuntCompensatorModification(id, reconnect, shuntCompensator == null ? null : findSectionCount(shuntCompensator, b))); } /** diff --git a/open-reac/src/main/resources/openreac/reactiveopf.run b/open-reac/src/main/resources/openreac/reactiveopf.run index 4279b371..f69be358 100644 --- a/open-reac/src/main/resources/openreac/reactiveopf.run +++ b/open-reac/src/main/resources/openreac/reactiveopf.run @@ -364,12 +364,12 @@ if "default_constraint_scaling_factor" in PARAM_ALGO_KEYS then let default_const printf{LOG_INFO} "Parameter: default scaling factor for constraints := %.3f\n",default_constraint_scaling_factor; check default_constraint_scaling_factor >= 0; -param reactive_slack_variable_scaling_factor default 1e-1; +param reactive_slack_variable_scaling_factor default 1; if "reactive_slack_variable_scaling_factor" in PARAM_ALGO_KEYS then let reactive_slack_variable_scaling_factor := num(PARAM_ALGO_VALUES["reactive_slack_variable_scaling_factor"]); printf{LOG_INFO} "Parameter: scaling factor for reactive slack variables := %.3f\n",reactive_slack_variable_scaling_factor; check reactive_slack_variable_scaling_factor > 0; -param transformer_ratio_variable_scaling_factor default 1e-1; +param transformer_ratio_variable_scaling_factor default 1; if "transformer_ratio_variable_scaling_factor" in PARAM_ALGO_KEYS then let transformer_ratio_variable_scaling_factor := num(PARAM_ALGO_VALUES["transformer_ratio_variable_scaling_factor"]); printf{LOG_INFO} "Parameter: scaling factor for tranformer ratio variables := %.3f\n",transformer_ratio_variable_scaling_factor; check transformer_ratio_variable_scaling_factor > 0; diff --git a/open-reac/src/test/java/com/powsybl/openreac/OpenReacRunnerTest.java b/open-reac/src/test/java/com/powsybl/openreac/OpenReacRunnerTest.java index c7754656..c5d612da 100644 --- a/open-reac/src/test/java/com/powsybl/openreac/OpenReacRunnerTest.java +++ b/open-reac/src/test/java/com/powsybl/openreac/OpenReacRunnerTest.java @@ -323,8 +323,8 @@ void testShunt() throws IOException { values = reportShunts.getChildren().get(1).getValues(); assertEquals("SHUNT", values.get("shuntCompensatorId").toString()); assertEquals("25", values.get("maxSectionCount").toString()); - assertEquals("2080.0", values.get("discretizedValue").toString()); - assertEquals("2128.0", values.get("optimalValue").toString()); + assertEquals("160.0", values.get("discretizedValue").toString()); + assertEquals("123.4", values.get("optimalValue").toString()); assertEquals(TypedValue.TRACE_SEVERITY.getValue(), values.get("reportSeverity").toString()); assertTrue(shunt.getTerminal().isConnected()); // shunt has been reconnected diff --git a/open-reac/src/test/java/com/powsybl/openreac/parameters/output/ShuntCompensatorNetworkOutputTest.java b/open-reac/src/test/java/com/powsybl/openreac/parameters/output/ShuntCompensatorNetworkOutputTest.java new file mode 100644 index 00000000..fdc33f8b --- /dev/null +++ b/open-reac/src/test/java/com/powsybl/openreac/parameters/output/ShuntCompensatorNetworkOutputTest.java @@ -0,0 +1,90 @@ +/** + * 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.openreac.parameters.output; + +import com.powsybl.ampl.converter.AmplSubset; +import com.powsybl.commons.util.StringToIntMapper; +import com.powsybl.iidm.network.Network; +import com.powsybl.openreac.parameters.output.network.ShuntCompensatorNetworkOutput; +import org.junit.jupiter.api.Test; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; + +import static com.powsybl.openreac.network.ShuntNetworkFactory.create; +import static org.junit.jupiter.api.Assertions.*; + +/** + * @author Pierre Arvy {@literal } + */ +class ShuntCompensatorNetworkOutputTest { + @Test + void read() throws IOException { + Network network = create(); + ShuntCompensatorNetworkOutput output = new ShuntCompensatorNetworkOutput(network, 0); + StringToIntMapper mapper = new StringToIntMapper<>(AmplSubset.class); + mapper.newInt(AmplSubset.SHUNT, "SHUNT"); + for (int i = 0; i < 7; i++) { + mapper.newInt(AmplSubset.BUS, "BUS" + i); + } + try (InputStream input = getClass().getResourceAsStream("/mock_outputs/reactiveopf_results_shunts.csv"); + InputStreamReader in = new InputStreamReader(input); + BufferedReader reader = new BufferedReader(in)) { + output.read(reader, mapper); + assertEquals(1, output.getModifications().size()); + assertEquals("SHUNT", output.getModifications().get(0).getShuntCompensatorId()); + assertNull(output.getModifications().get(0).getConnect()); + assertEquals(0, output.getModifications().get(0).getSectionCount()); + } + } + + @Test + void readNullShuntCompensator() throws IOException { + Network network = create(); + ShuntCompensatorNetworkOutput output = new ShuntCompensatorNetworkOutput(network, 0); + StringToIntMapper mapper = new StringToIntMapper<>(AmplSubset.class); + mapper.newInt(AmplSubset.SHUNT, "wrongId"); + for (int i = 0; i < 7; i++) { + mapper.newInt(AmplSubset.BUS, "BUS" + i); + } + try (InputStream input = getClass().getResourceAsStream("/mock_outputs/reactiveopf_results_shunts.csv"); + InputStreamReader in = new InputStreamReader(input); + BufferedReader reader = new BufferedReader(in)) { + output.read(reader, mapper); + assertEquals(0, output.getModifications().size()); + } + } + + @Test + void noShuntNumberInMapper() throws IOException { + Network network = create(); + ShuntCompensatorNetworkOutput output = new ShuntCompensatorNetworkOutput(network, 0); + StringToIntMapper mapper = new StringToIntMapper<>(AmplSubset.class); + try (InputStream input = getClass().getResourceAsStream("/mock_outputs/reactiveopf_results_shunts.csv"); + InputStreamReader in = new InputStreamReader(input); + BufferedReader reader = new BufferedReader(in)) { + assertThrows(IllegalArgumentException.class, () -> output.read(reader, mapper)); + } + } + + @Test + void noBusNumberInMapper() throws IOException { + Network network = create(); + ShuntCompensatorNetworkOutput output = new ShuntCompensatorNetworkOutput(network, 0); + StringToIntMapper mapper = new StringToIntMapper<>(AmplSubset.class); + mapper.newInt(AmplSubset.SHUNT, "SHUNT"); + try (InputStream input = getClass().getResourceAsStream("/mock_outputs/reactiveopf_results_shunts.csv"); + InputStreamReader in = new InputStreamReader(input); + BufferedReader reader = new BufferedReader(in)) { + assertThrows(IllegalArgumentException.class, () -> output.read(reader, mapper)); + } + } + +} diff --git a/open-reac/src/test/resources/openreac-output-shunt/reactiveopf_results_shunts.csv b/open-reac/src/test/resources/openreac-output-shunt/reactiveopf_results_shunts.csv index 50d1eac3..7b7a196a 100644 --- a/open-reac/src/test/resources/openreac-output-shunt/reactiveopf_results_shunts.csv +++ b/open-reac/src/test/resources/openreac-output-shunt/reactiveopf_results_shunts.csv @@ -1,2 +1,2 @@ #variant;num;bus;b(pu);Q(Mvar);section; -1;1;3;0.0133;136.453;0; +1;1;3;1.234;136.453;0;