LinDistFlow.jl

LinDistFlow builds the linear distflow constraints using JuMP. The intent of this package is to allow users to build mathematical programs that include LinDistFlow constraints. No objective is added to the JuMP model in this package and so solving any problem defined by the constraints built by LinDistFlow.jl is a feasibility problem. Dictionaries of constraints are provided so that one can delete and/or modify the base constraints to fit their problem.

Inputs

There are two methods for creating Inputs:

  1. Using openDSS files
  2. Providing the network topology
CommonOPF.InputsMethod
Inputs(
    dssfilepath::String, 
    substation_bus::String;
    Pload::AbstractDict=Dict(), 
    Qload::AbstractDict=Dict(), 
    Sbase=1, 
    Vbase=1, 
    v0, 
    v_lolim=0.95, 
    v_uplim=1.05,
    Ntimesteps=1, 
    P_up_bound=1e4,
    Q_up_bound=1e4,
    P_lo_bound=-1e4,
    Q_lo_bound=-1e4,
    relaxed=true,
    extract_phase::Int=0  # set to 1, 2, or 3
)

Inputs constructor that parses a openDSS file for the network. If Pload and Qload are not provided then the loads are also parsed from the openDSS file.

If extract_phase is set to 1, 2, 3 then the loads for that phase are put into Pload and Qload and the impedance values are set to the positive sequence impedance for each line. Note that single phase lines and two phase lines do not have positive sequence definitions but single phase lines only have one impedance value anyway and for two phase lines we use zmutual = z12 and zself = 1/2(z11 + z22).

CommonOPF.InputsMethod
Inputs(
    edges::Array{Tuple}, 
    linecodes::Array{String}, 
    linelengths::Array{Float64}, 
    phases::Vector{Vector},
    substation_bus::String;
    Pload, 
    Qload, 
    Sbase=1, 
    Vbase=1, 
    Zdict, 
    v0, 
    v_lolim=0.95, 
    v_uplim=1.05,
    Ntimesteps=1, 
    P_up_bound=1e4,
    Q_up_bound=1e4,
    P_lo_bound=-1e4,
    Q_lo_bound=-1e4,
    Isquared_up_bounds=Dict{String, Float64}(),
    relaxed=true
)

Lowest level Inputs constructor (the only one that returns the Inputs struct).

Note

The real and reactive loads provided are normalized using Sbase.

Both of the Inputs functions return a mutable Inputs struct:

CommonOPF.InputsType
mutable struct Inputs{T<:Phases} <: AbstractInputs
    edges::Array{Tuple, 1}
    linecodes::Array{String, 1}
    linelengths::Array{Float64, 1}
    busses::Array{String}
    phases::Vector{Vector}
    substation_bus::String
    Pload::Dict{String, Any}
    Qload::Dict{String, Any}
    Sbase::Real
    Vbase::Real
    Ibase::Real
    Zdict::Dict{String, Dict{String, Any}}
    v0::Real
    v_lolim::Real
    v_uplim::Real
    Zbase::Real
    Ntimesteps::Int
    pf::Float64
    Nnodes::Int
    P_up_bound::Float64
    Q_up_bound::Float64
    P_lo_bound::Float64
    Q_lo_bound::Float64
    Isquared_up_bounds::Dict{String, <:Real}
    phases_into_bus::Dict{String, Vector{Int}}
    relaxed::Bool
    edge_keys::Vector{String}
    regulators::Dict
    shunt_susceptance::Dict
end

Inputs

  • edges Vector{Tuple} e.g. [("0", "1"), ("1", "2")]
  • linecodes vector of string keys for the Zdict (impedance values for lines). When using an OpenDSS model a linecode is the name in New linecode.name
  • linelengths vector of floats to scale impedance values
  • busses vector of bus names
  • phases vector of vectors with ints for the line phases (e.g. [[1,2,3], [1,3], ...])
  • Pload dict with busses for keys and uncontrolled real power loads (positive is load) by phase and time
  • Qload dict with busses for keys and uncontrolled reactive power loads (positive is load) by phase and time
  • Sbase base apparent power for network, typ. feeder capacity. Used to normalize all powers in model
  • Vbase base voltage for network, used to determine Zbase = Vbase^2 / Sbase
  • Ibase = Sbase / (Vbase * sqrt(3))
  • Zdict dict with linecodes for keys and subdicts with "xmatrix" and "zmatrix" keys with per unit length values. Values are divided by Zbase and multiplied by linelength in mathematical model.
  • v0 slack bus reference voltage

TODO Zdict example

TODO test against simple model to make sure scaling is done right

Note

The edges, linecodes, phases, edge_keys, and linelengths are in mutual order (i.e. the i-th value in each list corresponds to the same line)

Building a Model

The build_ldf! function takes a JuMP.Model and Inputs struct as its two arguments and adds the variables and constraints:

LinDistFlow.build_ldf!Function
build_ldf!(m::JuMP.AbstractModel, p::Inputs)

Add variables and constraints to m using the values in p. Calls the following functions:

add_variables(m, p)
constrain_power_balance(m, p)
constrain_substation_voltage(m, p)
constrain_KVL(m, p)
constrain_loads(m, p)
source

Variables

Let m be the JuMP.Model provided by the user, then the variables can be accessed via:

  • m[:vsqrd] voltage magnitude squared, indexed on busses, (phases), time
  • m[:Pj], m[:Qj] net real, reactive power injection, indexed on busses, (phases), time
  • m[:Pij], m[:Qij] net real, reactive line flow, indexed on edges, (phases), time

After a model has been solved using JuMP.optimize! variable values can be extracted with JuMP.value. For more see Getting started with JuMP.

Note

Single phase models do not have a phase index

Accessing and Modifying Constraints

Let the JuMP.Model provided by the user be called m. All constraints are stored in m[:cons] as anonymous constraints.

Power Injections

LinDistFlow.jl uses the convention that power injections are positive (and loads are negative). If no load is provided for a given bus (and phase) then the real and reactive power injections at that bus (and phase) are set to zero with an equality constraint.

All power injection constraints are stored in m[:cons][:injection_equalities]. The constraints are indexed in the following order:

  1. by bus name (string), as provided in Inputs.busses;
  2. by :P or :Q for real and reactive power respectively;
  3. by phase number (integer); and
  4. by time (integer).

For example, m[:cons][:injection_equalities]["680"][:P][2][1] contains the constraint reference for the power injection equality constraint for bus "680", real power, on phase 2, in time step 1.

If one wished to replace any constraint one must first delete the constraint using the delete function. For example:

delete(m, m[:cons][:injection_equalities]["680"][:P][1])

Note that the time index was not provided in the delete command in this example, which implies that the equality constraints for all time steps were deleted. One can also delete individual time step constraints by providing the time index.

The deleted constraints can then be replaced with a new set of constraints. For example:

m[:cons][:injection_equalities]["680"][:P][1] = @constraint(m, [t in 1:p.Ntimesteps],
    m[:Pj]["680",1,t] == -1e3 / p.Sbase
)

where p is short for "parameters" and is the Inputs struct for the problem of interest. Note that it is not necessary to store the new constraints in the m[:cons][:injection_equalities].

See the JuMP documentation for more on deleting constraints.

Results

Results for the power flow variables can be retrieved via the Results methods described below. The fields of the Results struct are dictionaries that have the same indices as the variables described above.

LinDistFlow.ResultsMethod
Results(m::AbstractModel, p::Inputs{SinglePhase}; digits=8)

return a Results struct with fieldnames:

voltage_magnitudes
real_power_injections
reactive_power_injections
current_magnitudes
real_sending_end_powers
reactive_sending_end_powers
prices
source
LinDistFlow.ResultsMethod
Results(m::AbstractModel, p::Inputs{MultiPhase}; digits=8)

return a Results struct with fieldnames:

voltage_magnitudes
real_power_injections
reactive_power_injections
current_magnitudes
real_sending_end_powers
reactive_sending_end_powers
source

Approximate line amperage values can be obtained via get_line_amps and get_peak_line_amps_percent for multi-phase models.

LinDistFlow.get_line_ampsFunction
get_line_amps(m::JuMP.AbstractModel, p::Inputs{MultiPhase})

Estimating the line amps as $|(V_i - V_j) / Z|$ where we use the approximation: $|V_i - V_j| \approx r_{ij} P_{ij} + x_{ij} Q_{ij}$

source
LinDistFlow.get_peak_line_amps_percentFunction
get_peak_line_amps_percent(m::JuMP.AbstractModel, p::Inputs{MultiPhase})

A Dict{String, Float64} with edge keys and peak percent line amps (peak over all time steps)

Estimating the line amps as $|(V_i - V_j) / Z|$ where we use the approximation: $|V_i - V_j| \approx r_{ij} P_{ij} + x_{ij} Q_{ij}$

source