Appearance
Chapter 17 — Modeling control systems
Modules 1 through 4 built the plant: resistors, inductors and capacitors as companion models, lines as travelling waves, transformers and machines as coupled magnetic circuits. Every one of them was assembled into a single matrix and solved simultaneously. This chapter is about the other half of a power system — the control domain, the block diagrams that sense, compute and command — and it is solved on a completely different principle. There is no matrix, no simultaneous solution, and no iteration. There is an order, and there is a delay, and getting both right is the whole job.
Chapter 4 introduced the control palette and traced one loop from a sensed power to an IGBT gate. Here we open the machinery: how a continuous block becomes a difference equation, how the engine decides what runs before what, and exactly what it costs — in time steps — for a signal to cross between the two domains. Chapters 18 and 19 then build real generator controllers on top of it.
Learning objectives
By the end of this chapter you should be able to:
- Explain why the control domain is solved as an ordered sequence of assignments rather than as a matrix, and what that buys.
- Write the discrete update NumaSim actually uses for a first-order lag, and say how it differs from the trapezoidal companion models of Chapter 2.
- State the accuracy and stability consequences of that choice, and compute the time step at which a given block goes unstable.
- Explain what a topological sort is doing in a simulator, which edges it deliberately ignores, and what happens when the graph has a cycle.
- Predict, in time steps, the lag a signal accumulates when it crosses between the control and electrical domains — in either direction, and around a loop.
- Choose between a wire, a label and a Go-To/From pair, and know which signals you can put on a scope.
17.1 The control domain is a second solver
An electrical network is a system of simultaneous equations. Kirchhoff's current law at one node involves the voltages at neighbouring nodes, those nodes' equations involve their neighbours, and there is no way to compute one voltage before another. That is why Chapter 3 assembled everything into
A control diagram has no such coupling. A gain block's output depends on its input and nothing else. An integrator's output depends on its input and its own stored state. Every block is a plain assignment
and the state is private to the block. Nothing in a control diagram requires two blocks to be solved together, so nothing needs a matrix. The engine walks a list of blocks, evaluates each one, and moves on.
This is not a simplification or an approximation — it is a structural property of signal-flow diagrams, and it is why control blocks are almost free compared with network nodes. A hundred extra control blocks add a hundred cheap function evaluations per step. A hundred extra electrical nodes change the size of the matrix.
Two consequences follow immediately, and they are the subject of the next three sections. First, if each block is an assignment, then somebody has to decide what order to evaluate them in — get it wrong and a block reads a value that has not been computed yet. Second, the control domain and the electrical domain are now two different solvers running side by side, and something has to define how they exchange values across the boundary.
17.2 Discretizing a control block
A control block specified in the frequency domain — a lag
NumaSim's control blocks do not use the trapezoidal rule. They use forward Euler. This is a real and deliberate difference between the two domains, and you need to know it to predict what a control block will do.
The first-order lag
Take the first-order lag
Forward Euler evaluates the derivative using values already in hand and steps forward:
which rearranges to the update the engine actually performs:
There is one multiply and one add. Notice what is not there: no division by a step-dependent denominator, no averaging of
You can verify this against the engine without running anything. NumaSim keeps golden reference outputs for its blocks; the one for the first-order transfer function drives a unit step into
Those are exactly the first three rows of the stored reference. Had the block been trapezoidal, the same step would have produced
Why the two domains differ
The trapezoidal rule earns its cost in the network because the network is solved as a matrix anyway: the companion model's history source folds into the right-hand side at no extra cost, and second-order accuracy comes free. A control block has no matrix to hide in, and forward Euler is the cheapest thing that works — one multiply-add, no per-block bookkeeping. Given that control time constants are typically milliseconds to seconds while EMT time steps are microseconds, the accuracy the explicit method gives up is usually far below the uncertainty in the parameters themselves.
What forward Euler costs you
Two properties matter in practice.
It is first-order accurate. The local error per step is
It has a stability limit. Write the update with
The block decays only if
Step at exactly
In an EMT study the margin is usually enormous. A voltage transducer with
Zero means "not there"
Which brings us to a convention every model in the library relies on: a zero time constant removes its block rather than dividing by zero. A lag with
This matters because the standard controller models in Chapters 18 and 19 are published with tables of parameters in which zeros are common and meaningful — a manufacturer who does not use transient gain reduction publishes
The rest of the palette
Not every block is a lag. The engine uses whichever discretization suits the block, and it is worth knowing which is which:
| Block family | Discretization | Examples |
|---|---|---|
| Lags, integrators, washouts | Forward Euler | integrator, tf1, the internal stages of every controller in Chapters 18-19 |
| Derivatives, degenerate leads | Backward difference | the D term of pid, a lead-lag with a zero denominator |
| Pure transport delay | Integer-sample ring buffer | the dead time of tf1, the engine delay of a gas turbine governor |
| One-step shift | The memory arena itself, no arithmetic | unit_delay |
| Sliding window | Ring buffer over one cycle | rms |
| Algebraic, no state | Evaluated directly at the current sample | gain, the Park transform abc_dq0, the PWM comparator pwm |
Two of these deserve a note. A transport delay is rounded to a whole number of samples,
17.3 Evaluation order is a topological sort
If every block is an assignment, order is everything. Evaluate a summing junction before the gain that feeds it and the junction adds last step's value instead of this step's. The result is not a crash; it is a subtly wrong answer, which is worse.
Read the control diagram as a directed graph: one vertex per block, one edge from each block that produces a signal to each block that consumes it. "Every producer runs before all of its consumers" is precisely a topological sort of that graph. NumaSim computes one with Kahn's in-degree algorithm: count each block's unsatisfied inputs, repeatedly emit any block whose count has reached zero, and decrement its consumers. The cost is linear in blocks plus wires.
The sort runs once, at compile time. It happens when the circuit is built, before
Three kinds of edge the sort ignores
A naive reading would build the graph from every connection in the circuit. The engine deliberately does not, and the exclusions are the interesting part.
Electrical-to-electrical connections are not edges. Two resistors sharing a node do not have a producer/consumer relationship — they are simultaneous, and the matrix solve handles their coupling. Feeding those connections to a topological sort would be meaningless (and would usually produce a cycle, since electrical connections are undirected).
Cross-domain connections are not edges either. This is the surprising one. When a control block commands an electrical device, you might expect the engine to force the control block to run first. It does not. There is no ordering constraint between the domains at all, which is what allows them to be evaluated independently — and, on a multi-core run, genuinely in parallel. Section 17.4 explains what makes that safe.
Edges into a unit delay are not edges. A unit_delay is defined to output its input from the previous step, so it does not care whether its source has run yet. Dropping its incoming edges is what lets it break a feedback loop.
Underneath the sort there is a second, coarser mechanism: each executable carries an execution phase, with control blocks at phase 0, the electrical pre-solve (which stamps switch conductances and injections) at 10, the network solve at 40, and the electrical post-solve (which reads node voltages back out) at 50. These are a tie-break, not a dependency: when several blocks are ready to run, the lowest phase goes first. The phases keep the electrical sub-loop in a sensible internal order — you cannot read node voltages before solving for them — but they impose nothing between control and electrical.
Feedback needs a delay somewhere
A topological order exists only for an acyclic graph, and a feedback loop is a cycle by definition. Two cases, with different outcomes.
The loop closes through the network. A controller senses a voltage, computes, and commands a device; the device changes the voltage. On the graph this is not a cycle at all, because neither cross-domain connection is an edge. The loop sorts cleanly with no work from you, and the delay that makes it physically sensible is supplied by the mechanism of §17.4.
The loop stays inside the control domain. A block's output reaches its own input through control blocks only. That is a genuine cycle — the classic algebraic loop, in which a block would need its own output in order to compute its own output. Break it with an explicit unit_delay on the feedback path. Its output is its input from the previous step, so it imposes no ordering constraint, and you choose where the delay lands.
Leave the cycle unbroken and the run still proceeds. When Kahn's algorithm stalls with blocks remaining, the engine picks the one with the fewest unsatisfied inputs, forces it into the schedule, logs a warning to the log panel, and carries on. The forced block reads a stale value for its unsatisfied input, which is to say the engine has silently inserted a one-step delay — on an edge chosen by a tie-break rather than by you. The numbers you get are plausible and the run completes, which is exactly what makes it worth heeding the warning. If a loop needs a delay, put it where you want it.
17.4 The delay between control and electrical
Here is the mechanism that makes everything above hold together, and it is worth stating precisely because it is the most common source of "why is my controller a step behind?"
Every signal value in a simulation lives in a slot with two cells: a current cell and a prev cell. Producers always write current. At the start of every step the engine copies the whole current plane into the prev plane in one bulk memory copy, so for the entire duration of a step, prev holds a frozen, consistent snapshot of the previous step while current is being filled in.
When the circuit is compiled, each connection is classified once — never again at run time — into which of the two cells the consumer will read:
- Same-domain connections (control to control, or electrical to electrical) read
current. The topological order guarantees the producer already ran this step, so the value is fresh and the block behaves exactly as its transfer function says. - Cross-domain connections read
prev. Both directions. Control to electrical, and electrical to control.
That last rule is the whole story. It is why the topological sort can ignore cross-domain edges: a consumer reading prev has no dependency on the producer's execution this step, because it is not reading anything the producer writes this step. The two domains genuinely cannot race, whatever order they run in.
Counting the steps
The arithmetic follows directly. A controller commanding a device sees its command take effect one step later. A controller sensing the network sees measurements that are one step old. And a loop that leaves the control domain and comes back — the normal case for any real regulator — accumulates two steps of lag.
You can see all three numbers on a scope. The control loop delay sample is built for nothing else: a square-wave command drives a breaker, and the resulting load current is brought back into the control domain. At a 1 ms step the command falls at
Is this a defect?
No — it is the honest representation of a system in which the controller and the plant are separate machines. A real regulator samples a transducer, computes for a finite time, and drives an actuator; it is never in instantaneous communication with the flux in an iron core. A simulator that pretended otherwise would have to iterate the two domains to convergence within every step, at a cost far out of proportion to the physics.
What matters is keeping the lag small compared with the dynamics you are studying. Two steps at 20 µs is 40 µs against an exciter time constant of half a second — four orders of magnitude, and utterly negligible. Two steps at 1 ms against a switching converter running at 10 kHz is 2 % of a switching period, which is starting to matter. The rule of thumb is the same one that governs every other choice of
Three related cases
The same prev rule shows up in three other places, and they are worth recognizing so they do not surprise you:
- Portless named inputs — signals a component picks up by name rather than through a drawn wire — always read
prev, regardless of domain. They are always one step delayed. - Across a rate boundary. In a multi-rate subsystem, a signal read from a different rate group is forced to
preveven within the control domain, giving a one-substep delay. See multi-rate monitoring. - A unit delay feeding across the domain boundary costs two steps, not one: the
unit_delaycontributes its own step, and the domain crossing contributes another.
17.5 Naming, observables and wire-less routing
The last piece of the control domain is bookkeeping, and it is what makes a model readable and measurable.
Any control signal you give a name becomes an observable — something you can put on a scope, plot, or export. There are three ways to name one, and they are equivalent as far as the engine is concerned:
- Label a wire. Give the wire between two blocks a signal name and the value travelling along it is recorded.
- Name a component's monitored output. Most components have
measure_…toggles with an accompanying name field — a resistor's current, a breaker's state, a controller's field-voltage output. Turning one on publishes that internal quantity under the name you choose. - Publish with a Go-To. A
gotoblock takes a signal in and publishes it under a name, with no wire leaving it.
Whatever publishes a name, a from block reads it back somewhere else on the sheet, with no wire drawn between them. This is not cosmetic. A generator, its exciter, its governor and its stabilizer exchange half a dozen signals, and drawing all of them as wires across a one-line diagram makes the diagram unreadable. Naming lets you keep the power diagram looking like a power diagram and the control diagram looking like a control diagram.
One caution follows from §17.4: a Go-To/From pair inside the control domain is a plain alias and costs nothing, but a From reading a signal published by an electrical component is a domain crossing and costs its step, exactly as a wire would. Naming changes how the sheet looks, never what the engine computes.
17.6 Lab: counting the steps
Lab 17A — measure the cross-domain delay
Open the control loop delay demo in simulator →
(Full description: control loop delay.)
The circuit is a square-wave command driving a breaker that feeds a 10 Ω load from a 10 V source, with the load current brought back into the control domain through a From tag and a unity gain. The run uses a deliberately coarse 1 ms step so that one step is one visible millisecond.
Run it and plot cmd, brk, i_load and i_ctl on the same axes. Zoom in on the edge at
| Signal | Domain | Edge time | Steps behind cmd |
|---|---|---|---|
cmd | control | 0.050 s | 0 |
brk, i_load | electrical | 0.051 s | 1 |
i_ctl | control | 0.052 s | 2 |
Then change the time step to 100 µs and re-run. Every gap shrinks by a factor of ten, because the delay is two steps and not a fixed span of time. That is the single most useful thing to internalize about it.
Lab 17B — find the loop in a working controller
Open the PWM power controller in simulator →
(Full description: PWM power control.)
This is the loop from Chapter 4, revisited with the machinery of this chapter. Trace it and answer three questions on the canvas. Which connections are same-domain and therefore exact within the step? Which two are the domain crossings that give the loop its lag? And why does this circuit sort without needing a unit_delay anywhere, even though it is unambiguously a feedback loop? (Answers in §17.8.)
17.7 Summary
- The control domain is a second solver with a different principle: every block is a plain assignment
, so there is no matrix and no simultaneous solution — only an order. - Continuous control blocks are discretized with forward Euler, not the trapezoidal rule used for the network's companion models. The first-order lag is
. - That choice is first-order accurate and conditionally stable, requiring
. The bound is irrelevant at EMT time steps unless you set a time constant smaller than the step, in which case the block diverges. - A zero time constant removes its block rather than dividing by zero — the convention that lets published controller parameter sets contain meaningful zeros.
- Blocks are evaluated in a topological order computed once at compile time. The sort ignores electrical-to-electrical connections, cross-domain connections, and edges into a unit delay.
- An algebraic loop inside the control domain needs an explicit
unit_delay. Without one the engine breaks the cycle itself, warns, and puts the delay wherever the tie-break lands. - Every cross-domain signal reads the previous step's value, in both directions. One step out, one step back, two steps around a loop that closes through the network.
- Naming a signal — by wire label, by a component's monitor, or with a Go-To — makes it an observable, and lets a
Fromread it anywhere on the sheet without a drawn wire.
17.8 Problems
Problem 17.1. A first-order lag with
Solution 17.1
Here
The steady state is where
Problem 17.2. A colleague sets a transducer time constant to 10 µs in a model running at
Solution 17.2
Forward Euler on a lag is stable only for
Fix one: reduce the time step below 20 µs. Fix two: set the time constant to zero, which by the zero-parameter convention makes the block a pass-through.
Use the second. A 10 µs time constant carries no information in a study resolved at 50 µs — it is far faster than anything else being modelled — so representing it as instantaneous is both cheaper and more honest than dragging the whole simulation down to resolve a dynamic nobody is studying.
Problem 17.3. A sheet contains control blocks gain.
Solution 17.3
With
With gain, every connection is same-domain and every one is an edge. The graph is unit_delay on whichever edge you want the delay to land — after which the loop carries exactly one step, not two.
The pair is worth comparing. Replacing a physical device with a "mathematically equivalent" control block does not preserve the timing: it changes the loop from two steps of delay to one, and turns a graph that sorts silently into one that warns.
Problem 17.4. In Lab 17B, the PWM power controller is a closed feedback loop, yet it needs no unit_delay and produces no cycle warning. Why? Contrast this with a loop built entirely from gain and add2 blocks.
Solution 17.4
The PWM loop leaves the control domain (the gate signal commands an IGBT) and re-enters it (the sensed load voltage and current come back). Neither crossing is an edge in the dependency graph, because cross-domain consumers read the previous step's value and therefore have no execution dependency on the producer. With both crossings removed the remaining graph is a simple chain, which sorts trivially. The physical delay the loop needs is supplied by the prev reads — two steps around.
A loop of pure gain and add2 blocks never leaves the control domain, so every edge is a same-domain edge reading current, every edge constrains the order, and the cycle is real. That is an algebraic loop and it needs an explicit unit_delay.
Problem 17.5. A controller reads a machine's terminal voltage, computes a field voltage, and commands the machine, all at
Solution 17.5
Generator case: the round trip is two steps, 40 µs, against a 0.4 s time constant — a ratio of
Converter case: two steps is 2 ms... except that a 10 kHz switching period is only 100 µs, so a 1 ms step cannot resolve the switching at all — the time step is the primary problem, not the delay. Choosing a step that does resolve switching, say 1 µs, makes the round trip 2 µs, or 2 % of a switching period, which is small but no longer negligible for a tightly-tuned inner current loop.
The general lesson: the cross-domain delay is never the binding constraint on
Problem 17.6. A tf1 block is given a dead time of 350 µs in a run at
Solution 17.6
Transport delay is stored in a ring buffer holding a whole number of samples,
To get closer, reduce the time step so the requested delay lands nearer a sample boundary: at 50 µs,
17.9 References
- P. Kundur, Power System Stability and Control, McGraw-Hill — representation of control blocks and transfer functions in stability programs.
- J. Arrillaga and N. R. Watson, Power Systems Electromagnetic Transients Simulation, IET Power and Energy Series 39 — control-system representation and the interface between control and network solutions.
- H. W. Dommel, Electromagnetic Transients Program (EMTP) Theory Book, Bonneville Power Administration — transient analysis of control systems (TACS) and its coupling to the network solution.
- A. B. Kahn, "Topological sorting of large networks," Communications of the ACM, vol. 5, no. 11, pp. 558-562, 1962 — the in-degree algorithm used to order the control blocks.
- J. C. Butcher, Numerical Methods for Ordinary Differential Equations, Wiley — accuracy and stability regions of explicit and implicit one-step methods.
Previous: Chapter 16 — The induction machine · Next: Chapter 18 — Excitation systems and AVRs.