ELE-380 · Lesson 6 of 12 · 32 min

Load flow

Load flow (or power flow) analysis answers the most basic question about a power system in steady state: given the loads and the generators' outputs and voltage settings, what are the voltages at every bus and the power flows on every line and transformer? It is the most frequently run calculation in power engineering. Planners use it to check that the network will stay within limits as loads grow and new generators or data centres connect; operators run it continuously in control centres, together with contingency analysis, to make sure that the loss of any element will not cause overloads or voltage problems. This lesson formulates the load flow problem, builds the bus admittance matrix, develops the Gauss-Seidel and Newton-Raphson solution methods and the fast decoupled and DC approximations, and shows how load flow supports voltage control and contingency analysis.

The bus admittance matrix

The network of lines and transformers, represented by their per-unit π equivalents (Lessons 2 and 4), is linear. Nodal analysis from ELE-202 relates the injected currents at the buses to the bus voltages through the bus admittance matrix:

\[\mathbf{I} = Y_{\mathrm{bus}}\,\mathbf{V}\]

It is built by inspection: each diagonal element \(Y_{ii}\) is the sum of all admittances connected to bus \(i\), including half the charging susceptance of each line and any shunt elements; each off-diagonal element \(Y_{ij}\) is the negative of the series admittance between buses \(i\) and \(j\), and zero if they are not directly connected. Because each bus connects to only a few others, \(Y_{\mathrm{bus}}\) is extremely sparse: a 10,000-bus network might have only three or four non-zero entries per row, and sparse matrix techniques make large studies fast.

Diagram of a four-bus test network. Bus 1, the slack bus with a generator, connects to buses 2 and 3. Bus 2 has a generator and a small load and connects to buses 3 and 4. Bus 3 and bus 4 have loads and are connected to each other. Each line is labelled with its series impedance in per unit. Next to the diagram, the four-by-four bus admittance matrix is shown with its non-zero pattern: all diagonal elements, and off-diagonal elements for each connected pair, with zeros for buses 1 and 4, which are not directly connected.
A four-bus network and the sparsity pattern of its bus admittance matrix.

The load flow equations

The complex power injected at bus \(i\) is \(S_i = V_iI_i^*\). Substituting the nodal equations gives the power flow equations:

\[P_i - jQ_i = V_i^*\sum_{k=1}^{n} Y_{ik}V_k\]

Writing \(V_i = |V_i|\angle\theta_i\) and \(Y_{ik} = G_{ik} + jB_{ik}\), the real and imaginary parts are

\[P_i = |V_i|\sum_k |V_k|\big(G_{ik}\cos\theta_{ik} + B_{ik}\sin\theta_{ik}\big), \qquad Q_i = |V_i|\sum_k |V_k|\big(G_{ik}\sin\theta_{ik} - B_{ik}\cos\theta_{ik}\big)\]

with \(\theta_{ik} = \theta_i - \theta_k\). Each bus has four quantities, \(P\), \(Q\), \(|V|\) and \(\theta\); two are specified and two are found. Buses are classified accordingly:

Bus types in load flow.
Bus typeSpecifiedUnknownTypical example
Slack (swing, reference)\(|V|\) and \(\theta = 0\)\(P\), \(Q\)One large generator; supplies the losses, which are not known in advance
PV (generator)\(P\) and \(|V|\)\(Q\), \(\theta\)Generators with voltage regulators, synchronous condensers, STATCOMs
PQ (load)\(P\) and \(Q\)\(|V|\), \(\theta\)Load buses and buses with no generation

The equations are non-linear in the voltages, so there is no closed-form solution; they are solved by iteration. A load flow may also have no solution at all, when the loads exceed what the network can deliver; this corresponds physically to voltage collapse, and the point at which solutions cease to exist is the voltage stability limit.

Gauss-Seidel method

The earliest computer method rearranges the equation for each PQ bus to give its voltage in terms of the others:

\[V_i^{(k+1)} = \frac{1}{Y_{ii}}\left[\frac{P_i - jQ_i}{V_i^{(k)*}} - \sum_{j < i} Y_{ij}V_j^{(k+1)} - \sum_{j > i} Y_{ij}V_j^{(k)}\right]\]

and sweeps through the buses repeatedly, using each new value immediately. For PV buses, \(Q_i\) is first estimated from the current voltages and the voltage magnitude is reset to its setpoint after each update. Gauss-Seidel is simple and needs little memory, but its convergence slows dramatically as networks grow, often needing hundreds of iterations, and it is now used mainly for small systems and to provide starting values.

Newton-Raphson method

Newton-Raphson linearizes the equations around the current estimate and solves for a correction. The unknowns are the angles of all non-slack buses and the voltage magnitudes of the PQ buses; the equations are the mismatches between scheduled and calculated power, \(\Delta P_i = P_i^{\mathrm{sch}} - P_i(\theta, |V|)\) at PV and PQ buses and \(\Delta Q_i\) at PQ buses. Each iteration solves

\[\begin{pmatrix} \Delta\mathbf{P} \\ \Delta\mathbf{Q} \end{pmatrix} = \begin{pmatrix} \partial\mathbf{P}/\partial\boldsymbol\theta & \partial\mathbf{P}/\partial|\mathbf{V}| \\ \partial\mathbf{Q}/\partial\boldsymbol\theta & \partial\mathbf{Q}/\partial|\mathbf{V}| \end{pmatrix}\begin{pmatrix} \Delta\boldsymbol\theta \\ \Delta|\mathbf{V}| \end{pmatrix}\]

for the corrections, updates the voltages, and repeats. The matrix of partial derivatives is the Jacobian; it is sparse with the same pattern as \(Y_{\mathrm{bus}}\), and is recomputed every iteration. Newton-Raphson converges quadratically near the solution, roughly doubling the number of correct digits each iteration, and typically reaches a mismatch below \(10^{-6}\) pu in three to five iterations regardless of system size. It is the standard method in commercial software.

Newton-Raphson load flow for the four-bus network, with line flows and losses
import numpy as np

# lines: from, to, R, X, total charging B (pu on 100 MVA)
lines = [(1, 2, 0.010, 0.050, 0.040), (1, 3, 0.020, 0.080, 0.040), (2, 3, 0.015, 0.060, 0.030),
         (2, 4, 0.020, 0.070, 0.030), (3, 4, 0.010, 0.040, 0.020)]
n = 4
Y = np.zeros((n, n), complex)
for f, t, R, X, B in lines:
    i, j = f - 1, t - 1
    y = 1 / complex(R, X)
    Y[i, i] += y + 0.5j * B; Y[j, j] += y + 0.5j * B
    Y[i, j] -= y; Y[j, i] -= y

# bus data: type (0 slack, 1 PV, 2 PQ), |V| setpoint, P generation, P load, Q load
btype = np.array([0, 1, 2, 2])
Vset = np.array([1.02, 1.01, 1.0, 1.0])
Pg = np.array([0.0, 1.0, 0.0, 0.0])
Pd = np.array([0.0, 0.2, 1.2, 0.9]); Qd = np.array([0.0, 0.1, 0.5, 0.35])
Psch, Qsch = Pg - Pd, -Qd

V = Vset.astype(complex)
pv_pq = np.where(btype > 0)[0]; pq = np.where(btype == 2)[0]
for it in range(10):
    S = V * np.conj(Y @ V)
    mis = np.concatenate([Psch[pv_pq] - S.real[pv_pq], Qsch[pq] - S.imag[pq]])
    print(f"iteration {it}: largest mismatch = {np.abs(mis).max():.2e} pu")
    if np.abs(mis).max() < 1e-8:
        break
    Ibus = Y @ V
    dS_dVa = 1j * np.diag(V) @ np.conj(np.diag(Ibus) - Y @ np.diag(V))
    dS_dVm = np.diag(V) @ np.conj(Y @ np.diag(V / abs(V))) + np.conj(np.diag(Ibus)) @ np.diag(V / abs(V))
    J = np.block([[dS_dVa.real[np.ix_(pv_pq, pv_pq)], dS_dVm.real[np.ix_(pv_pq, pq)]],
                  [dS_dVa.imag[np.ix_(pq, pv_pq)], dS_dVm.imag[np.ix_(pq, pq)]]])
    dx = np.linalg.solve(J, mis)
    Va = np.angle(V); Vm = abs(V)
    Va[pv_pq] += dx[:len(pv_pq)]; Vm[pq] += dx[len(pv_pq):]
    V = Vm * np.exp(1j * Va)

S = V * np.conj(Y @ V)
print("bus  |V| pu  angle deg   P inj MW  Q inj Mvar")
for k in range(n):
    print(f"{k + 1:3d}  {abs(V[k]):.4f}  {np.degrees(np.angle(V[k])):8.3f}  {100 * S[k].real:9.1f}  {100 * S[k].imag:9.1f}")
loss = 0
print("line   P from MW  Q from Mvar  loss MW")
for f, t, R, X, B in lines:
    i, j = f - 1, t - 1
    y = 1 / complex(R, X)
    Sij = V[i] * np.conj((V[i] - V[j]) * y + V[i] * 0.5j * B)
    Sji = V[j] * np.conj((V[j] - V[i]) * y + V[j] * 0.5j * B)
    loss += (Sij + Sji).real
    print(f"{f}-{t}   {100 * Sij.real:8.1f}  {100 * Sij.imag:10.1f}  {100 * (Sij + Sji).real:7.2f}")
print(f"total losses {100 * loss:.2f} MW; slack generation {100 * S[0].real:.1f} MW")
Output
iteration 0: largest mismatch = 1.10e+00 pu
iteration 1: largest mismatch = 3.09e-02 pu
iteration 2: largest mismatch = 6.31e-05 pu
iteration 3: largest mismatch = 2.64e-10 pu
bus  |V| pu  angle deg   P inj MW  Q inj Mvar
  1  1.0200     0.000      133.6       36.4
  2  1.0100    -1.410       80.0       46.8
  3  0.9823    -3.355     -120.0      -50.0
  4  0.9770    -3.853      -90.0      -35.0
line   P from MW  Q from Mvar  loss MW
1-2       52.8         8.4     0.28
1-3       80.8        28.0     1.43
2-3       64.0        30.1     0.75
2-4       68.5        27.8     1.09
3-4       22.7         6.4     0.06
total losses 3.60 MW; slack generation 133.6 MW

The mismatch falls from 1.1 pu to \(10^{-10}\) in three iterations, the hallmark of quadratic convergence. The generator at bus 2 holds its terminal at 1.01 pu by supplying 46.8 Mvar net of its local load; the load buses settle at 0.98 pu, and voltage angles decrease from the slack toward the loads, showing real power flowing "downhill" in angle. The slack generator supplies 133.6 MW: the 230 MW of load, less the 100 MW from bus 2, plus 3.6 MW of losses that could only be found by solving the load flow.

Worked problem

Building Y-bus

For the four-bus network, find \(Y_{12}\) and \(Y_{11}\). Line 1-2 has \(z = 0.01 + j0.05\) pu and total charging \(B = 0.04\); line 1-3 has \(z = 0.02 + j0.08\) and \(B = 0.04\).

Show solution
  1. Line 1-2: \(y_{12} = 1/(0.01 + j0.05) = 3.846 - j19.231\).
  2. Line 1-3: \(y_{13} = 1/(0.02 + j0.08) = 2.941 - j11.765\).
  3. \(Y_{12} = -y_{12} = -3.846 + j19.231\).
  4. \(Y_{11} = y_{12} + y_{13} + j(0.04 + 0.04)/2 = 6.787 - j30.956\).

Answer: \(Y_{12} = -3.85 + j19.23\) and \(Y_{11} = 6.79 - j30.96\) pu. \(Y_{14} = 0\) because buses 1 and 4 are not directly connected.

Fast decoupled load flow

At transmission level, \(X \gg R\) and angle differences across lines are small, so real power depends strongly on angles and weakly on voltage magnitudes, while reactive power depends strongly on magnitudes and weakly on angles (as in Lesson 4). The fast decoupled method exploits this by neglecting the off-diagonal blocks of the Jacobian and approximating the diagonal blocks by constant matrices \(B'\) and \(B''\) built from line reactances:

\[\frac{\Delta\mathbf P}{|\mathbf V|} = B'\,\Delta\boldsymbol\theta, \qquad \frac{\Delta\mathbf Q}{|\mathbf V|} = B''\,\Delta|\mathbf V|\]

The constant matrices are factorized once, so each iteration is very cheap. Convergence is linear rather than quadratic and needs more iterations, but the total time is often less, which makes the method popular for contingency screening, where thousands of cases are solved.

DC load flow

Going further, assume all voltage magnitudes are 1.0 pu, resistances are negligible and angle differences are small, so \(\sin\theta_{ik} \approx \theta_{ik}\). Then the flow on a line is simply

\[P_{ik} = \frac{\theta_i - \theta_k}{X_{ik}}, \qquad \mathbf P = B\,\boldsymbol\theta\]

a linear system, like a DC resistive circuit with angles playing the role of voltages and reactances the role of resistances. The DC load flow ignores reactive power, voltage magnitudes and losses, but it gives real power flows to within a few percent on transmission networks, needs no iteration, and is the basis of electricity market models (locational marginal prices), transmission expansion planning and fast contingency screening.

DC load flow and N-1 contingency screening of the four-bus network
import numpy as np

lines = [(1, 2, 0.050), (1, 3, 0.080), (2, 3, 0.060), (2, 4, 0.070), (3, 4, 0.040)]
rating = {(1, 2): 150, (1, 3): 150, (2, 3): 110, (2, 4): 120, (3, 4): 80}   # MW
P = np.array([0.0, 0.8, -1.2, -0.9])          # net injections (pu); bus 1 is the slack

def dc_flow(active):
    n = 4
    B = np.zeros((n, n))
    for f, t, X in active:
        i, j = f - 1, t - 1
        B[i, i] += 1 / X; B[j, j] += 1 / X; B[i, j] -= 1 / X; B[j, i] -= 1 / X
    theta = np.zeros(n)
    theta[1:] = np.linalg.solve(B[1:, 1:], P[1:])   # slack angle = 0
    return {(f, t): 100 * (theta[f - 1] - theta[t - 1]) / X for f, t, X in active}

base = dc_flow(lines)
print("base case flows (MW):", {f"{k[0]}-{k[1]}": float(round(v, 1)) for k, v in base.items()})
print("N-1 contingency analysis:")
for out in lines:
    flows = dc_flow([l for l in lines if l != out])
    worst = max(flows, key=lambda k: abs(flows[k]) / rating[k])
    load = abs(flows[worst]) / rating[worst]
    flag = "OVERLOAD" if load > 1 else "ok"
    print(f"  outage of {out[0]}-{out[1]}: worst line {worst[0]}-{worst[1]} at {abs(flows[worst]):6.1f} MW "
          f"({load:5.0%} of rating)  {flag}")
Output
base case flows (MW): {'1-2': 50.7, '1-3': 79.3, '2-3': 63.4, '2-4': 67.3, '3-4': 22.7}
N-1 contingency analysis:
  outage of 1-2: worst line 1-3 at  130.0 MW (  87% of rating)  ok
  outage of 1-3: worst line 2-3 at  114.7 MW ( 104% of rating)  OVERLOAD
  outage of 2-3: worst line 2-4 at  101.7 MW (  85% of rating)  ok
  outage of 2-4: worst line 3-4 at   90.0 MW ( 112% of rating)  OVERLOAD
  outage of 3-4: worst line 2-4 at   90.0 MW (  75% of rating)  ok

The DC flows agree with the full AC solution within about 2 MW (for example 79.3 MW against 80.8 MW on line 1-3), because losses and voltage magnitudes are ignored. The contingency screen shows why N−1 matters: the base case is comfortable, but losing line 1-3 overloads line 2-3, and losing line 2-4 forces all the power for bus 4 through line 3-4, overloading it by 12%. Remedies include re-dispatching generation (raising output at bus 2 relieves both), upgrading the overloaded lines, adding a line, or a special protection scheme that sheds load after the outage.

Two copies of the four-bus network side by side. On the left, the base case, each line labelled with its flow in megawatts and a coloured bar showing its loading against its rating, all below 70 percent. On the right, the case with line 2-4 out of service, drawn dashed; line 3-4 now carries 90 megawatts against an 80 megawatt rating and is highlighted red as overloaded.
N−1 contingency: losing line 2-4 overloads line 3-4.

Voltage control in load flow

Realistic load flows include the devices that regulate voltage:

  • Generator reactive limits: a PV bus holds its voltage only while its reactive output stays within the generator's capability. If the solution requires more, the bus is converted to a PQ bus with \(Q\) fixed at the limit, and its voltage is allowed to fall.
  • Transformer taps: on-load tap changers adjust the off-nominal ratio between iterations to hold a target voltage at a bus, within the tap range.
  • Switched shunt capacitors and reactors are switched in or out to keep voltages within a band.
  • Phase-shifting transformers adjust angles to control real power flow on a corridor.

Because these controls interact, load flow programs alternate between solving the network and adjusting the controls until both settle.

Worked problem

A PV bus hitting its limit

A generator bus is set to hold 1.03 pu, but the solution requires 180 Mvar from a unit whose maximum reactive output is 150 Mvar. What does the load flow do, and what happens to voltages?

Show solution
  1. The bus is switched from PV to PQ with \(Q = 150\) Mvar (net of any local load).
  2. The load flow is re-solved; with less reactive support, the bus voltage falls below 1.03 pu, and nearby load buses also fall.

Answer: The voltage at the generator bus falls below its setpoint, possibly to around 1.00 pu or lower, and neighbouring voltages drop. If they fall below limits, additional reactive support (capacitors, a STATCOM) or a different generator dispatch is needed. Reaching reactive limits one after another is a classic path to voltage collapse.

Using load flow

Load flow studies support most planning and operating decisions:

  • Interconnection studies for new generators and large loads, such as a data centre campus, test whether the network can supply the new load under N−1 conditions, and identify the upgrades the customer must fund.
  • Transfer capability studies find how much power can move between regions before a limit is reached.
  • Voltage stability studies trace PV curves (receiving voltage against load) by solving a sequence of load flows with increasing load until no solution exists, giving the margin to collapse.
  • Operators run a state estimator, which fits a load flow solution to thousands of real-time measurements, then contingency analysis on the estimated state every few minutes, and optimal power flow, which adds an objective such as minimum generation cost subject to all limits.
PV curve plotting load bus voltage in per unit against total load in megawatts. The upper branch starts at about 1.0 per unit at zero load and falls slowly, then faster, to a nose point around 0.7 per unit at maximum loadability. A lower dashed branch, the unstable solutions, returns from the nose toward lower voltages. A second curve, with added shunt capacitance, has its nose at higher load, showing an increased margin.
A PV curve: load flow solutions exist only up to the nose, the voltage stability limit.

Lesson 7 adds costs and limits to load flow to obtain economic dispatch and optimal power flow, and Lesson 12 applies contingency analysis to a planning study.

Common mistakes

  • MistakeOmitting the slack bus or specifying P at every bus.

    InsteadLosses are unknown until solved; one slack bus balances them.

  • MistakeIgnoring generator reactive limits.

    InsteadConvert PV buses to PQ at their limits; otherwise voltages are unrealistically supported.

  • MistakeUsing DC load flow to study voltages or reactive power.

    InsteadDC load flow gives only approximate real power flows; use AC load flow for voltages.

  • MistakeTreating a non-converging load flow as a numerical nuisance.

    InsteadIt may mean the operating point does not exist: the system is beyond its voltage stability limit.

  • MistakeChecking only the base case.

    InsteadRun N−1 contingency analysis; most limits appear after an outage.

Summary and key terms

  • Load flow finds bus voltages and branch flows in steady state.
  • Y_bus is built by inspection and is very sparse; I = Y_bus V.
  • Power flow equations P_i − jQ_i = V_i* Σ Y_ik V_k are non-linear and solved iteratively.
  • Bus types: slack (|V|, θ given), PV (P, |V| given), PQ (P, Q given).
  • Gauss-Seidel is simple but slow; Newton-Raphson converges quadratically in a few iterations.
  • Fast decoupled load flow uses constant B′ and B″ matrices for speed.
  • DC load flow, P = Bθ, gives approximate real power flows linearly; it underpins markets and contingency screening.
  • Controls include reactive limits, tap changers, switched shunts and phase shifters.
  • Load flow supports interconnection, transfer, voltage stability and real-time studies.
Load flow
Steady-state calculation of bus voltages and branch power flows.
Bus admittance matrix
The nodal admittance matrix relating bus currents to voltages.
Slack bus
The reference bus whose generator balances losses and mismatch.
PV bus
A bus with specified real power and voltage magnitude.
PQ bus
A bus with specified real and reactive power.
Mismatch
The difference between scheduled and calculated power at a bus.
Jacobian
The matrix of partial derivatives of power with respect to angles and magnitudes.
Newton-Raphson method
Iterative solution by repeated linearization, with quadratic convergence.
Fast decoupled load flow
An approximation separating P-θ and Q-|V| with constant matrices.
DC load flow
A linear approximation of real power flows using reactances and angles.
Contingency analysis
Checking the system after each credible outage.
State estimation
Fitting a load flow solution to real-time measurements.
Optimal power flow
A load flow with an objective, such as minimum cost, subject to limits.
PV curve
Voltage against load, showing the voltage stability limit at its nose.

Practice problems

Work each problem on paper first, then reveal the answer to check.

  1. A line has z = 0.02 + j0.10 pu. What is its series admittance?

    Show answer

    1/(0.02 + j0.10) = 1.923 − j9.615 pu.

  2. Why is Y_bus sparse?

    Show answer

    Each bus connects directly to only a few others; non-connected pairs have zero entries.

  3. What are the unknowns at a PV bus and why is a slack bus needed?

    Show answer

    Q and θ; the losses are unknown until the solution is found, so one bus must supply the balance.

  4. A system has 1 slack, 20 PV and 79 PQ buses. How many equations does Newton-Raphson solve?

    Show answer

    ΔP at 99 buses + ΔQ at 79 = 178.

  5. Mismatches in successive Newton-Raphson iterations are 0.5, 0.04, 3e-4. Estimate the next.

    Show answer

    Quadratic: about 1e-8 to 1e-7.

  6. In DC load flow, a line with X = 0.05 pu has θ_i = 0.08 rad and θ_k = 0.02 rad. Flow on a 100 MVA base?

    Show answer

    (0.06)/0.05 = 1.2 pu = 120 MW.

  7. What does the DC load flow neglect?

    Show answer

    Resistance (losses), reactive power, voltage magnitude variation and large-angle effects.

  8. A generator bus needs 120 Mvar but its limit is 100 Mvar. How is it modelled?

    Show answer

    As a PQ bus with Q = 100 Mvar; its voltage is allowed to fall.

  9. Give two remedies for a line overloaded after a contingency.

    Show answer

    Re-dispatch generation; upgrade or add lines; special protection schemes; phase shifters.

  10. What does the nose of a PV curve represent?

    Show answer

    The maximum load the network can supply: the voltage stability limit, where load flow solutions cease to exist.

Lesson quiz · 5 questions

Score 60% or more to complete this lesson.

Take the quiz