Opened 4 years ago

Closed 4 years ago

#6238 closed enhancement (invalid)

Extend overconstrained connector semantics to handle dynamic branches

Reported by: Francesco Casella Owned by: Karim Adbdelhak
Priority: high Milestone: 1.17.0
Component: Backend Version: 1.16.0
Keywords: Cc: Andreas Heuermann, Philip Hannebohm, Per Östlund, Adrian Pop, Dietmar Winkler, massimo ceraolo, Andrea Bartolini, adrien.guironnet@…, Rüdiger Franke, dr.christian.kral@…, anton.haumer@…

Description (last modified by Francesco Casella)

The overconstrained connector semantics defined in Section 9.4 of Modelica 3.4 considers a fixed connection graph, that can be handled statically at compile time.

One of the cited examples for overconstrained types in connectors is the phase information in AC power systems. The current semantics is able to represent synchronous systems whose topology remains unchanged during simulation. The phase reference is generated by one component of the synchronous system (normally a power generator) and distributed throughout the entire synchronous system via the overconstrained connector variable. In fact, it is possible to have multiple synchronous systems in the same Modelica model, e.g. two national grids connected by a DC link, but in any case their topology is fixed at runtime.

When modelling AC trasmission systems, is is possible that in case of severe perturbations, some key circuit breakers are switched open (i.e., their conductance brough to zero), effectively splitting a single synchronous system into multiple synchronous islands. Note that this requires no structural changes in the grid equations, only to bring some admittance values to zero.

When this happens, the two (or more) ensuing islands can settle into new steady states with different frequencies. Hence, if a single, whole-system-wide reference is still used, the phase angle of currents and voltages of the now disconnected islands will continue to rotate permanently with a frequency that is the difference between the local island frequency and the frequency of the island where the original root node was picked. This is very inconvenient, because it prevents variable step-size solvers to increase the step size once the system settles to the new steady state.

This problem could be avoided if the Connections.branch() operators could be made time-dependent. It would then be possible to disable the Connections.branch() statements of the circuit breakers being opened. As a consequence, two or more disjoint connection graphs would be formed at the time of the breaker openings, each corresponding to a new synchronous island. The new graph topology should be analyzed at this point, picking a new root node for each newly formed island in the grid. Then, instead of having a single phase reference for the entire system, which would no longer be adequate, one would now have two or more independent phase references, one for each island, which would ensure that the variables of each island reach a steady state, thus avoiding the persistent sinusoidal oscillations found in the case of a statically determined connection topology.

From the point of view of the language specification, this extension only requires to lift (or suitably relax) the restriction mentioned in the definition of the Connections.branch() operator in Section 9.4.1:

This function can be used at all places where a connect(..) statement is allowed.

From an implementation point of view, this means that the graph connection analysis no longer can be performed statically at compile time, but needs to be deferred to run time.

In general this can be quite a big deal, because Modelica cannot handle structural dynamics at runtime. However, the special case of scalar overconstrained connector variables with zero-dimensional equalityConstraint() function output turns out to be very easy to handle, and is enough to address the issue with the synchronous AC grids. This is demonstrated by the attached examplary case.

The attached package contains components to build conceptual models of phasor-based AC power grid models. The components are overly simplified and only retain the minimal features that are needed to analyze this language semantics extension.

In this case, the overconstrained variable is actually not the phase angle but rather the angular velocity of the phasor reference frame, which is sufficient to ensure that all state variables are constant when the synchronous islands are all operating in steady state.

System 1 is a basic example with two generators G1 and G2, each connected to a local load, connected by an inductive transmission line T. The system starts in steady state with balanced loads, then at time = 1 the active power consumption of load L2 is reduced by 20%. This starts a transient, that eventually dies out once the primary frequency controls stabilize the system at a new, higher frequency. Note that at that point, all state variables are constant, allowing implicit solvers to take large time steps.

System 2 is similar to System1, except that line T is split in the series connection of lines T1a and T1b, running in parallel, with line T2. This allows to demonstrate the overconstrained connector mechanism, since T1a and T1b form a loop that must be broken to avoid getting an overconstrained system of equations. The overall impedance is the same, so the power and frequency transients are the same as in System1.

In System 3, line T2 also includes a circuit breaker mechanism, that brings its susceptance B to zero when the T2.open signal becomes true. From that point onwards, there is no longer any exchange of power along the lines, so the system is effectively split into two synchronous islands, one containing G1 and L1, the other one containing G2 and L2. The frequencies of the two drift apart, causing the phasors and state variables of G2 and L2, that still use G1.port.omegaRef as a reference angular velocity, to oscillate forever even when the new steady state is reached.

In order to avoid this, the connection graph needs to be split when the breaker embedded in T2 is opened. This could be achieved in System4 by using the modified TransmissionLineVariableBranch component, that contains the following equation

  if closed then
    port_a.omegaRef = port_b.omegaRef;
    Connections.branch(port_a.omegaRef, port_b.omegaRef);
  end if;

Note that:

  • the Connections.branch() operator is called in a branch of an if-equation, so it is only active when closed = true.
  • the if-equation violates the balancing rule of Section 8.3.4, apparently breaking the single-assignment rule. However, it only involves overconstrained connector variables that appear in the Connections.branch() call in the same branch of the if-equation

Handling this case at runtime is relatively straighforward. During initialization, and each time an if-equation is triggered that contains a Connections.branch(A.R, B.R) call and an equation A.R = B.R, the runtime must:

  • build the connection graph according to the rules set forth in Section 9.4, only accounting for branches that are active once the if-equation has been processed
  • identify disjoint sub-graphs and select one root node for each of them, if not already given by Connections.root()
  • break all loops in the graph, replacing the connection equation with the (empty) constraint in each branch that is broken
  • set each overconstrained connector variable of each sub-graph to be equal to the value of the corresponding root node variable (this can be done trivially via pointers)
  • in case a branch is re-established, it may be sensible to add a numerical check that the corresponding equation involving the overconstrained connector variables is actually verified within some tolerance, otherwise aborting the simulation; this would correspond to the requirement that two islands can only be reconnected if the are operating exactly at the same frequency. This check could be defined by adding one more function to the overconstrained type definition, which returns a boolean indicating if the reconnection is feasible

I suppose this feature could be implemented rather easily in OpenModelica, as a first prototype implementation of this language extension. It could be tested in an extended version of the attached package, including some more examples with a bit more nodes and edges in the connection graphs, and finally tested on an extended version of the PowerGrids library, that already envisioned such a feature.

If the experiments with the prototype are successful, this could form the basis of an MCP to introduce this feature into the next version of the Modelica Specification.

Attachments (1)

DynamicOverconstrainedConnectors.mo (6.2 KB ) - added by Francesco Casella 4 years ago.

Download all attachments as: .zip

Change History (8)

by Francesco Casella, 4 years ago

comment:1 by Francesco Casella, 4 years ago

Description: modified (diff)

comment:2 by Dietmar Winkler, 4 years ago

@casella, Just a small comment on clarification. Whenever you talk about Connection.* I guess you mean Connections.*, correct?

comment:3 by Adrian Pop, 4 years ago

The thing you need to break are connect statements.
This means you need to keep the connect statements through the compiler until the runtime.

Either that or you find out all the if equations containing the Connection.branch build & break those graphs already at compile time and then do some sort of if equation that changes the system based on the condition.

in reply to:  2 comment:4 by Francesco Casella, 4 years ago

Replying to dietmarw:

@casella, Just a small comment on clarification. Whenever you talk about Connection.* I guess you mean Connections.*, correct?

Absolutely, I just updated the text accordingly.

comment:5 by Francesco Casella, 4 years ago

Description: modified (diff)

in reply to:  3 comment:6 by Francesco Casella, 4 years ago

Replying to adrpo:

The thing you need to break are connect statements.

Aha, sorry, of course you're right. I designed the whole thing under the assumption that I wanted to conditionally break unbreakable branches, but that made things unecessarily complicated and potentially wrong in some cases. Thanks for pointing this out!

I will try to redesign the whole concept by conditionally breaking the connection graphs corresponding to the breakable branches, i.e. to the connect statements. I guess the whole thing should could eventually come out simpler and more general than this.

Of course it is not the entire connect() statement that needs to be conditional, because that would have an impact on the connection sets, flow variables equations etc. This is not necessary, since, according to Section 9.4.2, everything except overconstrained connector variables is always handled in the same way according to Section 9.2.

I guess we could add an optional boolean input to the connect() statement, e.g.

  connect(A.R, B.R, activeBranch = true)

where activeBranch is a Boolean input that can be set to false to dynamically break the branch in the overconstrained connection graph.

This means you need to keep the connect statements through the compiler until the runtime.

Yeah, probably that is not necessary. As I wrote above, the handling of connection sets and of all non-overconstrained variables in the connectors (which is quite complicated) is not affected at all by this feature, so it can still be carried out statically by the front end. No need to change that.

The only thing that we need to carry on to the runtime are reconfigurable equality constraints, related to the breaking of breakable branches and to the choice of new root nodes in the connection graphs.

Either that or you find out all the if equations containing the Connection.branch build & break those graphs already at compile time and then do some sort of if equation that changes the system based on the condition.

I guess won't be possible in practice. Power grids can have thousands of breakers, of course you cannot pre-compile all possible combinations of their states, since they grow as O(2N).

In general, we'll need to re-run the graph connectivity analysis each time the activeBranch input of some connect() statemente is changed, and determine on the fly who are the new root nodes and to which root node each overconstrained variable is equal to.

Of course if there is only a handful of such dynamically breakable branches, then one could pre-compute all the combinations at compile time and avoid doing the analysis at runtime, but I'd say that is reasonable only if N < 8. The limit could be set by a compilation flag.

I'll close this ticket as invalid and open a new one with an improved proposal.

comment:7 by Francesco Casella, 4 years ago

Resolution: invalid
Status: newclosed
Note: See TracTickets for help on using tickets.