← Physics-informed GNN course

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.

These phasors illustrate balanced three-phase voltage at a bus: equal magnitudes and 120° separation. Voltages can differ between buses. Changing the operating point in the later experiment also updates this diagram.

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.

Trace branch
1 · Network and branch status
yᵢⱼyⱼₖyᵢₖ ijk VᵢVⱼVₖ
Checked branches are connected
Iᵢⱼ = yᵢⱼ (Vᵢ − Vⱼ)
2 · This branch's contribution to Y (ΔY)
ij
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
Yijk
iYii= yᵢⱼYij= − yᵢⱼYik= 0
jYji= − yᵢⱼYjj= yᵢⱼ + yⱼₖYjk= − yⱼₖ
kYki= 0Ykj= − 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.

This series-only diagram omits shunts and transformers: self-admittance Yii = Σ yiℓ over connected branches; off-diagonal Yij = −yij, or zero with no connected direct branch. + and − are algebraic coefficients of complex admittances, not signs of their numerical values.

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

GenerationPɢ , Qɢ i DemandPᴅ , QᴅNet to networkPˢᵖᵉᶜ = Pɢ − Pᴅ , Qˢᵖᵉᶜ = Qɢ − Qᴅ
Arrows define the positive directions. When demand exceeds generation, net injection is negative and the network supplies the bus. Compute P and Q separately: active generation does not automatically offset reactive demand.

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 typeSpecifiedSolved
Slack / referenceVoltage magnitude and angleP and Q injection
PQNet P and QVoltage magnitude and angle
PVP and voltage magnitudeQ 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?

  1. Initialize voltagesChoose initial PQ voltage magnitudes and angles.x⁽⁰⁾ = [θ, v]
  2. Calculate mismatchUse V and Y to calculate P/Q and compare with the specifications.Δs = sˢᵖᵉᶜ − s(x)
  3. Check convergenceIs the largest mismatch below tolerance? If yes, return voltages; otherwise continue.‖Δs‖∞ < ε ?
  4. Solve and updateUse the Jacobian for the correction, then apply step size α.J Δx = Δsx ← x + α Δx
Step 3 meets tolerance → return V and calculate branch flowsAfter step 4 ↩ return to step 2
Recompute the mismatch and Jacobian each round. A stalled step or iteration limit ends the algorithm without convergence. Operating-limit checks are performed separately on a converged result.

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

1–3 tie: normally openz₁₂z₂₃ 123 1∠0° puReference Load 120 kWPF = 0.95Load 180 kWDG 50 kWQɢ = 0

Swipe sideways to see the full diagram.

Solid branches form the radial 1 → 2 → 3 feeder; the dashed branch is the spare tie. Combine demand and generation at bus 3 as a net PQ injection. The experiment later changes generation and closes the tie.
BusEquipment and specified quantitiesPower-flow type
1Upstream source, with voltage fixed at 1∠0° puSlack / reference
2120 kW load at PF = 0.95 laggingPQ
3180 kW load at PF = 0.95 lagging, plus 50 kW distributed generation at unity power factorPQ

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:

LineImpedance (Ω)Impedance (pu)
1–20.012 + j0.0080.075 + j0.050
2–30.008 + j0.0060.050 + j0.0375
1–3, normally open tie0.022 + j0.0140.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 givesV₂≈ 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 MVA

Arrows: active-power direction. Line width: current. Red and text labels: violations. Positive reactive injection supplies Q; negative absorbs Q.

Minimum voltage—
Active-power loss—
Slack P / 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.

LineP 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

  1. caseControls provide an input dictionary
  2. balanced_power_flow.pySource defines the model functions
  3. 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…
ParameterMeaning and units
load_scaleMultiplier applied to the 120 kW and 180 kW base demands.
power_factorLoad power factor; determines Q = P tan(arccos(pf)).
dg_kw / q_support_kvarFixed generation at bus 3: three-phase total kW / kvar.
slack_puBus 1 voltage magnitude in pu; its angle is fixed at 0°.
r_scale / x_scaleMultipliers on the branch resistances / reactances.
current_limit_aCurrent limit in A, used to check the solution.
topology / open12 / open23 / open13radial 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 sourceWhat 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.

Download solver .py

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.

6. Practice and explain

What does “balanced” require at a bus?

  1. 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)*.
  2. 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.
  3. 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.
  4. Write a sweep: in Python, solve for demand multipliers from 0.5 to 2.0. PrintV₃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