Power Flow

PowerFlows.jl provides the capability to run a power flow using NLSolve, in the current stage of development it can't force reactive power constraints. This power flow routine does not check for reactive power limits or other limiting mechanisms in the grid, and can therefore be used to check for solver convergence - making no guarantees of the solution feasibility.

The power flow solver uses NLsolve.jl under the hood and takes any keyword argument accepted by NLsolve. The solver uses the current operating point in the buses to provide the initial guess.

Limitations: The PowerFlow solver doesn't support systems with HVDC lines or Phase Shifting transformers yet. The power flow solver can't handle systems with islands.

using PowerFlows
using PowerSystems
using PowerSystemCaseBuilder

system_data = build_system(PSITestSystems, "c_sys14")
System
Property Value
Name
Description
System Units Base SYSTEM_BASE
Base Power 100.0
Base Frequency 60.0
Num Components 70
Static Components
Type Count
ACBus 14
Arc 20
Line 16
PowerLoad 11
TapTransformer 3
ThermalStandard 5
Transformer2W 1
Time Series Summary
owner_type owner_category time_series_type time_series_category initial_timestamp resolution_ms count
String String String String String Int64 Int64
PowerLoad Component Deterministic Forecast 2024-01-01T00:00:00 3600000 11

PowerFlows.jl has two modes of using the power flow solver.

  1. Solving the power flow for the current operating point in the system. Takes the data in the buses, the active_power and reactive_power fields in the static injection devices. Returns a dictionary with results in a DataFrame that can be exported or manipulated as needed.

  2. Solves the power flow and updated the devices in the system to the operating condition. This model will update the values of magnitudes and angles in the system's buses. It also updates the active and reactive power flows in the branches and devices connected to PV buses. It also updates the active and reactive power of the injection devices connected to the Slack bus, and updates only the reactive power of the injection devices connected to PV buses. If multiple devices are connected to the same bus, the power is divided proportional to the base power. This utility is useful to initialize systems before serializing or checking the addition of new devices is still AC feasible.

Solving the power flow with mode 1:

results = run_powerflow(system_data)
results["bus_results"]

Solving the power flow with mode 2:

Before running the power flow command these are the values of the voltages:

for b in get_components(Bus, system_data)
    println("$(get_name(b)) - Magnitude $(get_magnitude(b)) - Angle (rad) $(get_angle(b))")
end
Bus 6 - Magnitude 1.07 - Angle (rad) -0.24818581963359368
Bus 9 - Magnitude 1.056 - Angle (rad) -0.2607521902479528
Bus 12 - Magnitude 1.055 - Angle (rad) -0.2630211182755455
Bus 11 - Magnitude 1.057 - Angle (rad) -0.2581341963699613
Bus 7 - Magnitude 1.062 - Angle (rad) -0.23335052099164186
Bus 4 - Magnitude 1.019 - Angle (rad) -0.18029251173101424
Bus 13 - Magnitude 1.05 - Angle (rad) -0.2645919146023404
Bus 2 - Magnitude 1.045 - Angle (rad) -0.08691739674931762
Bus 14 - Magnitude 1.036 - Angle (rad) -0.27995081201989047
Bus 8 - Magnitude 1.09 - Angle (rad) -0.2331759880664424
Bus 10 - Magnitude 1.051 - Angle (rad) -0.26354471705114374
Bus 3 - Magnitude 1.01 - Angle (rad) -0.22200588085367873
Bus 1 - Magnitude 1.06 - Angle (rad) 0.0
Bus 5 - Magnitude 1.02 - Angle (rad) -0.15323990832510212

run_powerflow! return true or false to signal the successful result of the power flow. This enables the integration of a power flow into functions and use the return as check. For instance, initializing dynamic simulations. Also, because run_powerflow! uses NLsolve.jl all the parameters used for NLsolve are also available for run_powerflow!

run_powerflow!(system_data; method = :newton)

After running the power flow command this are the values of the voltages:

for b in get_components(Bus, system_data)
    println("$(get_name(b)) - Magnitude $(get_magnitude(b)) - Angle (rad) $(get_angle(b))")
end
Bus 6 - Magnitude 1.07 - Angle (rad) -0.24818581963359368
Bus 9 - Magnitude 1.056 - Angle (rad) -0.2607521902479528
Bus 12 - Magnitude 1.055 - Angle (rad) -0.2630211182755455
Bus 11 - Magnitude 1.057 - Angle (rad) -0.2581341963699613
Bus 7 - Magnitude 1.062 - Angle (rad) -0.23335052099164186
Bus 4 - Magnitude 1.019 - Angle (rad) -0.18029251173101424
Bus 13 - Magnitude 1.05 - Angle (rad) -0.2645919146023404
Bus 2 - Magnitude 1.045 - Angle (rad) -0.08691739674931762
Bus 14 - Magnitude 1.036 - Angle (rad) -0.27995081201989047
Bus 8 - Magnitude 1.09 - Angle (rad) -0.2331759880664424
Bus 10 - Magnitude 1.051 - Angle (rad) -0.26354471705114374
Bus 3 - Magnitude 1.01 - Angle (rad) -0.22200588085367873
Bus 1 - Magnitude 1.06 - Angle (rad) 0.0
Bus 5 - Magnitude 1.02 - Angle (rad) -0.15323990832510212