Physics-informed GNN · Microgrids & distribution systems
Balanced Power Flow
Why does a feeder’s voltage fall when demand increases? Can local generation reverse a line’s power flow? How does reactive support change the result? In this lesson, derive the model, solve a concrete example, and test your predictions with an interactive experiment and Python.
1. Background: a snapshot of a microgrid
A power-flow calculation describes a steady-state operating point. Given network impedances, loads, generation, and a voltage reference, we solve for bus voltages and then calculate line currents, power transfers, and losses. For a microgrid or distribution feeder, these quantities help us inspect voltage drop, reverse flow, and loading before and after a switching action.
In a balanced three-phase system, phase magnitudes are equal and phase angles are separated by 120°. With symmetric impedances and balanced injections, one phase or the positive-sequence equivalent is sufficient. Balanced does not mean every bus has the same voltage, or that reactive power is zero.
Modeling assumptions
The equations and experiments in this chapter use the following assumptions:
- Sinusoidal steady state: voltages and currents are represented by RMS complex phasors.
- Balanced three-phase operation: the network is symmetric, and loads and distributed generation are balanced across the three phases. We use a single-phase equivalent, with P and Q expressed as three-phase totals.
- Series line impedances: each line has resistance R and reactance X. Line charging, shunts, and transformers are omitted.
- Constant-PQ loads and distributed generation: active power P and reactive power Q are specified at each operating point and remain fixed as the solver updates voltage. The model solves for both voltage magnitude and angle.
- One voltage reference: a slack bus has a specified voltage magnitude and angle. Its active and reactive supply balance the remaining network injections and line losses.
These assumptions define the general model. The worked example below introduces a specific feeder, assigns bus numbers, and sets its loads and generation.
For the later N-1 prediction task, a topology and operating point become model inputs, and solved voltages/currents can become labels or physical consistency checks. A converged solution is not automatically a secure operating point: we must separately check limits, connectivity, and the scope of the model. Dynamic stability and protection behavior require additional analysis.
2. Derive the balanced AC equations
Step A — Choose consistent bases
Use three-phase apparent-power base SB and line-to-line voltage base VLL,B. The phase-voltage base is VLL,B/√3. The impedance base is a per-phase impedance; P and Q expressed on this power base are three-phase totals.
Choose the bases before converting line impedances and power injections to per unit. Do not divide the total three-phase power by three again after applying the three-phase power base.
Step B — Build the network admittance matrix
Use lowercase yij = 1/zij for a branch’s series admittance and uppercase Yij for an entry of the bus admittance matrix. The diagonal entry Yii is bus i’s self-admittance: add the admittances of all connected branches at i. Each branch adds to both diagonal entries and subtracts from the two corresponding off-diagonal entries. Opening a line removes those four contributions. This example omits line charging, shunts, transformers, and mutual coupling.
From one branch to the full Y matrix
Start with the current at each end of a branch, then place its contributions in the matrix. Here i, j, and k are generic bus labels.
Distinguish the symbols: lowercase yij = 1/zij is the series admittance of branch i–j. Uppercase Yii is bus i's self-admittance; Yij is an off-diagonal entry of the bus admittance matrix. Each cell below shows the entry name followed by its value.
1 · Network and branch status
2 · This branch's contribution to Y (ΔY)
| i | j | |
|---|---|---|
| i | ΔYii= + yᵢⱼ | ΔYij= − yᵢⱼ |
| j | ΔYji= − yᵢⱼ | ΔYjj= + yᵢⱼ |
ΔY is this branch's increment to the matrix. Expanding the two end-current equations adds +y to each end's self-admittance and −y to the two corresponding off-diagonal entries. The full Y also includes contributions from the other connected branches.
3 · Add the connected branches
| Y | i | j | k |
|---|---|---|---|
| i | Yii= yᵢⱼ | Yij= − yᵢⱼ | Yik= 0 |
| j | Yji= − yᵢⱼ | Yjj= yᵢⱼ + yⱼₖ | Yjk= − yⱼₖ |
| k | Yki= 0 | Ykj= − yⱼₖ | Ykk= yⱼₖ |
Pale-blue diagonal entries are self-admittances, such as Yii; their values sum all connected branch admittances at that bus. With only i–j connected, Yii = yij. Connecting i–k gives Yii = yij + yik. Orange marks only the traced branch's contribution.
Step C — Convert currents into complex-power injections
Take positive net injection as supply to the network: Pspec = PG − PD and Qspec = QG − QD. For Vi = viejθᵢ, combine Kirchhoff’s current law with S = VI*:
Combine generation and demand into net bus injection
Write Yij = Gij + jBij and δij = θi − θj. Expanding the complex product gives two real equations at each bus:
These equations retain resistance and reactive coupling. That matters in a distribution feeder, where an approximation that discards R can miss an important part of voltage drop and losses.
Step D — Decide what is known at each bus
| Bus type | Specified | Solved |
|---|---|---|
| Slack / reference | Voltage magnitude and angle | P and Q injection |
| PQ | Net P and Q | Voltage magnitude and angle |
| PV | P and voltage magnitude | Q and angle |
Under this chapter’s assumptions, loads and fixed-PQ distributed generators are represented at PQ buses. The unknowns are the voltage angles and magnitudes at those buses; the slack voltage is fixed. Collect them in x = [θPQT, vPQT]T. The PV bus type is listed for context: it represents voltage regulation and is not used in this experiment. This bus classification and nonlinear power-balance formulation are described in the MATPOWER AC power-flow manual.
Step E — Solve with Newton–Raphson
This chapter uses Newton–Raphson (NR) to build a general AC power-flow solution from nodal admittance, power mismatch and simultaneous Jacobian-based voltage corrections. Although the example is a distribution feeder, its experiment can close a tie and form a loop, so NR provides a method that does not require a tree topology.
Three-phase balance is not a requirement for NR. A balanced radial network can also use backward/forward sweep; an unbalanced network can be formulated for three-phase NR. The next chapter exploits a single-source, radial four-wire structure and uses current-summation sweeps to make the shared neutral and phase-voltage updates visible. See the algorithm comparison and applicability discussion.
Start with flat voltages and zero angles. Compute the mismatch between specified and calculated P/Q. Let J be the derivatives of the calculated injections with respect to x, so the update sign below is positive:
What happens in a Newton iteration?
- Initialize voltagesChoose initial PQ voltage magnitudes and angles.
x⁽⁰⁾ = [θ, v] - Calculate mismatchUse V and Y to calculate P/Q and compare with the specifications.
Δs = sˢᵖᵉᶜ − s(x) - Check convergenceIs the largest mismatch below tolerance? If yes, return voltages; otherwise continue.
‖Δs‖∞ < ε ? - Solve and updateUse the Jacobian for the correction, then apply step size α.
J Δx = Δsx ← x + α Δx
The implementation uses an analytic Jacobian, pivoted elimination, and a backtracking step α to reduce the mismatch while keeping positive voltage magnitudes. It stops when ‖Δs‖∞ < 10−10 pu, or reports failure after a stalled step or 30 updates. Failure of this algorithm alone does not prove that no physical solution exists.
Show the Jacobian entries
For i ≠ j:
For i = j:
The corresponding complex-matrix derivatives are documented in MATPOWER's voltage-derivative reference.
Step F — Recover branch flows and losses
For each active line, calculate the current and the powers injected into the line at both ends. Their sum is the branch’s complex loss. Since this model contains only series impedance, current magnitude is the same at both ends.
3. Worked example: a 400 V feeder
Set up the feeder and its buses
Consider a three-bus, 400 V distribution feeder. Bus 1 is the upstream source, which connects to bus 2 through line 1–2. Line 2–3 supplies the downstream bus 3. A normally open tie line 1–3 can be closed in the later experiment to provide an alternative path.
Example network: a reference source, two PQ buses, and a tie
Swipe sideways to see the full diagram.
| Bus | Equipment and specified quantities | Power-flow type |
|---|---|---|
| 1 | Upstream source, with voltage fixed at 1∠0° pu | Slack / reference |
| 2 | 120 kW load at PF = 0.95 lagging | PQ |
| 3 | 180 kW load at PF = 0.95 lagging, plus 50 kW distributed generation at unity power factor | PQ |
The inverter at bus 3 is modeled as a specified P, Q injection: PG = 50 kW and QG = 0. It does not regulate the bus voltage. The bus remains PQ, with the generator and load combined into one net injection.
Choose SB = 1 MVA and VLL,B = 0.4 kV. Then ZB = 0.16 Ω and IB = 1443.38 A. The lines have these per-phase impedances:
| Line | Impedance (Ω) | Impedance (pu) |
|---|---|---|
| 1–2 | 0.012 + j0.008 | 0.075 + j0.050 |
| 2–3 | 0.008 + j0.006 | 0.050 + j0.0375 |
| 1–3, normally open tie | 0.022 + j0.014 | 0.1375 + j0.0875 |
With bus 1 as the reference and buses 2 and 3 as PQ buses, the four unknowns are x = [θ₂, θ₃, v₂, v₃]T.
Form the injections and solve
1. Convert load power factors. QD = PD tan(arccos 0.95), giving 39.44 kvar at bus 2 and 59.16 kvar at bus 3.
2. Form net injections. S₂spec = −0.120 − j0.03944 pu and S₃spec = −0.130 − j0.05916 pu. The 50 kW generator offsets part of bus 3’s active demand; it does not offset its reactive demand.
3. Take the first Newton step. At the flat start, calculated injections are zero, so the mismatch is [−0.120, −0.130, −0.03944, −0.05916]T. Build J at that operating point, solve for Δx, and repeat.
| 4. Check the solution. The baseline gives | V₂ | ≈ 0.97559 pu, | V₃ | ≈ 0.96657 pu, θ₂ ≈ −0.2994°, θ₃ ≈ −0.4159°, and total active loss ≈ 6.8389 kW. The slack supplies approximately 256.8389 kW, satisfying 256.8389 + 50 − 300 = 6.8389 kW. |
Predict what doubling both loads will do before selecting High demand below. Compare the resulting voltage drop and loss with the baseline; the relationship is nonlinear.
4. Experiment: change the operating point
Move one control at a time. The phase diagram above shows the three-phase voltage at bus 3. It updates together with the network, voltage profile, current loading, and Newton-iteration table, all from the same operating point. Opening a radial branch isolates buses; a closed tie can provide an alternative path.
A three-bus low-voltage feeder
400 V · 1 MVAArrows: active-power direction. Line width: current. Red and text labels: violations. Positive reactive injection supplies Q; negative absorbs Q.
Initializing experiment…
Voltage profile
Line current loading
The 0.95–1.05 pu voltage band and adjustable current limits are teaching references, not equipment ratings or a compliance standard.
Bus, branch, and Newton-iteration results
| Bus | |V| (pu) | θ (°) | P (kW) | Q (kvar) |
|---|
P and Q are total three-phase net injections: positive supplies, negative consumes.
| Line | P from (kW) | I (A) | Loss (kW) |
|---|
| Iteration | ‖ΔS‖∞ (pu) | |V₂| (pu) | |V₃| (pu) |
|---|
5. Edit and run Python
Turn the worked example into a code experiment: read the input dictionary case, inspect the source that defines the solver, then use main() to organize the calculation and output. Run it unchanged to check the example voltages and loss, then change one parameter using the comments. The slider lab uses the original JavaScript model for immediate feedback; the Python panel runs the editable source below.
How the code fits together
- caseControls provide an input dictionary
- balanced_power_flow.pySource defines the model functions
- main(case) → resultMain computes, prints and returns a result to plot
When you press Run, the page copies the controls into case, executes the folded model source, then executes your editable main program. This makes the source's functions available directly to main(), without a separate import statement. Read the three parts in that order.
1 · Inputs: expand the current case and parameter meanings
These are the inputs for the next run and update with the controls. Copying them into parameters inside main() lets you test another case without changing the controls.
Reading parameters…
| Parameter | Meaning and units |
|---|---|
load_scale | Multiplier applied to the 120 kW and 180 kW base demands. |
power_factor | Load power factor; determines Q = P tan(arccos(pf)). |
dg_kw / q_support_kvar | Fixed generation at bus 3: three-phase total kW / kvar. |
slack_pu | Bus 1 voltage magnitude in pu; its angle is fixed at 0°. |
r_scale / x_scale | Multipliers on the branch resistances / reactances. |
current_limit_a | Current limit in A, used to check the solution. |
topology / open12 / open23 / open13 | radial or meshed; True opens the corresponding branch. |
2 · Model source: balanced_power_flow.py (expand to inspect / edit)
Dependencies: cmath (complex phasors) and math (trigonometry) are Python standard-library modules. No NumPy or pandapower is needed.
This is the source actually executed by Python. Use the table to connect its functions to this chapter's equations. Edits take effect on the next main-program run. The slider lab keeps the original JavaScript model as a reference.
| Function in the source | What it does |
|---|---|
network(options) | Input settings → line impedances, Y matrix, specified P/Q; internally converts to pu. |
injections(y, vm, theta) | Y and voltage estimate → calculated nodal P/Q using S = V(YV)*. |
jacobian(y, vm, theta) | Voltage estimate → the 4 × 4 derivatives of P/Q at PQ buses 2 and 3. |
linear_solve(matrix, rhs) | J and mismatch → Newton correction Δx, using Gaussian elimination. |
solve(options) | Calls the functions above, iterates to convergence, and returns voltages, flows, losses and limit checks. |
3 · Main program: what does main() do?
The main program copies / changes inputs → calls the model → checks the solution → prints → returns the result. solved is the result dictionary inside the function; the final result = main(case) passes it to the page's plot. print() writes to the output panel and does not draw the plot.
First run the example unchanged and compare it with the worked example. Then remove the # before parameters["q_support_kvar"] = 60 and compare voltage and loss with reactive support.
The first run downloads Python and needs internet access. Computation runs in your browser; no installation is needed. The solver uses only the Python standard library.
Ready to run.
How to read the output
ok reports numerical convergence; buses contains vm_pu and theta_deg for buses 1–3; loss_kw is total active loss; slack_p_kw is source power; violations lists limit violations. Convergence alone does not mean limits are satisfied.
Download experiment includes the current inputs, model source and main program in one runnable .py file.
Output appears here.
Code result: voltage profile
6. Practice and explain
- Power factor: hold active demand fixed and reduce the load power factor from 0.95 to 0.80. Record the current, minimum voltage, and loss. Explain the change using S = P + jQ and I = (S/V)*.
- Reactive support: at demand multiplier 2.0, inject 60 kvar at bus 3. Compare with the unsupported case. Can all voltage violations be removed? Test rather than assume.
- N-1 topology: open line 1–2 in the radial case, then repeat with the tie closed. Distinguish an isolated bus, a converged but limit-violating state, and a state within the teaching limits.
Write a sweep: in Python, solve for demand multipliers from 0.5 to 2.0. Print V₃ and loss at each step. Keep the result dictionary from the last successful solve to display its voltage profile.
7. Scope and next steps
This is a steady-state, balanced, constant-PQ teaching model with a fixed slack voltage. It omits phase imbalance, voltage-dependent loads, transformer/tap models, inverter capability limits, source limits, line charging, and protection. An isolated component is reported as outside the single-slack model; its DG is not automatically converted into a grid-forming source. Use the next Unbalanced Power Flow module to relax phase symmetry, and PandaPower-based Implementation for a broader modeling workflow.
References
- MATPOWER: AC power-flow formulation — bus types and Newton formulation.
- MATPOWER: power-injection derivatives — analytic voltage sensitivities.
- pandapower: balanced AC power flow — algorithms for the later implementation module.
- Pyodide: Python in a browser worker — the code runner’s execution approach.