Remote Voltage Control
A machine regulates the voltage magnitude at a bus that is not its own connection point: its reactive output is the actuator, the voltage at the target bus the controlled variable. Sparlectra implements this as the outer-loop controller MachineVoltageControl on the control framework.
Why this is not just a PV bus
A PV bus couples actuator ($Q_g$) and controlled variable ($|V|$) at one node. With remote regulation the roles separate: at the machine bus $m$ the reactive injection is free, at the target bus $t$ the magnitude $|V_t|$ is prescribed without an adjustable injection. Folding this into the Newton iteration (drop the $Q$ mismatch at $m$, add $|V_t| - V^{\mathrm{set}}$) changes the Jacobian structure, interacts with Q-limit switching and couples buses that share no branch. Sparlectra keeps the inner solver untouched: the machine stays a PQ injection in every inner solve, and its reactive setpoint moves between solves.
The scalar control problem
Between two solves the controller sees the scalar map
\[Q_m \mapsto V_t(Q_m),\]
with everything else held by the power flow. Around an operating point the map is close to linear; its slope is the voltage sensitivity
\[s = \frac{\partial V_t}{\partial Q_m} > 0,\]
positive for any working actuator, falling with the electrical distance between $m$ and $t$, and near zero when the target bus is far away or held stiff by nearby sources.
Secant iteration
The controller solves $V_t(Q_m) = V^{\mathrm{set}}$ by secant iteration, without Jacobian or probe solves:
Bootstrap. The first move is 25 % of the remaining reactive headroom in the expected direction: voltage too low, toward
qmax_mvar; too high, towardqmin_mvar.Secant step. Every following move uses the two previous operating points $(Q^{k-1}, V^{k-1})$ and $(Q^k, V^k)$:
\[Q^{k+1} = Q^k + \frac{V^{\mathrm{set}} - V^k}{s_k}, \qquad s_k = \frac{V^k - V^{k-1}}{Q^k - Q^{k-1}},\]
clamped to $[Q_{\min}, Q_{\max}]$. This settles within the deadband in three to five outer iterations.
Slope guard. A measured slope $s_k \le s_{\min}$ with $s_{\min} = 10^{-6}$ pu/MVAr (the target barely responds, or other controllers moved the state) triggers the bootstrap step instead of a step toward the wrong bound.
Convergence: $|V_t - V^{\mathrm{set}}| \le$ deadband_vm_pu. ControlConfig.max_outer_iterations caps the loop.
Reactive limits: honest at_limit
The actuator range is the machine's reactive capability $[Q_{\min}, Q_{\max}]$ (the imported ReactiveCapabilityCurve evaluated at the scheduled P where one exists, else the scalar hull). When the secant step is clamped at a bound and the target is still outside the deadband, the controller parks with status at_limit, the outer-loop analogue of a PV bus switching to PQ. The report row carries at_limit = true, converged = false and the achieved voltage.
STATCOM mode: current-based limit
The constant box models a synchronous machine. A STATCOM is bounded by its converter current, so the deliverable reactive power scales with the terminal voltage,
\[Q_{lim}(V) = V \cdot S_{max}\]
with $S_{max}$ the rating at 1.0 pu (s_max_mva, or i_max_ka converted via $\sqrt{3}\,U_n I_{max}$ at registration). The machine's own minQ/maxQ are ignored: the converter current is the limit. The bounds $\pm V \cdot S_{max}$ follow the solved machine-bus voltage as a live bound (FACTS Devices); in range the mode behaves like the constant-Q controller.
Interaction with the rest of the solver
- Machine bus and target bus stay PQ; a PV or slack target is rejected (two authorities for one voltage).
- The machine is exempt from the Q-limit machinery: active-set switching only considers
isRegulatingprosumers (an RVC machine hasisRegulated = falseand no voltage-adjust controller), PV→PQ switching only acts on PV buses. The controller's clamping is the single limit instance. - Each applied step updates the machine's
ProSumer.qValand its bus-level generation sum by the same delta. - Tap and machine controllers run in the same outer loop (
run_control!evaluates all, applies by rank, re-solves; at equal priority, the default, all apply in the same pass). One machine controller per target bus; several machines at one bus targeting different buses pollute each other's measured sensitivities (more outer iterations). - A tap controller and a machine controller on one target bus:
addMachineVoltageControl!warns (a tap-regulated bus stays PQ) unless the machine has another priority and the same target within both deadbands; then the lower rank waits while the higher one is in its band. Otherwise reconfigure one of them. - Coordinated Q-sharing among several machines on one target (participation factors) is not implemented; the first machine claims the target.
API
| Use | Where |
|---|---|
| Call | addMachineVoltageControl! (keywords in the example), run_control!, collect_outer_controllers, printMachineControllerSummary |
| Config key | cgmes_import.machine_control (importCGMES(machine_control = true)): attaches the controllers for machines whose voltage RegulatingControl points at a foreign bus (CGMES Import) |
| Result field | report row: converged, at_limit, achieved voltage; STATCOM element row: the currently deliverable range |
| Example | examples/others/machine_remote_voltage_control.jl |
addMachineVoltageControl!(net;
bus = "GenBus", # machine's own bus (PQ machine required)
target_bus = "Load", # remote regulated bus (PQ required)
target_vm_pu = 1.02,
deadband_vm_pu = 1e-3, # convergence band
# qmin_mvar / qmax_mvar default to the machine's minQ/maxQ
)
# STATCOM variant: current-based limit instead of the constant box
addMachineVoltageControl!(net;
bus = "StatcomBus", target_bus = "Load", target_vm_pu = 1.0,
s_max_mva = 25.0, # converter rating at 1.0 pu; Q_lim = V * S_max
)
result = run_control!(net; controllers = collect_outer_controllers(net))
printMachineControllerSummary(stdout, net)