Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions distribution/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,11 @@
<artifactId>open-rao-search-tree-rao</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>open-rao-roda</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>open-rao-sensitivity-analysis</artifactId>
Expand Down
48 changes: 48 additions & 0 deletions docs/parameters/implementation-specific-parameters.md
Original file line number Diff line number Diff line change
Expand Up @@ -580,3 +580,51 @@ fast-rao-parameters:
~~~
:::
::::

### RODA parameters

RODA implementation specific parameters ("roda-parameters"). Optional.

#### forced-preventive-actions-list

You can create an optional forced-preventive-actions-list to force some preventive actions before running the RAO.
While this is equivalent to pre-processing the network file before running the RAO, it can be useful if
you want to test different preventive actions in an outside loop, without having to pre-process (and
eventually serialize) the network.
Note that this is not currently supported in the yaml format.
Also note that when using lazy networks, this will make all the networks load at the beginning of the RAO. So you may encounter memory issues.

- **Expected value**: a list of PowSyBl Actions. In JSON, this should be represented by a serialized ActionList.
- **Default value**: empty array
- **Usage**: these actions will be applied on the network before running the RAO. In the case of time-coupled RAO,
the actions will be applied for all timestamps.
Actions that cannot be applied (for example if the ID of the element is wrong) will be ignored (the issue will be logged in a warning).

#### Example
::::{tabs}
:::{group-tab} JSON
~~~json
"roda-parameters": {
"forced-preventive-actions-list": {
"version": "1.3",
"actions": [
{
"type": "PHASE_TAP_CHANGER_TAP_POSITION",
"id": "PRA_PST_BE",
"transformerId": "BBE2AA1 BBE3AA1 1",
"tapPosition": -16,
"relativeValue": false,
"side": "TWO"
},
{
"type": "TERMINALS_CONNECTION",
"id": "Open FR1 FR2",
"elementId": "FFR1AA1 FFR2AA1 1",
"open": true
}
]
}
}
~~~
:::
::::
61 changes: 61 additions & 0 deletions ra-optimisation/roda/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
</properties>

<dependencies>
<!-- Compile dependencies -->
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>open-rao-rao-api</artifactId>
Expand All @@ -29,6 +30,66 @@
<groupId>${project.groupId}</groupId>
<artifactId>open-rao-search-tree-rao</artifactId>
</dependency>
<dependency>
<groupId>com.powsybl</groupId>
<artifactId>powsybl-action-api</artifactId>
</dependency>

<!-- Test dependencies -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>open-rao-commons</artifactId>
<version>${project.version}</version>
<scope>test</scope>
<type>test-jar</type>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>open-rao-crac-impl</artifactId>
<version>${project.version}</version>
<type>test-jar</type>
<scope>test</scope>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>open-rao-rao-result-impl</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.google.jimfs</groupId>
<artifactId>jimfs</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.powsybl</groupId>
<artifactId>powsybl-commons-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.powsybl</groupId>
<artifactId>powsybl-config-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

</project>
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import com.powsybl.openrao.commons.TemporalData;
import com.powsybl.openrao.commons.TemporalDataImpl;
import com.powsybl.openrao.commons.Unit;
import com.powsybl.openrao.commons.logs.OpenRaoLoggerProvider;
import com.powsybl.openrao.data.crac.api.Crac;
import com.powsybl.openrao.data.crac.api.State;
import com.powsybl.openrao.data.crac.api.cnec.FlowCnec;
Expand All @@ -23,6 +24,7 @@
import com.powsybl.openrao.raoapi.parameters.extensions.OpenRaoSearchTreeParameters;
import com.powsybl.openrao.raoapi.parameters.extensions.SearchTreeRaoCostlyMinMarginParameters;
import com.powsybl.openrao.raoapi.parameters.extensions.SearchTreeRaoRelativeMarginsParameters;
import com.powsybl.openrao.roda.parameters.RodaParameters;
import com.powsybl.openrao.searchtreerao.commons.ToolProvider;
import com.powsybl.openrao.searchtreerao.commons.objectivefunction.ObjectiveFunction;
import com.powsybl.openrao.searchtreerao.commons.optimizationperimeters.OptimizationPerimeter;
Expand Down Expand Up @@ -71,6 +73,8 @@ public class Roda implements TimeCoupledRaoProvider {

@Override
public CompletableFuture<TimeCoupledRaoResult> run(TimeCoupledRaoInput timeCoupledRaoInput, RaoParameters raoParameters) {
applyForcedActions(timeCoupledRaoInput.getRaoInputs(), raoParameters.getExtension(RodaParameters.class));

if (timeCoupledRaoInput.getRaoInputs().getTimestamps().size() == 1) {
TECHNICAL_LOGS.info("[RODA] Only one time-step in inputs. Calling single time-step RAO directly: {}", DEFAULT_SINGLE_TS_RAO);
return runSingleTsRao(timeCoupledRaoInput, raoParameters);
Expand Down Expand Up @@ -212,6 +216,18 @@ public CompletableFuture<TimeCoupledRaoResult> run(TimeCoupledRaoInput timeCoupl
return CompletableFuture.completedFuture(timeCoupledRaoResult);
}

static void applyForcedActions(TemporalData<RaoInput> raoInputs, RodaParameters rodaParameters) {
if (rodaParameters == null || rodaParameters.getForcedPreventiveActions().isEmpty()) {
return;
}
OpenRaoLoggerProvider.BUSINESS_LOGS.info(String.format("Applying %d forced preventive actions before running RAO.", rodaParameters.getForcedPreventiveActions().size()));
Comment thread
pet-mit marked this conversation as resolved.
raoInputs.getDataPerTimestamp().values().stream().map(RaoInput::getNetwork).forEach(network -> {
rodaParameters.getForcedPreventiveActions().stream().filter(action -> !action.toModification().apply(network, true))
.forEach(action -> OpenRaoLoggerProvider.BUSINESS_WARNS.warn(String.format("Action '%s' could not be applied.", action.getId())));
rodaParameters.getForcedPreventiveActions().forEach(action -> action.toModification().apply(network, false));
});
}

private CompletableFuture<TimeCoupledRaoResult> runSingleTsRao(TimeCoupledRaoInput raoInputs, RaoParameters raoParameters) {
OffsetDateTime ts = raoInputs.getRaoInputs().getTimestamps().getFirst();
RaoInput raoInput = raoInputs.getRaoInputs().getData(ts).orElseThrow();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/*
* Copyright (c) 2026, 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.openrao.roda.parameters;

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.google.auto.service.AutoService;
import com.powsybl.action.ActionList;
import com.powsybl.action.json.ActionJsonModule;
import com.powsybl.commons.json.JsonUtil;
import com.powsybl.openrao.commons.OpenRaoException;
import com.powsybl.openrao.raoapi.json.JsonRaoParameters;

import java.io.IOException;
import java.util.List;

/**
* RODA parameters extension json serializer & deserializer.
* Depends on PowSyBl's ActionList serializer & deserializer.
*
* @author Peter Mitri {@literal <peter.mitri at rte-france.com>}
*/
@AutoService(JsonRaoParameters.ExtensionSerializer.class)
public class JsonRodaParameters implements JsonRaoParameters.ExtensionSerializer<RodaParameters> {

private static final String PREVENTIVE_ACTION_LIST = "forced-preventive-actions-list";

@Override
public void serialize(RodaParameters rodaParameters, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
jsonGenerator.writeStartObject();
jsonGenerator.writeFieldName(PREVENTIVE_ACTION_LIST);
createObjectMapper().writeValue(jsonGenerator, new ActionList(rodaParameters.getForcedPreventiveActions()));
jsonGenerator.writeEndObject();
}

private static ObjectMapper createObjectMapper() {
return JsonUtil.createObjectMapper()
.registerModule(new ActionJsonModule());
}

@Override
public RodaParameters deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException {
ActionList actionList = null;
while (!jsonParser.nextToken().isStructEnd()) {
if (jsonParser.currentName().equals(PREVENTIVE_ACTION_LIST)) {
jsonParser.nextToken();
actionList = createObjectMapper().readValue(jsonParser, ActionList.class);
} else {
throw new OpenRaoException("Unexpected token: " + jsonParser.currentName());
}
}
if (actionList == null) {
return new RodaParameters(List.of());
}
return new RodaParameters(actionList.getActions());
}

@Override
public String getExtensionName() {
return RodaParameters.EXTENSION_NAME;
}

@Override
public String getCategoryName() {
return "rao-parameters";
}

@Override
public Class<? super RodaParameters> getExtensionClass() {
return RodaParameters.class;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
* Copyright (c) 2026, 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.openrao.roda.parameters;

import com.powsybl.action.Action;
import com.powsybl.commons.extensions.AbstractExtension;
import com.powsybl.openrao.raoapi.parameters.RaoParameters;

import java.util.Collections;
import java.util.List;

/**
* RODA specific RAO parameters.
* For now, only allows forcing actions on the network before running RAO.
* Could be useful to avoid pre-processing network multiple times (for example when testing topological changes
* in an outside loop).
*
* @author Peter Mitri {@literal <peter.mitri at rte-france.com>}
*/
public class RodaParameters extends AbstractExtension<RaoParameters> {
public static final String EXTENSION_NAME = "roda-parameters";
private final List<Action> forcedPreventiveActions;

public RodaParameters(List<Action> forcedPreventiveActions) {
this.forcedPreventiveActions = Collections.unmodifiableList(forcedPreventiveActions);
}

@Override
public String getName() {
return EXTENSION_NAME;
}

public List<Action> getForcedPreventiveActions() {
return forcedPreventiveActions;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
/*
* Copyright (c) 2026, 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.openrao.roda;

import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.read.ListAppender;
import com.powsybl.action.Action;
import com.powsybl.action.PhaseTapChangerTapPositionAction;
import com.powsybl.action.TerminalsConnectionAction;
import com.powsybl.iidm.network.Network;
import com.powsybl.openrao.commons.TemporalData;
import com.powsybl.openrao.commons.TemporalDataImpl;
import com.powsybl.openrao.commons.logs.RaoBusinessWarns;
import com.powsybl.openrao.data.crac.api.Crac;
import com.powsybl.openrao.data.crac.impl.utils.CommonCracCreation;
import com.powsybl.openrao.data.crac.impl.utils.NetworkImportsUtil;
import com.powsybl.openrao.raoapi.RaoInput;
import com.powsybl.openrao.roda.parameters.RodaParameters;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.slf4j.LoggerFactory;

import java.time.OffsetDateTime;
import java.util.List;
import java.util.Map;

import static org.junit.jupiter.api.Assertions.*;

/**
* @author Peter Mitri {@literal <peter.mitri at rte-france.com>}
*/
class RodaTest {
Network network;
TemporalData<RaoInput> raoInput;

@BeforeEach
void setUp() {
network = NetworkImportsUtil.import12NodesNetwork();
Crac crac = CommonCracCreation.createWithPreventivePstRange();
String variantId = network.getVariantManager().getWorkingVariantId();
raoInput = new TemporalDataImpl<>(
Map.of(OffsetDateTime.now(), RaoInput.buildWithPreventiveState(network, crac)
.withNetworkVariantId(variantId)
.build()));
}

@Test
void testApplyForcedActions() {
ch.qos.logback.classic.Logger logger = (ch.qos.logback.classic.Logger) LoggerFactory.getLogger(RaoBusinessWarns.class);
ListAppender<ILoggingEvent> listAppender = new ListAppender<>();
listAppender.start();
logger.addAppender(listAppender);
List<ILoggingEvent> logsList = listAppender.list;

Action action1 = new PhaseTapChangerTapPositionAction("action1", "BBE2AA1 BBE3AA1 1", false, -8);
Action action2 = new TerminalsConnectionAction("action2", "FFR1AA1 FFR2AA1 1", true);
Action action3 = new TerminalsConnectionAction("wrong_action", "wrong_id", true);
RodaParameters rodaParameters = new RodaParameters(List.of(action1, action2, action3));
Roda.applyForcedActions(raoInput, rodaParameters);
assertEquals(-8, network.getTwoWindingsTransformer("BBE2AA1 BBE3AA1 1").getPhaseTapChanger().getTapPosition());
assertFalse(network.getLine("FFR1AA1 FFR2AA1 1").getTerminal1().isConnected());
assertFalse(network.getLine("FFR1AA1 FFR2AA1 1").getTerminal2().isConnected());
assertTrue(logsList.stream().anyMatch(e -> e.getMessage().contains("Action 'wrong_action' could not be applied.")));
}

@Test
void testApplyForcedActionsNullOrEmpty() {
assertDoesNotThrow(() -> Roda.applyForcedActions(raoInput, null));
assertDoesNotThrow(() -> Roda.applyForcedActions(raoInput, new RodaParameters(List.of())));
}
}
Loading
Loading