Skip to content

Commit

Permalink
Export buses voltage and update targets V (#53)
Browse files Browse the repository at this point in the history
* Refactor output classes.
* Add TU for wrong number of columns.
* Small renaming.
* Add export of voltage plan.
* remove useless id.
* Add TU for VoltagePlanOutput.
* update/add TU.
* Add warm-start and TU.
* Add boolean for warm start update and refactor TUs.
* Fix codesmell.
* Refactor and add TUs.
* Refactor mock files.
* Add TU for missing regulated bus.
* Add log when bus/regulation terminal is null.
* Improve coverage.
* Keep same changes but on modifiable tap changers/shunts.
* Remove restriction on target V update.
* Rework to clarify.

---------

Signed-off-by: parvy <[email protected]>
Co-authored-by: Anne Tilloy <[email protected]>
  • Loading branch information
p-arvy and annetill authored Feb 6, 2024
1 parent 63dd464 commit 4701635
Show file tree
Hide file tree
Showing 27 changed files with 617 additions and 51 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import com.powsybl.openreac.parameters.input.algo.AlgorithmInput;
import com.powsybl.openreac.parameters.output.OpenReacResult;
import com.powsybl.openreac.parameters.output.ReactiveSlackOutput;
import com.powsybl.openreac.parameters.output.VoltageProfileOutput;
import com.powsybl.openreac.parameters.output.network.NetworkModifications;

import java.util.ArrayList;
Expand All @@ -40,6 +41,7 @@ public class OpenReacAmplIOFiles implements AmplParameters {
private final VoltageLevelLimitsOverrideInput voltageLimitsOverride;
private final ConfiguredBusesWithReactiveSlack configuredReactiveSlackBuses;
private final NetworkModifications networkModifications;
private final VoltageProfileOutput voltageProfileOutput;
private final boolean debug;

public OpenReacAmplIOFiles(OpenReacParameters params, Network network, boolean debug) {
Expand All @@ -54,6 +56,7 @@ public OpenReacAmplIOFiles(OpenReacParameters params, Network network, boolean d
//outputs
this.reactiveSlackOutput = new ReactiveSlackOutput();
this.networkModifications = new NetworkModifications(network);
this.voltageProfileOutput = new VoltageProfileOutput();

this.debug = debug;
}
Expand All @@ -66,6 +69,10 @@ public NetworkModifications getNetworkModifications() {
return networkModifications;
}

public VoltageProfileOutput getVoltageProfileOutput() {
return voltageProfileOutput;
}

@Override
public Collection<AmplInputFile> getInputParameters() {
return List.of(constantQGenerators, variableShuntCompensators, variableTwoWindingsTransformers,
Expand All @@ -76,9 +83,10 @@ public Collection<AmplInputFile> getInputParameters() {
public Collection<AmplOutputFile> getOutputParameters(boolean isConvergenceOk) {
if (isConvergenceOk) {
List<AmplOutputFile> networkModificationsOutputFiles = networkModifications.getOutputFiles();
List<AmplOutputFile> list = new ArrayList<>(networkModificationsOutputFiles.size() + 1);
List<AmplOutputFile> list = new ArrayList<>(networkModificationsOutputFiles.size() + 2);
list.addAll(networkModificationsOutputFiles);
list.add(reactiveSlackOutput);
list.add(voltageProfileOutput);
return list;
}
return List.of();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,16 @@
package com.powsybl.openreac.parameters.output;

import com.powsybl.iidm.modification.*;
import com.powsybl.iidm.modification.tapchanger.AbstractTapPositionModification;
import com.powsybl.iidm.modification.tapchanger.RatioTapPositionModification;
import com.powsybl.iidm.network.Network;
import com.powsybl.iidm.network.*;
import com.powsybl.openreac.parameters.OpenReacAmplIOFiles;
import com.powsybl.openreac.parameters.output.ReactiveSlackOutput.ReactiveSlack;
import org.jgrapht.alg.util.Pair;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.*;

/**
* OpenReac user interface to get results information.
Expand All @@ -24,6 +25,7 @@
*/
public class OpenReacResult {

private static final Logger LOGGER = LoggerFactory.getLogger(OpenReacResult.class);
private final OpenReacStatus status;
private final List<ReactiveSlack> reactiveSlacks;
private final Map<String, String> indicators;
Expand All @@ -32,6 +34,8 @@ public class OpenReacResult {
private final List<VscConverterStationModification> vscModifications;
private final List<StaticVarCompensatorModification> svcModifications;
private final List<RatioTapPositionModification> tapPositionModifications;
private final HashMap<String, Pair<Double, Double>> voltageProfile;
private boolean updateNetworkWithVoltages = true;

/**
* @param status the final status of the OpenReac run.
Expand All @@ -48,6 +52,7 @@ public OpenReacResult(OpenReacStatus status, OpenReacAmplIOFiles amplIOFiles, Ma
this.vscModifications = List.copyOf(amplIOFiles.getNetworkModifications().getVscModifications());
this.svcModifications = List.copyOf(amplIOFiles.getNetworkModifications().getSvcModifications());
this.tapPositionModifications = List.copyOf(amplIOFiles.getNetworkModifications().getTapPositionModifications());
this.voltageProfile = new HashMap<>(amplIOFiles.getVoltageProfileOutput().getVoltageProfile());
}

public OpenReacStatus getStatus() {
Expand Down Expand Up @@ -82,7 +87,19 @@ public List<VscConverterStationModification> getVscModifications() {
return vscModifications;
}

public List<NetworkModification> getAllModifs() {
public Map<String, Pair<Double, Double>> getVoltageProfile() {
return voltageProfile;
}

public boolean isUpdateNetworkWithVoltages() {
return updateNetworkWithVoltages;
}

public void setUpdateNetworkWithVoltages(boolean updateNetworkWithVoltages) {
this.updateNetworkWithVoltages = updateNetworkWithVoltages;
}

public List<NetworkModification> getAllNetworkModifications() {
List<NetworkModification> modifs = new ArrayList<>(getGeneratorModifications().size() +
getShuntsModifications().size() +
getSvcModifications().size() +
Expand All @@ -97,8 +114,71 @@ public List<NetworkModification> getAllModifs() {
}

public void applyAllModifications(Network network) {
for (NetworkModification modif : getAllModifs()) {
for (NetworkModification modif : getAllNetworkModifications()) {
modif.apply(network);
}

// update target of ratio tap changers specified as variable by user
getTapPositionModifications().stream()
.map(AbstractTapPositionModification::getTransformerId)
.map(network::getTwoWindingsTransformer)
.forEach(transformer -> {
RatioTapChanger ratioTapChanger = transformer.getRatioTapChanger();
if (ratioTapChanger != null) {
Optional<Bus> bus = getRegulatingBus(ratioTapChanger.getRegulationTerminal(), transformer.getId());
bus.ifPresent(b -> {
Pair<Double, Double> busUpdate = voltageProfile.get(b.getId());
if (busUpdate != null) {
ratioTapChanger.setTargetV(busUpdate.getFirst() * b.getVoltageLevel().getNominalV());
} else {
throw new IllegalStateException("Voltage profile not found for bus " + b.getId());
}
});
}
});

// update target of shunts specified as variable by user
getShuntsModifications().stream()
.map(ShuntCompensatorModification::getShuntCompensatorId)
.map(network::getShuntCompensator)
.forEach(shunt -> {
Optional<Bus> bus = getRegulatingBus(shunt.getRegulatingTerminal(), shunt.getId());
bus.ifPresent(b -> {
Pair<Double, Double> busUpdate = voltageProfile.get(b.getId());
if (busUpdate != null) {
shunt.setTargetV(busUpdate.getFirst() * b.getVoltageLevel().getNominalV());
} else {
throw new IllegalStateException("Voltage profile not found for bus " + b.getId());
}
});
});

// update voltages of the buses
if (isUpdateNetworkWithVoltages()) {
for (var busUpdate : voltageProfile.entrySet()) {
Optional.ofNullable(network.getBusView().getBus(busUpdate.getKey())).ifPresentOrElse(
bus -> {
double v = busUpdate.getValue().getFirst();
double angle = busUpdate.getValue().getSecond();
bus.setV(v * bus.getVoltageLevel().getNominalV());
bus.setAngle(Math.toDegrees(angle));
}, () -> {
throw new IllegalStateException("Bus " + busUpdate.getKey() + " not found in network " + network.getId());
});
}
}
}

Optional<Bus> getRegulatingBus(Terminal terminal, String elementId) {
if (terminal == null) {
LOGGER.warn("Regulating terminal of element {} is null.", elementId);
return Optional.empty();
}
Bus bus = terminal.getBusView().getBus();
if (bus == null) {
LOGGER.warn("Bus of regulating terminal of element {} is null.", elementId);
return Optional.empty();
}
return Optional.ofNullable(bus);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/**
* 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/.
*/
package com.powsybl.openreac.parameters.output;

import com.powsybl.ampl.converter.AmplSubset;
import com.powsybl.commons.util.StringToIntMapper;
import com.powsybl.openreac.parameters.AmplIOUtils;
import org.jgrapht.alg.util.Pair;

import java.util.*;

/**
* @author Pierre Arvy <pierre.arvy at artelys.com>
*/
public class VoltageProfileOutput extends AbstractNoThrowOutput {

private static final String ELEMENT = "voltages";
public static final int EXPECTED_COLS = 5;
private static final int ID_COLUMN_INDEX = 4;
private static final int V_COLUMN_INDEX = 2;
private static final int ANGLE_COLUMN_INDEX = 3;

private final Map<String, Pair<Double, Double>> voltageProfile = new HashMap<>();

public Map<String, Pair<Double, Double>> getVoltageProfile() {
return voltageProfile;
}

@Override
public String getElement() {
return ELEMENT;
}

@Override
public int getExpectedColumns() {
return EXPECTED_COLS;
}

@Override
protected void readLine(String[] tokens, StringToIntMapper<AmplSubset> stringToIntMapper) {
String id = AmplIOUtils.removeQuotes(tokens[ID_COLUMN_INDEX]);
double v = readDouble(tokens[V_COLUMN_INDEX]);
double angle = readDouble(tokens[ANGLE_COLUMN_INDEX]);
voltageProfile.put(id, Pair.of(v, angle));
}

@Override
public boolean throwOnMissingFile() {
triggerErrorState();
return false;
}

}
18 changes: 18 additions & 0 deletions open-reac/src/main/resources/openreac/reactiveopfoutput.run
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,24 @@ close (fileOut);



###############################################################################
#
# Writing results for buses states
#
###############################################################################
if final_status == "OK" then {

let fileOut := "reactiveopf_results_voltages.csv";
printf "#variant;bus;V(pu);theta(rad);id;\n" > (fileOut);
printf {n in BUSCC} "%i;%i;%.3f;%.3f;%s;\n",
1, n, V[n], teta[n], '"' & bus_id[1,n] & '"'
> (fileOut);
close (fileOut);

}



###############################################################################
#
# Writing results for LCC converters
Expand Down
118 changes: 118 additions & 0 deletions open-reac/src/test/java/com/powsybl/openreac/OpenReacResultsTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
package com.powsybl.openreac;

import com.powsybl.ieeecdf.converter.IeeeCdfNetworkFactory;
import com.powsybl.iidm.modification.ShuntCompensatorModification;
import com.powsybl.iidm.modification.tapchanger.RatioTapPositionModification;
import com.powsybl.iidm.network.Network;
import com.powsybl.iidm.network.RatioTapChanger;
import com.powsybl.iidm.network.ShuntCompensator;
import com.powsybl.openreac.network.ShuntNetworkFactory;
import com.powsybl.openreac.network.VoltageControlNetworkFactory;
import com.powsybl.openreac.parameters.OpenReacAmplIOFiles;
import com.powsybl.openreac.parameters.input.OpenReacParameters;
import com.powsybl.openreac.parameters.output.OpenReacResult;
import com.powsybl.openreac.parameters.output.OpenReacStatus;
import org.junit.jupiter.api.Test;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.HashMap;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;

class OpenReacResultsTest {

@Test
void testTransformerTargetVUpdateWithoutVoltageResult() throws IOException {
Network network = VoltageControlNetworkFactory.createNetworkWithT2wt();
String t2wtId = "T2wT";
RatioTapChanger rtc = network.getTwoWindingsTransformer(t2wtId).getRatioTapChanger()
.setTargetDeadband(0)
.setRegulating(true);

// add transformer as variable for target V update
OpenReacAmplIOFiles io = getIOWithMockVoltageProfile(network);
io.getNetworkModifications().getTapPositionModifications().add(new RatioTapPositionModification(t2wtId, 0));

OpenReacResult results = new OpenReacResult(OpenReacStatus.OK, io, new HashMap<>());
IllegalStateException e = assertThrows(IllegalStateException.class, () -> results.applyAllModifications(network));
assertEquals("Voltage profile not found for bus " + rtc.getRegulationTerminal().getBusView().getBus().getId(), e.getMessage());
}

@Test
void testTransformerTargetVUpdateWithoutRegulationBus() throws IOException {
Network network = VoltageControlNetworkFactory.createNetworkWithT2wt();
String t2wtId = "T2wT";
RatioTapChanger rtc = network.getTwoWindingsTransformer(t2wtId).getRatioTapChanger()
.setTargetDeadband(0)
.setRegulating(true);
rtc.getRegulationTerminal().disconnect();

// add transformer as variable for target V update
OpenReacAmplIOFiles io = getIOWithMockVoltageProfile(network);
io.getNetworkModifications().getTapPositionModifications().add(new RatioTapPositionModification(t2wtId, 0));

// apply results without warm start (to avoid exception)
OpenReacResult results = new OpenReacResult(OpenReacStatus.OK, io, new HashMap<>());
results.setUpdateNetworkWithVoltages(false);
results.applyAllModifications(network);

// target V is not updated
assertEquals(33, rtc.getTargetV());
}

@Test
void testShuntTargetVUpdateWithoutVoltageResult() throws IOException {
Network network = ShuntNetworkFactory.create();
ShuntCompensator shunt = network.getShuntCompensator("SHUNT");
String regulatedBusId = shunt.getRegulatingTerminal().getBusView().getBus().getId();

OpenReacAmplIOFiles io = getIOWithMockVoltageProfile(network);
io.getNetworkModifications().getShuntModifications().add(new ShuntCompensatorModification("SHUNT", true, 0));

OpenReacResult results = new OpenReacResult(OpenReacStatus.OK, io, new HashMap<>());
IllegalStateException e = assertThrows(IllegalStateException.class, () -> results.applyAllModifications(network));
assertEquals("Voltage profile not found for bus " + regulatedBusId, e.getMessage());
}

@Test
void testShuntUpdateWithoutRegulationBus() throws IOException {
Network network = ShuntNetworkFactory.create();
ShuntCompensator shunt = network.getShuntCompensator("SHUNT");
shunt.getRegulatingTerminal().disconnect();

OpenReacAmplIOFiles io = getIOWithMockVoltageProfile(network);
io.getNetworkModifications().getShuntModifications().add(new ShuntCompensatorModification("SHUNT", null, 0));

// apply results without warm start
OpenReacResult results = new OpenReacResult(OpenReacStatus.OK, io, new HashMap<>());
results.setUpdateNetworkWithVoltages(false);
results.applyAllModifications(network);

// target V not updated
assertEquals(393, shunt.getTargetV());
}

@Test
void testWrongVoltageResult() throws IOException {
Network network = IeeeCdfNetworkFactory.create14();
OpenReacAmplIOFiles io = getIOWithMockVoltageProfile(network);
String idBusNotFound = io.getVoltageProfileOutput().getVoltageProfile().keySet().iterator().next();
OpenReacResult results = new OpenReacResult(OpenReacStatus.OK, io, new HashMap<>());
IllegalStateException e = assertThrows(IllegalStateException.class, () -> results.applyAllModifications(network));
assertEquals("Bus " + idBusNotFound + " not found in network " + network.getId(), e.getMessage());
}

private OpenReacAmplIOFiles getIOWithMockVoltageProfile(Network network) throws IOException {
OpenReacAmplIOFiles io = new OpenReacAmplIOFiles(new OpenReacParameters(), network, true);
try (InputStream input = getClass().getResourceAsStream("/mock_outputs/reactiveopf_results_voltages.csv");
InputStreamReader in = new InputStreamReader(input);
BufferedReader reader = new BufferedReader(in)) {
io.getVoltageProfileOutput().read(reader, null);
}
return io;
}
}
Loading

0 comments on commit 4701635

Please sign in to comment.