Internal json CRAC format#

Introduction#

The name CRAC is a standard denomination defined by the ENTSO-E which means: Contingency list, Remedial Actions, and additional Constraints.

In other words, it gathers the following information:

  • critical outages,

  • critical network elements,

  • and remedial actions.

It is typically used in European coordinated processes. It enables, for a given geographical region, to define the network elements that might be critical after specific outages, and the remedial actions that might help to manage them.

A CRAC object model has been designed in OpenRAO in order to store all the aforementioned information. This page aims to present:

  • the content and the organization of the data present in the OpenRAO CRAC object model,

  • how a OpenRAO CRAC object can be built,

    • using the java API,

    • or using the OpenRAO internal Json CRAC format.

Note that other pages of this documentation describe how the OpenRAO CRAC object model can be built with other standard CRAC formats, such as the FlowBasedConstraint format, the CSE Format, and the CIM format.

Full CRAC examples#

Example of complete CRACs are given below

The creation of a small CRAC is for instance made in this test class of powsybl-open-rao repository:
example on GitHub

The following paragraphs of this page explain, step by step, the content of these examples.

KEY
🔴 marks a mandatory field
⚪ marks an optional field
🔵 marks a field that can be mandatory in some cases
⭐ marks a field that must be unique in the CRAC

Network elements#

OpenRAO relies on the PowSyBl framework, and OpenRAO’s CRAC relies on some elements of PowSyBl network model: the so-called network elements.

The network elements can be:

  • the elements that are disconnected from the network by a contingency,

  • the power lines that are identified as critical network elements in the CRAC,

  • the PSTs that are identified by the CRAC as remedial actions,

  • the switches that can be used as topological remedial actions…

Network elements are referenced in the CRAC with:

🔴⭐ an id : The id must match the unique id of a PowSyBl object. When using a network element, the applications relying on the CRAC will look in the Network for an identifiable element with the very same id: if this element cannot be found, it can lead to an error in the application. When building the CRAC, one must therefore make sure that only the network elements whose IDs can be understood by the PowSyBl iidm Network are added to the CRAC. This is particularly important to keep in mind if you want to develop your own CRAC importer.

⚪ a name : The name shouldn’t be absolutely required by the application relying on the CRAC object, but it could be useful to make the CRAC or some outputs of the application more readable for humans.

Network elements are never built on their own in the CRAC object: they are always a component of a larger business object, and are added implicitly to the CRAC when the business object is added (e.g. a CNEC, or a remedial action). When building a business object, one of the two following methods can be used to add a network element to it (using an ID only, or an ID and a name).

(...).withNetworkElement("network_element_id")
(...).withNetworkElement("network_element_id", "network_element_name")

These methods will be depicted in the following examples on this page.

Contingencies#

A CRAC object must define “critical contingencies” (or “critical outages”, or “CO”, or “N-k”…).
The denomination chosen within the OpenRAO internal format is “Contingency”.

A contingency is the representation of an incident on the network (i.e. a cut line or a group/transformer failure, etc.). In OpenRAO, it is modelled by the loss of one or several network elements. Usually we have either a one-network-element-loss called “N-1”, or a two-network-element-loss called “N-2”.

Examples:

  • N-1: The loss of one generator

  • N-2: The loss of two parallel power lines

A contingency is a probable event that can put the grid at risk. Therefore, contingencies must be considered when operating the electrical transmission / distribution system.

In OpenRAO, contingencies come from PowSyBl Contingency, where a contingency element has the id of the impacted network element, and its type is linked to the network elements type. They are represented in the following way:

crac.newContingency()
    .withId("CO_0001")
    .withName("N-1 on generator")
    .withContingencyElement("powsybl_generator_id", ContingencyElementType.GENERATOR)
    .add();

crac.newContingency()
    .withId("CO_0002")
    .withName("N-2 on electrical lines")
    .withContingencyElement("powsybl_electrical_line_1_id", ContingencyElementType.LINE)
    .withContingencyElement("powsybl_electrical_line_2_id", ContingencyElementType.LINE)
    .add();

Instants and States#

The instant is a moment in the chronology of a contingency event. Four instants kinds currently exist in OpenRAO:

  • the preventive instant kind occurs before any contingency, and describes the “base-case” situation. A CRAC may contain only one instant of kind preventive.

  • the outage instant kind occurs just after a contingency happens, in a time too short to allow the activation of any curative remedial action. A CRAC may contain only one instant of kind outage.

  • the auto instant kind occurs after a contingency happens, and spans through the activation of automatic curative remedial actions (“automatons”) that are triggered without any human intervention. These automatons are pre-configured to reduce some constraints, even though they can generate constraints elsewhere in the network. A CRAC may contain any number of instants of kind auto.

  • the curative instant kind occurs after a contingency happens, after enough time that would allow the human activation of curative remedial actions. A CRAC may contain any number of instants of kind auto.

💡 NOTE
Flow / angle / voltage limits on critical network elements are usually different for each instant.
The outage and auto instant kinds are transitory, therefore less restrictive temporary limits (TATL) can be allowed in these instants.
On the contrary, the preventive and curative instant kinds are supposed to be a lasting moment during which the grid operation is nominal (sometimes thanks to preventive and/or curative remedial actions), so they usually come with more restrictive permanent limits (PATL).
OpenRAO allows a different limit setting for different instants on critical network elements (see CNECs).

patl-vs-tatl (PRA = Preventive Remedial Action, ARA = Automatic Remedial Action, CRA = Curative Remedial Action)

The OpenRAO object model includes the notion of “state”. A state is either:

  • the preventive state: the state of the base-case network, without any contingency, at the preventive instant.

  • the combination of a given contingency with instant outage, auto or curative: the state of the network after the said contingency, at the given instant (= with more or less delay after this contingency).

The scheme below illustrates these notions of instant and state. It highlights the combinations of the situations which can be described in a CRAC, with a base-case situation, but also variants of this situation occurring at different moments in time after different probable and hypothetical contingencies.

Instants & states

States are not directly added to a OpenRAO CRAC object model; they are implicitly created by business objects that are described in the following paragraphs (CNECs and remedial actions).

Instants are added one after the other in the CRAC object. The first instant must be of kind preventive. The second instant must be of kind outage.

crac.newInstant("preventive", InstantKind.PREVENTIVE)
    .newInstant("outage", InstantKind.OUTAGE)
    .newInstant("auto", InstantKind.AUTO)
    .newInstant("curative1", InstantKind.CURATIVE)
    .newInstant("curative2", InstantKind.CURATIVE);

CNECs#

A CRAC should define CNECs. A CNEC is a “Critical Network Element and Contingency” (also known as “CBCO” or “Critical Branch and Critical Outage”).

A CNEC is a network element, which is considered at a given instant, after a given contingency (i.e. at a given OpenRAO “state”).
The contingency is omitted if the CNEC is defined at the preventive instant.

💡 NOTE
A OpenRAO CNEC is associated to one instant and one contingency only. This is not the case for all native CRAC formats: for instance, in the CORE merged-CB CRAC format, the post-outage CNECs are implicitly defined for the two instants outage and curative.

However, we are talking here about the internal OpenRAO CRAC format, which has its own independent conventions, and which is imported from native CRAC formats using CRAC importers.

A CNEC has an operator, i.e. the identifier of the TSO operating its network element. It may also have a border, which can be useful on some processes to know where the line is located and so which remedial actions could be useful for it. Moreover, a CNEC can have a reliability margin: a safety buffer to cope with unplanned events or uncertainties of input data (i.e. an extra margin).

Optimised and monitored CNECs#

CNECs can be monitored and/or optimised. This notion of monitored/optimised has been introduced by the capacity calculation on the CORE region, and is now also used for the CSE region:

  • maximise the margins of CNECs that are “optimised”

  • ensure that the margins of “monitored” CNECs are positive and/or are not decreased by the RAO.

OpenRAO contains 3 families of CNECs, depending on which type of physical constraints they have: FlowCnecs, AngleCnecs and VoltageCnecs.

Flow CNECs#

A “FlowCnec” has the two following specificities:

  • it contains one network element that is a Branch. In the PowSyBl vocabulary, a “Branch” is an element connected to two terminals. For instance, lines, tie-lines, transformers and PSTs are all “Branches”.

  • the physical parameter which is monitored on the CNEC is the power flow.

A FlowCnec has two sides, which correspond to the two terminals of the PowSyBl network element of the FlowCnec (usually called terminals “one” and “two”, or terminals “left” and “right”). The notion of direction is also inherent to the FlowCnec: a flow in direction “direct” is a flow from terminal one/left to terminal two/right, while a flow in direction “opposite” is a flow from terminal two/right to terminal one/left. The convention of OpenRAO is that a positive flow is a flow in the “direct” direction, while a negative flow is a flow in the “opposite” direction.

💡 NOTE
A OpenRAO FlowCnec is one implementation of the generic “BranchCnec”. If needed, this would allow you a fast implementation of other types of CNECs, on branches, but with a monitored physical parameter other than power flow.

Flow limits on a FlowCnec#

A FlowCnec has flow limits, called “thresholds” in OpenRAO. These thresholds define the limits between which the power flow of the FlowCnec should ideally remain.

  • They can be defined in megawatt, ampere, or in percentage of the Imax of the branch (in which case the Imax is read in the network file).

  • A threshold is defined either on the left or on the right side of the FlowCnec.

    💡 NOTE The side of the branch on which the threshold is set is particularly crucial in the following cases:

    • when the threshold is defined in ampere or %Imax on a transformer, as the current values on the two sides of a transformer are different,

    • when the threshold is defined in %Imax on a tie-line, as the current limits are usually different on both sides of a tie-line,

    • when the application uses AC load-flow computation, as the flow values on the two sides of a branch are different (due to losses). The CracCreationParameters allows the user to decide which side(s) should be monitored by default.

  • A threshold has a minimum and/or a maximum value. The maximum value represents the maximum value of the flow in the “direct” direction and the minimum value represents the maximum value of the flow in the “opposite” direction. Therefore, for FlowCnecs that only have one minimum or one maximum value, the flow is implicitly monitored in only one direction (see example 1 of picture below).

FlowCnec-Threshold

In the examples above, all the thresholds are defined in megawatt. If the thresholds are defined in ampere, or in %Imax, additional data is required in order to handle the following conversions:

  • if one threshold of the FlowCnec is in ampere or in percentage of Imax, the nominal voltage on both sides of the threshold must be defined

  • if one threshold of the FlowCnec is in percentage of Imax, the Imax of the FlowCnec on the side of the threshold must be defined

💡 NOTE A FlowCnec’s reliability margin is also known as FRM for Flow Reliability Margin. It can only be defined in megawatt, it is subtracted from the thresholds of the FlowCnec, adding an extra constraint.

Creating a FlowCnec#

In OpenRAO, FlowCnecs can be created by the java API, or written in the json CRAC internal format, as shown below:

crac.newFlowCnec()
    .withId("preventive-cnec-with-one-threshold-id")
    .withNetworkElement("network-element-id")
    .withInstant("preventive")
    .withOperator("operator1")
    .withBorder("border")
    .withReliabilityMargin(50.)
    .withOptimized(true)
    .newThreshold()
      .withUnit(Unit.MEGAWATT)
      .withSide(TwoSides.ONE)
      .withMin(-1500.)
      .withMax(1500.)
      .add()
    .add();

crac.newFlowCnec()
    .withId("curative-cnec-with-two-thresholds-id")
    .withName("curative-cnec-with-two-thresholds-name")
    .withNetworkElement("network-element-id")
    .withInstant("curative")
    .withContingency("contingency-id")
    .withOperator("operator1")
    .withBorder("border")
    .newThreshold()
      .withUnit(Unit.PERCENT_IMAX)
      .withSide(TwoSides.TWO)
      .withMax(0.95)
      .add()
    .newThreshold()
      .withUnit(Unit.AMPERE)
      .withSide(TwoSides.ONE)
      .withMin(-450.)
      .add()
    .withReliabilityMargin(50.)
    .withOptimized(true)
    .withMonitored(false)
    .withNominalVoltage(380., TwoSides.ONE)
    .withNominalVoltage(220., TwoSides.TWO)
    .withIMax(500.) // this means that the value is the same on both sides, but the side could have been specified using "withImax(500., TwoSides.TWO)" instead 
    .add();

Loop-flow extension#

When a FlowCnec carries a LoopFlowThreshold extension (and if loop-flow constraints are enabled in the RAO), its loop-flow is monitored by the RAO, that will keep it under its threshold when optimising remedial actions.
The loop-flow extension defines the loop-flow threshold to be respected by the RAO (even though the initial loop-flow value on this CNEC may override this user-defined thrshold, as explained here).

flowCnec = crac.getFlowCnec("cnec-with-mw-loop-flow-extension");
flowCnec.newExtension(LoopFlowThresholdAdder.class)
        .withUnit(Unit.MEGAWATT)
        .withValue(150.0)
        .add();

flowCnec = crac.getFlowCnec("cnec-with-pimax-loop-flow-extension");
flowCnec.newExtension(LoopFlowThresholdAdder.class)
        .withUnit(Unit.PERCENT_IMAX)
        .withValue(0.9)
        .add();

Angle CNECs#

An “AngleCnec” is a branch which may see a phase angle shift between its two ends when it’s disconnected. This may induce insecurities in the network when it’s back up. That’s why we monitor angle CNECs and associate with them remedial actions (generally re-dispatching) that can reduce the phase angle shift between the two ends.

In terms of OpenRAO object model, an AngleCnec is a CNEC. Even though it is associated with a branch, it is not a BranchCnec, because we cannot define on which side it is monitored: it is monitored on both sides (more specifically, we monitor the phase shift between the two sides).

An AngleCnec has the following specificities:

  • it contains two network elements, an importing node and an exporting node, that represent the importing and exporting ends of the branch.

  • the physical parameter which is monitored by the CNEC is the angle.

  • it must contain at least one threshold, defined in degrees. A threshold has a minimum and/or a maximum value.

💡 NOTE AngleCnecs currently cannot be optimised by the RAO, but they are monitored by an independent Monitoring module with ANGLE as physical parameter.

Creating an AngleCnec#

In OpenRAO, AngleCnecs can be created by the java API, or written in the json CRAC internal format, as shown below:

 cnec1 = crac.newAngleCnec()
  .withId("angleCnecId1")
  .withName("angleCnecName1")
  .withInstant("outage")
  .withContingency(contingency1Id)
  .withOperator("cnec1Operator")
  .withBorder("border1")
  .withExportingNetworkElement("eneId1", "eneName1")
  .withImportingNetworkElement("ineId1", "ineName1")
  .newThreshold()
    .withUnit(Unit.DEGREE)
    .withMax(1000.0)
    .withMin(-1000.0)
    .add()
  .withMonitored()
  .add();

cnec2 = crac.newAngleCnec()
  .withId("angleCnecId2")
  .withInstant("preventive")
  .withOperator("cnec2Operator")
  .withBorder("border2")
  .withExportingNetworkElement("eneId2")
  .withImportingNetworkElement("ineId2")
  .withReliabilityMargin(5.0)
  .newThreshold()
    .withUnit(Unit.DEGREE)
    .withMax(500.0)
    .add()
  .withMonitored()
  .add();

Voltage CNECs#

A “VoltageCnec” is a CNEC on which we monitor the voltage on substations. It has the following specificities:

  • it contains one network element (a VoltageLevel)

  • the physical parameter which is monitored by the CNEC is the voltage.

  • it must contain at least one threshold, defined in kilovolts. A threshold has a minimum and/or a maximum value.

💡 NOTE VoltageCnecs currently cannot be optimised by the RAO, but they are monitored by an independent VoltageMonitoring module with VOLTAGE as physical parameter.

Creating a VoltageCnec#

In OpenRAO, VoltageCnecs can be created by the java API, or written in the json CRAC internal format, as shown below:

crac.newVoltageCnec()
    .withId("voltageCnecId1")
    .withName("voltageCnecName1")
    .withInstant("outage")
    .withContingency(contingency1Id)
    .withOperator("cnec1Operator")
    .withBorder("border1")
    .withNetworkElement("neId1", "neName1")
    .newThreshold()
      .withUnit(Unit.KILOVOLT)
      .withMax(420.0)
      .withMin(360.0)
      .add()
    .withMonitored()
    .add();
crac.newVoltageCnec()
    .withId("voltageCnecId2")
    .withInstant("preventive")
    .withOperator("cnec2Operator")
    .withBorder("border2")
    .withNetworkElement("neId2")
    .newThreshold()
      .withUnit(Unit.KILOVOLT)
      .withMax(440.0)
      .add()
    .withMonitored()
    .add();

Remedial actions and usages rules#

A remedial action is an action on the network that is considered capable of reducing constraints on the CNECs.

Two types of remedial action exist in OpenRAO:

  • Network Actions: they have the specificity of being binary. A Network Action is either applied on the network, or not applied. Topological actions are a typical example of Network Actions.

  • Range Actions: they have the specificity of having a degree of freedom, a set-point. When a Range Action is activated, it is activated at a given value of its set-point. PSTs are a typical example of Range Actions.

Both Network Actions and Range Actions have usage rules which define the conditions under which they can be activated.
A usage rule contains a usage method which can be activated under certain conditions. The usage methods in OpenRAO are:

  • UNDEFINED: this means that the usage rule cannot decide if the remedial action can be used.

  • AVAILABLE: when activated, this means that the remedial action is available for the RAO to assess. The RAO can choose to activate it if it finds that it is optimal. Takes precedence over the above usage method.

  • FORCED: when activated, this means that the remedial action must be applied by the RAO (usually used for automatons). Takes precedence over the above usage methods.

  • UNAVAILABLE: when activated, this means that the remedial action cannot be used. Takes precedence over the above usage methods.

OpenRAO has the following usage rules with their activation conditions:

  • the FreeToUse usage rule (defined for a specific instant): the usage method is activated in all the states of a given instant.

  • the OnState usage rule (defined for a specific state): the usage method is activated in a given state.

  • the OnFlowConstraintInCountry usage rule (defined for a specific Country, a specific instant), and an optional contingency: the usage method is activated if any FlowCnec in the given country is constrained (ie has a flow greater than one of its thresholds) at the given instant. If a contingency is defined, then only constraints on FlowCnecs with the same contingency count.

  • the OnConstraint usage rule (defined for a specific instant and a specific Cnec): the usage method is activated if the given Cnec is constrained at the given instant.

A remedial action has an operator, which is the name of the TSO which operates the remedial action.

The examples below are given for a Network Action, but the same methods exists for Range Actions.
Complete examples of Network and Range Action creation are given in the following paragraphs.

crac.newNetworkAction()
    .newFreeToUseUsageRule()
        .withUsageMethod(UsageMethod.AVAILABLE)
        .withInstant("preventive")
        .add();

crac.newNetworkAction()
    .newOnStateUsageRule()
        .withUsageMethod(UsageMethod.AVAILABLE)
        .withInstant("curative")
        .withContingency("contingency-id")
        .add();

crac.newNetworkAction()
    .newOnConstraintUsageRule()
        .withInstant("auto")
        .withCnec("flow-cnec-id")
        .add();

crac.newNetworkAction()
    .newOnFlowConstraintInCountryUsageRule()
        .withInstant("preventive")
        .withCountry(Country.FR)
        .withContingency("contingency-id")
        .add();

crac.newNetworkAction()
    .newOnConstraintUsageRule()
        .withInstant("curative")
        .withCnec("angle-cnec-id")
        .add();

crac.newNetworkAction()
    .newOnConstraintUsageRule()
        .withInstant("curative")
        .withCnec("voltage-cnec-id")
        .add();

Network Actions#

A OpenRAO “Network Action” is a remedial action with a binary state: it is either active or inactive.
One network action is a combination of one or multiple “elementary actions”, among the following:

  • Terminals connection action: opening or closing all terminals of a connectable in the network.

  • Switch action: opening or closing a switch in the network.

  • Phase tap changer tap position action: setting the tap of a PST in the network to a specific position.

  • Generator action: setting the active power of a generator in the network to a specific value.

  • Load action: setting the active power of a load in the network to a specific value.

  • Dangling line action: setting the active power of a dangling line) in the network to a specific value.

  • Shunt compensator position action: setting the number of sections of a shunt compensator to a specific value.

  • Switch pairs: opening a switch in the network and closing another (actually used to model CSE bus-bar change remedial actions).

// combination of two switch actions
crac.newNetworkAction()
	.withId("topological-na-id")
    .withName("topological-na-name")
    .withOperator("operator")
    .newSwitchAction()
		.withNetworkElement("switch-id-1")
		.withActionType(ActionType.CLOSE)
		.add()
    .newSwitchAction()
		.withNetworkElement("switch-id-2")
		.withActionType(ActionType.OPEN)
		.add()
    .newOnInstantUsageRule().withUsageMethod(UsageMethod.AVAILABLE).withInstant(PREVENTIVE_INSTANT).add()
    .add();

// terminals connection action
crac.newNetworkAction()
	.withId("terminals-connection-na-id")
    .withName("terminals-connection-na-name")
    .withOperator("operator")
    .newTerminalsConnectionAction()
		.withNetworkElement("transformer-id")
		.withActionType(ActionType.CLOSE)
		.add()
    .newOnInstantUsageRule().withUsageMethod(UsageMethod.AVAILABLE).withInstant(PREVENTIVE_INSTANT).add()
    .add();

// phase tap chnager tap position action
crac.newNetworkAction()
	.withId("pst-setpoint-na-id")
    .withName("pst-setpoint-na-name")
    .withOperator("operator")
    .newPhaseTapChangerTapPositionAction()
		.withNormalizedSetpoint(15)
		.withNetworkElement("pst-id")
		.add()
    .newOnInstantUsageRule().withUsageMethod(UsageMethod.AVAILABLE).withInstant(PREVENTIVE_INSTANT).add()
    .add();

// generator action with two usage rules
crac.newNetworkAction()
	.withId("generator-action-na-id")
	.withOperator("operator")
	.newGeneratorAction()
		.withActivePowerValue(260.0)
		.withNetworkElement("generator-id")
		.add()
    .newOnInstantUsageRule().withUsageMethod(UsageMethod.AVAILABLE).withInstant(PREVENTIVE_INSTANT).add()
    .newOnContingencyStateUsageRule().withUsageMethod(UsageMethod.AVAILABLE).withContingency("contingency-id").withInstant(Instant.CURATIVE).add()
    .add();
	
// load action
crac.newNetworkAction()
	.withId("load-action-na-id")
	.withOperator("operator")
	.newLoadAction()
		.withActivePowerValue(260.0)
		.withNetworkElement("load-id")
		.add()
    .newOnInstantUsageRule().withUsageMethod(UsageMethod.AVAILABLE).withInstant(PREVENTIVE_INSTANT).add()
    .add();
	
// dangling line action
crac.newNetworkAction()
	.withId("dangling-line-na-id")
	.withOperator("operator")
	.newDanglingLineAction()
		.withActivePowerValue(260.0)
		.withNetworkElement("dangling-line-id")
		.add()
    .newOnInstantUsageRule().withUsageMethod(UsageMethod.AVAILABLE).withInstant(PREVENTIVE_INSTANT).add()
    .add();

// shunt compensator position action
crac.newNetworkAction()
    .withId("shunt-compensator-na-id")
    .withOperator("operator")
    .newShuntCompensatorPositionAction()
        .withSetpoint(3)
        .withNetworkElement("shunt-compensator-id")
        .add()
    .newOnInstantUsageRule().withUsageMethod(UsageMethod.AVAILABLE).withInstant(PREVENTIVE_INSTANT).add()
    .add();

// switch pair
crac.newNetworkAction()
	.withId("switch-pair-na-id")
	.withOperator("operator")
	.newSwitchPair()
		.withSwitchToOpen("switch-to-open-id", "switch-to-open-name")
		.withSwitchToClose("switch-to-close-id-and-name")
		.add()
    .newOnInstantUsageRule().withUsageMethod(UsageMethod.AVAILABLE).withInstant(PREVENTIVE_INSTANT).add()
    .add();

Range Actions#

A OpenRAO “Range Action” is a remedial action with a continuous or discrete set-point. If the range action is inactive, its set-point is equal to its value in the initial network. If it is activated, its set-point is optimized by the RAO to improve the objective function.
OpenRAO has four types of range actions : PST range actions, HVDC range actions, “injection” range actions and counter- trading range actions.

PST Range Action#

A PstRangeAction contains a network element which must point to a PST in the iidm PowSyBl network model. The PstRangeAction will be able to modify the set-point of this PST.

💡 NOTE

  • the set-point of the PstRangeAction is the angle of the PST, measured in degrees

  • all the methods of the PST which mention a set-point implicitly use angle values

  • however, they often have an equivalent that uses integer tap positions

The domain in which the PstRangeAction can modify the tap of the PST is delimited by ‘TapRanges’. A PstRangeAction contains a list (which can be empty) of TapRanges.

TapRanges can be of different types:

  • absolute: the mix/max admissible tap of the PST, given in the convention of the PowSyBl network model

  • relative to initial network: the maximum variation of the tap of the PST relatively to its initial tap

  • relative to previous instant: the maximum variation of the tap of the PST relatively to its tap in the previous instant. Note that this type of range does not make sense for PstRangeActions which are only available in the preventive instant, as there is no instant before the preventive one.

  • relative to previous time-step : the maximum variation of the tap of the PST relatively to its tap during the previous time-step (currently ignored by RAO)

The final validity range of the PstRangeAction is the intersection of its TapRanges, with the intersection of the min/max feasible taps of the PST.
The PstRangeAction also requires additional data, notably to be able to interpret the TapRanges. Those additional data are: the initial tap of the PST, and a conversion map which gives for each feasible tap of the PST its corresponding angle. Utility methods have been developed in OpenRAO to ease the management of these additional data during the creation of a PstRangeAction.

Two or more aligned PST range actions must have the same (random) group ID defined. The RAO will make sure their optimized set-points are always equal.

If the PstRangeAction is an automaton, it has to have a speed assigned. This is an integer that defines the relative speed of this range action compared to other range-action automatons (smaller “speed” value = faster range action). No two range-action automatons can have the same speed value, unless they are aligned.

crac.newPstRangeAction()
    .withId("pst-range-action-1-id")
    .withName("pst-range-action-1-name")
    .withOperator("operator")
    .withNetworkElement("pst-network-element-id")
    .withGroupId("pst-range-action-1 is aligned with pst-range-action-2")
    .withInitialTap(3)
    .withTapToAngleConversionMap(Map.of(-3, 0., -2, .5, -1, 1., 0, 1.5, 1, 2., 2, 2.5, 3, 3.))
    .newTapRange()
        .withRangeType(RangeType.ABSOLUTE)
        .withMinTap(0)
        .withMaxTap(3)
        .add()
    .newTapRange()
        .withRangeType(RangeType.RELATIVE_TO_INITIAL_NETWORK)
        .withMinTap(-2)
        .withMaxTap(2)
        .add()
    .newOnInstantUsageRule().withUsageMethod(UsageMethod.AVAILABLE).withInstant(Instant.PREVENTIVE).add()
    .withSpeed(1)
    .add();

In that case, the validity domain of the PST (intersection of its ranges and feasible taps) is [1; 3] Note that the PstHelper utility class can ease the creation of the TapToAngleConversionMap.

HVDC Range Action#

An HvdcRangeAction contains a network element that must point towards an HvdcLine of the iidm PowSyBl network model.
The HvdcRangeAction will be able to modify its active power set-point.

The domain in which the HvdcRangeAction can modify the HvdcSetpoint is delimited by ‘HvdcRanges’. An HvdcRangeAction contains a list of HvdcRanges. A range must be defined with a min and a max.

Two or more aligned HVDC range actions must have the same (random) group ID defined. The RAO will make sure their optimized set-points are always equal.

If the HvdcRangeAction is an automaton, it has to have a speed assigned. This is an integer that defines the relative speed of this range action compared to other range-action automatons (smaller “speed” value = faster range action). No two range-action automatons can have the same speed value, unless they are aligned.

 crac.newHvdcRangeAction()
        .withId("hvdc-range-action-id")
		.withName("hvdc-range-action-name")
   		.withOperator("operator")
        .withNetworkElement("hvec-id")
        .newHvdcRange().withMin(-5).withMax(10).add()
        .newOnInstantUsageRule().withInstant(Instant.PREVENTIVE).withUsageMethod(UsageMethod.AVAILABLE).add()
        .add();  

In that case, the validity domain of the HVDC is [-5; 10].

Injection Range Action#

An InjectionRangeAction modifies given generators’ & loads’ injection set-points inside a given range.
Each impacted generator or load has an associated “key”, which is a coefficient of impact that is applied on its set-point.

This range action is mainly used to represent an HVDC line in an AC equivalent model (where the line is disconnected and replaced by two injections, one on each side of the line, with opposite keys of 1 and -1).

HVDC AC model

Two or more aligned injection range actions must have the same (random) group ID defined. The RAO will make sure their optimized set-points are always equal.

If the InjectionRangeAction is an automaton, it has to have a speed assigned. This is an integer that defines the relative speed of this range action compared to other range-action automatons (smaller “speed” value = faster range action). No two range-action automatons can have the same speed value, unless they are aligned.

 crac.newInjectionRangeAction()
        .withId("injection-range-action-id")
		.withName("injection-range-action-name")
   		.withOperator("operator")
        .withNetworkElementAndKey(1, "network-element-1")
        .withNetworkElementAndKey(-0.5, "network-element-2")
        .newRange().withMin(-1200).withMax(500).add()
        .newOnInstantUsageRule().withInstant(Instant.PREVENTIVE).withUsageMethod(UsageMethod.AVAILABLE).add()
        .add();     

In that case, the validity domain of the injection range action’s reference set-point is [-1200; 500].
This means the set-point of “network-element-1” (key = 1) can be changed between -1200 and +500, while that of “network-element-2” (key = -0.5) will be changed between -250 and +600

Counter-Trade Range Action#

A CounterTradeRangeAction is an exchange between two countries. The exporting country send power to the importing country.

It is a costly remedial action which is currently not handled by the RAO.

 crac.newCounterTradeRangeAction()
        .withId("counter-trade-range-action-id")
        .withGroupId("group-id")
   		.withOperator("operator")
        .withExportingCountry(Country.FR)
        .withImportingCountry(Country.ES)
        .newRange().withMin(0).withMax(1000).add()
        .newOnInstantUsageRule().withInstant("preventive").withUsageMethod(UsageMethod.AVAILABLE).add()
        .add();     

In that case, the validity domain of the counter-trade range action’s reference set-point is [0; 1000]. The power is exported from France to Spain.

RAs usage limitations#

A OpenRAO “ra-usage-limits-per-instant” consists in limits on the usage of remedial actions for given instants.
The given instant IDs should match an ID of an instant in the CRAC. Otherwise, its limits will be ignored.
See here for further explanation on each field described in the following example.

crac.newRaUsageLimits("preventive")
    .withMaxRa(44)
    .withMaxTso(12)
    .withMaxRaPerTso(new HashMap<>(Map.of("FR", 41, "BE", 12)))
    .withMaxPstPerTso(new HashMap<>(Map.of("BE", 7)))
    .withMaxTopoPerTso(new HashMap<>(Map.of("DE", 5)))
    .withMaxElementaryActionPerTso(new HashMap<>(Map.of("BE", 20)))
    .add();
crac.newRaUsageLimits("curative")
    .withMaxRa(3)
    .add();

If several instants of the same kind are defined in the CRAC, the usage limits are cumulative among these instants.

For instance, let us consider a CRAC with 3 curative instants and the following usage limits:

"ra-usage-limits-per-instant" : [ {
  "instant": "curative 1",
  "max-ra" : 1,
}, {
  "instant": "curative 2",
  "max-ra" : 3,
}, {
  "instant": "curative 3",
  "max-ra" : 7,
} ]

The maximum number of applicable remedial actions defined for the second curative instant (3) is a cumulated value that includes the maximum number of applicable remedial actions during the first curative instant (1). Thus, if 1 remedial action was applied during the first curative instant, only 2 remedial actions can actually be applied during the second curative instant. Likewise, the maximum number of remedial actions for the third curative instant includes the remedial actions applied at curative 1 and 2 instants. Depending on the number of previously applied remedial actions, the number of actually applicable remedial actions during the third curative instant can vary between 4 and 7.