Solving a Power Flow

In this tutorial, you'll solve power flows on a 5-bus test system using three different solvers and compare their results.

Building a System

To get started, load the needed packages. We're using a standard test system and want to keep output clean, so we adjust the logging settings to filter out a few precautionary warnings.

julia> using PowerSystemCaseBuilder
julia> using PowerFlows
julia> using PowerSystems
julia> using Logging
julia> disable_logging(Logging.Warn)LogLevel(1001)

Create a System from PowerSystemCaseBuilder.jl:

julia> sys = build_system(MatpowerTestSystems, "matpower_case5_sys")              System
┌───────────────────┬─────────────┐
│ Property           Value       │
├───────────────────┼─────────────┤
│ Name              │             │
│ Description       │             │
│ System Units Base │ SYSTEM_BASE │
│ Base Power        │ 100.0       │
│ Base Frequency    │ 60.0        │
│ Num Components    │ 28          │
└───────────────────┴─────────────┘

         Static Components
┌──────────────────────────┬───────┐
│ Type                      Count │
├──────────────────────────┼───────┤
│ ACBus                    │ 5     │
│ Arc                      │ 6     │
│ Area                     │ 1     │
│ Line                     │ 5     │
│ LoadZone                 │ 1     │
│ PhaseShiftingTransformer │ 2     │
│ PowerLoad                │ 3     │
│ ThermalStandard          │ 5     │
└──────────────────────────┴───────┘

DC Power Flow

DCPowerFlow solves for bus voltage angles using the bus admittance matrix, then computes branch flows from the angle differences. Create a DCPowerFlow solver:

julia> pf_dc = DCPowerFlow()DCPowerFlow(nothing, PowerNetworkMatrices.NetworkReduction[], 1, String[], false)

Solve the power flow with solve_power_flow:

julia> dc_results = solve_power_flow(pf_dc, sys)ERROR: MethodError: no method matching solve_power_flow(::DCPowerFlow, ::System)
The function `solve_power_flow` exists, but no method is defined for this combination of argument types.

Closest candidates are:
  solve_power_flow(::T, ::System, ::FlowReporting) where T<:AbstractDCPowerFlow
   @ PowerFlows ~/work/PowerFlows.jl/PowerFlows.jl/src/solve_dc_power_flow.jl:155
  solve_power_flow(::Union{PowerFlows.PowerFlowData{DCPowerFlow, PowerNetworkMatrices.DC_ABA_Matrix_Factorized, PowerNetworkMatrices.DC_BA_Matrix}, PowerFlows.PowerFlowData{PTDFDCPowerFlow, PowerNetworkMatrices.DC_PTDF_Matrix, PowerNetworkMatrices.DC_ABA_Matrix_Factorized}, PowerFlows.PowerFlowData{vPTDFDCPowerFlow, <:PowerNetworkMatrices.DC_vPTDF_Matrix, PowerNetworkMatrices.DC_ABA_Matrix_Factorized}}, ::System, ::FlowReporting)
   @ PowerFlows ~/work/PowerFlows.jl/PowerFlows.jl/src/solve_dc_power_flow.jl:199
  solve_power_flow(::ACPowerFlow, ::System; kwargs...)
   @ PowerFlows ~/work/PowerFlows.jl/PowerFlows.jl/src/solve_ac_power_flow.jl:78

The result is a Dict{String, Dict{String, DataFrame}}. The outer key is the time step name: "1". The inner dictionary stores the power flow results at that time step: "bus_results" for bus data and "flow_results" key for AC line data. (There's also 3rd key, "lcc_results", for HVDC lines, but this sytem contains no such components, so the matching dataframe will be emtpy.) Inspect "bus_results":

julia> dc_results["1"]["bus_results"]ERROR: UndefVarError: `dc_results` not defined in `Main`
Suggestion: add an appropriate import or assignment. This global was declared but not assigned.

Notice that Vm (voltage magnitude) is 1.0 for all buses, and Q_gen and Q_load are 0. This is expected for DC power flow, which assumes flat voltage magnitudes and ignores reactive power.

julia> dc_results["1"]["flow_results"]ERROR: UndefVarError: `dc_results` not defined in `Main`
Suggestion: add an appropriate import or assignment. This global was declared but not assigned.

Likewise, Q_from_to and Q_to_from (reactive power flow on the line) are zero, for all lines.

PTDF DC Power Flow

PTDFDCPowerFlow computes branch flows directly from bus power injections using the Power Transfer Distribution Factor matrix, without solving for voltage angles as an intermediate step. (This means we can omit the angle computation in contexts where we only care about line flows, though we don't have that option implemented here.) Create a PTDFDCPowerFlow solver:

julia> pf_ptdf = PTDFPowerFlow()ERROR: UndefVarError: `PTDFPowerFlow` not defined in `Main`
Suggestion: check for spelling errors or missing imports.

As before, solve the power flow with solve_power_flow:

julia> dc_results = solve_power_flow(pf_ptdf, sys)ERROR: UndefVarError: `pf_ptdf` not defined in `Main`
Suggestion: add an appropriate import or assignment. This global was declared but not assigned.

Look at the bus results:

julia> ptdf_results["1"]["bus_results"]ERROR: UndefVarError: `ptdf_results` not defined in `Main`
Suggestion: check for spelling errors or missing imports.

The results match DCPowerFlow, as they should: the two are mathematically equivalent. For very large systems where forming the full PTDF matrix would be too expensive, consider vPTDFDCPowerFlow, which computes the same results without storing the dense matrix.

AC Power Flow

Create an ACPowerFlow solver:

julia> pf_ac = ACPowerFlow()ACPowerFlow{NewtonRaphsonACPowerFlow}(false, nothing, false, false, nothing, true, false, false, PowerNetworkMatrices.NetworkReduction[], 1, String[], false, Dict{Symbol, Any}())

Solve the power flow:

julia> solve_power_flow(pf_ac, sys)Dict{String, DataFrames.DataFrame} with 3 entries:
  "flow_results" => 6×9 DataFrame…
  "bus_results"  => 5×9 DataFrame…
  "lcc_results"  => 0×13 DataFrame

AC results are returned as a flat Dict{String, DataFrame}, with the same keys as before: "bus_results", "flow_results" (AC lines), and "lcc_results" (HVDC lines). (We don't support multi-period AC power flows yet.) Look at the bus results:

julia> ac_results["bus_results"]ERROR: UndefVarError: `ac_results` not defined in `Main`
Suggestion: check for spelling errors or missing imports.

Notice that Vm now varies across buses (not all 1.0), and Q_gen has non-zero values.

Look at the line flows:

julia> ac_results["flow_results"]ERROR: UndefVarError: `ac_results` not defined in `Main`
Suggestion: check for spelling errors or missing imports.

Q_from_to and Q_to_from now show reactive power flows, and P_from_to differs from P_to_from due to losses.

When AC Power Flow Fails

Unlike DC power flow, AC power flow is iterative and not guaranteed to converge. Systems with high impedance lines, poor initial voltage profiles, or insufficient reactive power support can cause the solver to fail. When this happens, solve_power_flow returns missing: you'll also see a logged error. If you encounter convergence failures, consider using a more robust solver such as TrustRegionACPowerFlow or RobustHomotopyPowerFlow.