Skip to content
Open
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
43 changes: 43 additions & 0 deletions docs/parameters/business-parameters.md
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,49 @@ See also: [Modelling the maximum minimum relative margin objective function](../
For CORE, we should use all the CORE region boundaries (all countries seperated by a - sign) plus Alegro's special
equation: "{BE}-{22Y201903144---9}-{DE}+{22Y201903145---4}"

### Forced Actions
You can add an optional ForcedActions extension ("forced-actions") 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.

#### preventive-actions-list
- **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
"forced-actions": {
"preventive-actions-list": {
"version": "1.2",
"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
}
]
}
}
~~~
:::
::::

## Examples
> ⚠️ **NOTE**
> The following examples in json and yaml are not equivalent
Expand Down
4 changes: 4 additions & 0 deletions ra-optimisation/rao-api/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,10 @@
<groupId>com.powsybl</groupId>
<artifactId>powsybl-ucte-util</artifactId>
</dependency>
<dependency>
<groupId>com.powsybl</groupId>
<artifactId>powsybl-action-api</artifactId>
</dependency>

<!-- Test dependencies -->
<dependency>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ private RaoParametersCommons() {

// header
public static final String VERSION = "version";
public static final String RAO_PARAMETERS_CATEGORY = "rao-parameters";

// objective function parameters
public static final String OBJECTIVE_FUNCTION = "objective-function";
Expand Down Expand Up @@ -125,6 +126,10 @@ private RaoParametersCommons() {
public static final String PTDF_SUM_LOWER_BOUND = "ptdf-sum-lower-bound";
public static final String SEARCH_TREE_PARAMETERS = "open-rao-search-tree-parameters";

// -- Forced actions parameters
public static final String FORCED_ACTIONS_PARAMETERS = "forced-actions";
public static final String PREVENTIVE_ACTION_LIST = "preventive-actions-list";

// -- Fast Rao Parameters
public static final String FAST_RAO_PARAMETERS = "fast-rao-parameters";
public static final String NUMBER_OF_CNECS_TO_ADD = "number-of-cnecs-to-add";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

package com.powsybl.openrao.raoapi.json;

import com.powsybl.action.json.ActionJsonModule;
import com.powsybl.openrao.raoapi.parameters.RaoParameters;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
Expand Down Expand Up @@ -178,6 +179,7 @@ public static void serialize(RaoParameters parameters, JsonGenerator jsonGenerat
private static ObjectMapper createObjectMapper() {
return JsonUtil.createObjectMapper()
.registerModule(new RaoParametersJsonModule())
.registerModule(new SensitivityJsonModule());
.registerModule(new SensitivityJsonModule())
.registerModule(new ActionJsonModule());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ public String getExtensionName() {

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

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
/*
* 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.raoapi.json.extensions;

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

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

import static com.powsybl.openrao.raoapi.RaoParametersCommons.*;

/**
* ForcedActions 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 JsonForcedActions implements JsonRaoParameters.ExtensionSerializer<ForcedActions> {

@Override
public void serialize(ForcedActions forcedActions, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
jsonGenerator.writeStartObject();
jsonGenerator.writeObjectField(PREVENTIVE_ACTION_LIST, new ActionList(forcedActions.getPreventiveActions()));
jsonGenerator.writeEndObject();
}

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

@Override
public String getExtensionName() {
return FORCED_ACTIONS_PARAMETERS;
}

@Override
public String getCategoryName() {
return RAO_PARAMETERS_CATEGORY;
}

@Override
public Class<? super ForcedActions> getExtensionClass() {
return ForcedActions.class;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ public String getExtensionName() {

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

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
* 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.raoapi.parameters.extensions;

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;

import static com.powsybl.openrao.raoapi.RaoParametersCommons.FORCED_ACTIONS_PARAMETERS;

/**
* Allows forcing actions on 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 ForcedActions extends AbstractExtension<RaoParameters> {
private final List<Action> preventiveActions;

public ForcedActions(List<Action> preventiveActions) {
this.preventiveActions = Collections.unmodifiableList(preventiveActions);
}

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

public List<Action> getPreventiveActions() {
return preventiveActions;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@

package com.powsybl.openrao.raoapi.json;

import com.powsybl.action.*;
import com.powsybl.iidm.network.PhaseTapChanger;
import com.powsybl.iidm.network.StaticVarCompensator;
import com.powsybl.iidm.network.ThreeSides;
import com.powsybl.openrao.commons.OpenRaoException;
import com.powsybl.openrao.raoapi.parameters.ObjectiveFunctionParameters;
import com.powsybl.openrao.raoapi.parameters.RaoParameters;
Expand All @@ -27,11 +31,10 @@

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.*;

import static com.powsybl.action.PercentChangeLoadAction.QModificationStrategy.CONSTANT_Q;
import static com.powsybl.iidm.network.HvdcLine.ConvertersMode.SIDE_1_RECTIFIER_SIDE_2_INVERTER;
import static com.powsybl.openrao.raoapi.RaoParametersCommons.RAO_PARAMETERS_VERSION;
import static org.junit.jupiter.api.Assertions.*;

Expand Down Expand Up @@ -162,6 +165,80 @@ void update() {

}

@Test
void testForcedActionsRoundTrip() throws IOException {
List<Action> actions = new ArrayList<>();
actions.add(new SwitchAction("id1", "switchId1", true));
actions.add(new MultipleActionsAction("id2", Collections.singletonList(new SwitchAction("id3", "switchId2", true))));
actions.add(new TerminalsConnectionAction("id3", "lineId3", true)); // both sides.
actions.add(new TerminalsConnectionAction("id4", "lineId4", false)); // both sides.
actions.add(new PhaseTapChangerTapPositionAction("id5", "transformerId1", true, 5, ThreeSides.TWO));
actions.add(new PhaseTapChangerTapPositionAction("id6", "transformerId2", false, 12));
actions.add(new PhaseTapChangerTapPositionAction("id7", "transformerId3", true, -5, ThreeSides.ONE));
actions.add(new PhaseTapChangerTapPositionAction("id8", "transformerId3", false, 2, ThreeSides.THREE));
actions.add(new GeneratorActionBuilder().withId("id9").withGeneratorId("generatorId1").withActivePowerRelativeValue(true).withActivePowerValue(100.0).build());
actions.add(new GeneratorActionBuilder().withId("id10").withGeneratorId("generatorId2").withVoltageRegulatorOn(true).withTargetV(225.0).build());
actions.add(new GeneratorActionBuilder().withId("id11").withGeneratorId("generatorId2").withVoltageRegulatorOn(false).withTargetQ(400.0).build());
actions.add(new LoadActionBuilder().withId("id12").withLoadId("loadId1").withRelativeValue(false).withActivePowerValue(50.0).build());
actions.add(new LoadActionBuilder().withId("id13").withLoadId("loadId1").withRelativeValue(true).withReactivePowerValue(5.0).build());
actions.add(new PercentChangeLoadActionBuilder().withId("id26").withLoadId("loadId1").withP0PercentChange(5.0).withQModificationStrategy(CONSTANT_Q).build());
actions.add(new DanglingLineActionBuilder().withId("id17").withDanglingLineId("dlId1").withRelativeValue(true).withReactivePowerValue(5.0).build());
actions.add(new RatioTapChangerTapPositionAction("id14", "transformerId4", false, 2, ThreeSides.THREE));
actions.add(new RatioTapChangerTapPositionAction("id15", "transformerId5", true, 1));
actions.add(RatioTapChangerRegulationAction.activateRegulation("id16", "transformerId5", ThreeSides.THREE));
actions.add(PhaseTapChangerRegulationAction.activateAndChangeRegulationMode("id17", "transformerId5", ThreeSides.ONE,
PhaseTapChanger.RegulationMode.ACTIVE_POWER_CONTROL, 10.0));
actions.add(PhaseTapChangerRegulationAction.deactivateRegulation("id18",
"transformerId6", ThreeSides.ONE));
actions.add(PhaseTapChangerRegulationAction.activateAndChangeRegulationMode("id19",
"transformerId6", ThreeSides.ONE,
PhaseTapChanger.RegulationMode.ACTIVE_POWER_CONTROL, 15.0));
actions.add(RatioTapChangerRegulationAction.activateRegulationAndChangeTargetV("id20", "transformerId5", 90.0));
actions.add(RatioTapChangerRegulationAction.deactivateRegulation("id21", "transformerId5", ThreeSides.THREE));
actions.add(new HvdcActionBuilder()
.withId("id22")
.withHvdcId("hvdc1")
.withAcEmulationEnabled(false)
.build());
actions.add(new HvdcActionBuilder()
.withId("id23")
.withHvdcId("hvdc2")
.withAcEmulationEnabled(true)
.build());
actions.add(new HvdcActionBuilder()
.withId("id24")
.withHvdcId("hvdc2")
.withAcEmulationEnabled(true)
.withDroop(121.0)
.withP0(42.0)
.withConverterMode(SIDE_1_RECTIFIER_SIDE_2_INVERTER)
.withRelativeValue(false)
.build());
actions.add(new HvdcActionBuilder()
.withId("id25")
.withHvdcId("hvdc1")
.withAcEmulationEnabled(false)
.withActivePowerSetpoint(12.0)
.withRelativeValue(true)
.build());
actions.add(new ShuntCompensatorPositionActionBuilder().withId("id22").withShuntCompensatorId("shuntId1").withSectionCount(5).build());
actions.add(new StaticVarCompensatorActionBuilder().withId("id23")
.withStaticVarCompensatorId("svc").withRegulationMode(StaticVarCompensator.RegulationMode.VOLTAGE)
.withVoltageSetpoint(56.0).build());
actions.add(new StaticVarCompensatorActionBuilder().withId("id24")
.withStaticVarCompensatorId("svc").withRegulationMode(StaticVarCompensator.RegulationMode.REACTIVE_POWER)
.withReactivePowerSetpoint(120.0).build());
actions.add(new TerminalsConnectionAction("id4", "transformerId25", ThreeSides.THREE, true)); // only one side.
actions.add(new AreaInterchangeTargetAction("id99", "AreaA", 101.0));
actions.add(new AreaInterchangeTargetAction("idDisabledTarget", "AreaA", Double.NaN));

RaoParameters raoParameters = new RaoParameters();
ForcedActions forcedActions = new ForcedActions(actions);
raoParameters.addExtension(ForcedActions.class, forcedActions);

roundTripTest(raoParameters, JsonRaoParameters::write, JsonRaoParameters::read, "/RaoParameters_with_ForcedActions.json");
}

@Test
void writeExtension() throws IOException {
RaoParameters parameters = new RaoParameters();
Expand Down Expand Up @@ -191,6 +268,20 @@ void testFailOnOldVersion() {
assertEquals(String.format("RaoParameters version '2.0' cannot be deserialized. The only supported version currently is '%s'.", RAO_PARAMETERS_VERSION), e.getMessage());
}

@Test
void testWrongForcedActions() {
OpenRaoException exception = assertThrows(OpenRaoException.class, () -> JsonRaoParameters.read(getClass().getResourceAsStream("/RaoParameters_with_wrong_ForcedActions.json")));
assertEquals("Unexpected token: wrong-key", exception.getMessage());
}

@Test
void testEmptyForcedActions() {
RaoParameters raoParameters = JsonRaoParameters.read(getClass().getResourceAsStream("/RaoParameters_with_empty_ForcedActions.json"));
ForcedActions forcedActions = raoParameters.getExtension(ForcedActions.class);
assertNotNull(forcedActions);
assertTrue(forcedActions.getPreventiveActions().isEmpty());
}

@ParameterizedTest
@ValueSource(strings = {"LoopFlowError", "ObjFuncTypeError", "WrongField"})
void importNokTest(String source) {
Expand Down
Loading