Skip to content

Commit

Permalink
Merge branch 'main' into od/update
Browse files Browse the repository at this point in the history
  • Loading branch information
odow committed May 6, 2024
2 parents c968b1c + 8c46dea commit 5a9702b
Show file tree
Hide file tree
Showing 14 changed files with 517 additions and 6 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@

## staged

- Indicated broken tests in `"2w_dy_lead"` and `"3-bus SOCConicUBF opf_bf"` by using `@test_skip`

## v0.15.2

- Added transformation `reduce_line_series!` which will reduce lines that consist of only buses with no additional connections to a single line.
- Added function `solve_mn_mc_opf_oltc` for multi-networks
- Fixed bug in `DssLine` parser where `c1` was being set to `c0`
- Fixed native pf unit tests, which assume no virtual branches from switches (applied `make_lossless!` before test)
- Added `g_fr`, `g_to`, `b_fr`, `b_to` to switches in `dss2eng` and `eng2math`
Expand Down
2 changes: 2 additions & 0 deletions Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ CSV = "336ed68f-0bac-5ca0-87d4-7b16caf5d00b"
Dates = "ade2ca70-3891-5945-98fb-dc099432e06a"
FilePaths = "8fc22ac5-c921-52a6-82fd-178b2807b824"
Glob = "c27321d9-0574-5035-807b-f59d2c89b15c"
Graphs = "86223c79-3864-5bf0-83f7-82e725a168b6"
InfrastructureModels = "2030c09a-7f63-5d83-885d-db604e0e9cc0"
JSON = "682c06a0-de6a-54ab-a142-c8b1cf79cde6"
JuMP = "4076af6c-e467-56ae-b986-b466b2749572"
Expand All @@ -25,6 +26,7 @@ CSV = "0.8.5, 0.9, 0.10"
Dates = "1.6"
FilePaths = "0.8.3"
Glob = "1.3"
Graphs = "1"
InfrastructureModels = "0.7.3, 0.7.5"
Ipopt = "1"
JSON = "0.18, 0.19, 0.20, 0.21"
Expand Down
3 changes: 1 addition & 2 deletions docs/src/reference/data_models.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

# [Data Models](@id DataModelAPI)

## Parsers
Expand Down Expand Up @@ -33,7 +32,7 @@ transform_solution
Modules = [PowerModelsDistribution]
Private = false
Order = [:function]
Pages = ["transformations.jl"]
Pages = ["transformations.jl", "reduce.jl", "kron.jl", "bounds.jl"]
```

## Multinetworks
Expand Down
2 changes: 2 additions & 0 deletions src/PowerModelsDistribution.jl
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ module PowerModelsDistribution
import Statistics: mean, std
import SparseArrays: spzeros

import Graphs

const _IM = InfrastructureModels

Expand Down Expand Up @@ -69,6 +70,7 @@ module PowerModelsDistribution
include("data_model/transformations/eng2math.jl")
include("data_model/transformations/math2eng.jl")
include("data_model/transformations/utils.jl")
include("data_model/transformations/reduce.jl")

include("core/data.jl")
include("core/ref.jl")
Expand Down
2 changes: 0 additions & 2 deletions src/core/objective.jl
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,6 @@ Fuel cost minimization objective with piecewise linear terms
"""
function objective_mc_min_fuel_cost_pwl(pm::AbstractUnbalancedPowerModel; report::Bool=true)
objective_mc_variable_pg_cost(pm; report=report)

return JuMP.@objective(pm.model, Min,
sum(
sum( var(pm, n, :pg_cost, i) for (i,gen) in nw_ref[:gen])
Expand Down Expand Up @@ -443,7 +442,6 @@ Checks that all generator cost models are of the same type
"""
function check_gen_cost_models(pm::AbstractUnbalancedPowerModel)
model = nothing

for (n, nw_ref) in nws(pm)
for (i,gen) in nw_ref[:gen]
if haskey(gen, "cost")
Expand Down
142 changes: 142 additions & 0 deletions src/data_model/transformations/reduce.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
"""
reduce_line_series!(eng::Dict{String,<:Any}; remove_original_lines::Bool=false)::Dict{String,<:Any}
This is a function to merge series of lines which only connect to buses with no other connections
(i.e., string of buses with no loads, generators, transformers, etc.). This function will preserve
the total length of the merged lines.
If `remove_original_lines`, the original lines and eliminated buses will be deleted from the data structure,
otherwise the lines and buses will be `DISABLED`.
"""
function reduce_line_series!(eng::Dict{String,<:Any}; remove_original_lines::Bool=false)
G = Graphs.Graph(sum([length(get(eng, k, Dict())) for k in ["bus", PowerModelsDistribution._eng_node_elements...]]))

id_map = Dict((k,i) => n for (n,(k,i)) in enumerate([(k,i) for k in ["bus", PowerModelsDistribution._eng_node_elements...] for i in keys(get(eng, k, Dict()))]))

for k in PowerModelsDistribution._eng_node_elements
for (i,obj) in get(eng, k, Dict())
Graphs.add_edge!(G, id_map[(k,i)], id_map[("bus",obj["bus"])])
end
end

lines_by_id = Set()
line_lookup_by_bus = Dict(i => [] for i in keys(eng["bus"]))
for k in PowerModelsDistribution._eng_edge_elements
for (i,obj) in get(eng, k, Dict())
if k == "transformer" && haskey(obj, "bus")
for bus1 in obj["bus"]
for bus2 in obj["bus"]
if bus1 != bus2
Graphs.add_edge!(G, id_map[("bus", bus1)], id_map[("bus", bus2)])
end
end
end
else
Graphs.add_edge!(G, id_map[("bus", obj["f_bus"])], id_map[("bus", obj["t_bus"])])
if k == "line"
push!(lines_by_id, Set([id_map[("bus", obj["f_bus"])], id_map[("bus", obj["t_bus"])]]))
push!(line_lookup_by_bus[obj["f_bus"]], i)
push!(line_lookup_by_bus[obj["t_bus"]], i)
end
end
end
end

id_lookup = Dict(n => (k,i) for ((k,i), n) in id_map)

b2r = Set()
for node in Graphs.vertices(G)
n = Graphs.all_neighbors(G, node)
if length(n) == 2 && all([id_lookup[i][1] == "bus" for i in n]) && all([Set([node, i]) in lines_by_id for i in n])
push!(b2r, node)
end
end
buses2remove = Set([id_lookup[n][2] for n in b2r])
lines2merge = [Set(line_lookup_by_bus[n]) for n in buses2remove if length(line_lookup_by_bus[n])==2]

while true
to_combine = []
for (i, l1) in enumerate(lines2merge)
for (j, l2) in enumerate(lines2merge)
if i<j && !isempty(intersect(l1, l2))
push!(to_combine, (i,j))
end
end
end

if isempty(to_combine)
break
else
touched = []
merged = []
for (i,j) in to_combine
if (j touched) && (i touched)
lines2merge[i] = union(lines2merge[i], lines2merge[j])
push!(touched, j)
push!(touched, i)
push!(merged, j)
end
end

lines2merge = [lines2merge[i] for i in 1:length(lines2merge) if (i merged)]
end
end

for line_group in lines2merge
if length(Set([Set(eng["line"][line_id][connection_type]) for line_id in line_group for connection_type in ["f_connections", "t_connections"]])) == 1
buses = [eng["line"][line_id][bus_end] for line_id in line_group for bus_end in ["f_bus", "t_bus"]]
buses = Dict(b => count(isequal(b), buses) for b in buses)
ft_buses = [k for (k,v) in buses if v == 1]
new_line = Dict{String,Any}(
"f_bus" => ft_buses[1],
"t_bus" => ft_buses[2],
"f_connections" => eng["line"][first(line_group)]["f_connections"],
"t_connections" => eng["line"][first(line_group)]["t_connections"],
"source_id" => "line._virtual.$(join(line_group, "+"))",
"status" => all(eng["line"][line_id]["status"] == ENABLED for line_id in line_group) ? ENABLED : DISABLED
)
end

if length(Set([eng["line"][line_id]["linecode"] for line_id in line_group])) == 1
new_line["length"] = sum(eng["line"][line_id]["length"] for line_id in line_group)
new_line["linecode"] = eng["line"][first(line_group)]["linecode"]
else
for line_id in line_group
_apply_linecode!(eng["line"][line_id], eng)
end

new_line["length"] = sum(eng["line"][line_id]["length"] for line_id in line_group)

for prop_id in ["rs", "xs"]
new_line[prop_id] = sum(eng["line"][line_id][prop_id]*eng["line"][line_id]["length"] for line_id in line_group) / new_line["length"]
end

for prop_id in ["b_fr", "b_to", "g_fr", "g_to"]
new_line[prop_id] = LinearAlgebra.pinv(sum(LinearAlgebra.pinv(eng["line"][line_id][prop_id]) for line_id in line_group)) / new_line["length"]
end
end

eng["line"]["_virtual.$(join(line_group, "+"))"] = new_line

for line_id in line_group
if remove_original_lines
for bus in keys(buses)
if bus ft_buses
delete!(eng["bus"], bus)
end
end
delete!(eng["line"], line_id)
else
for bus in keys(buses)
if bus ft_buses
eng["bus"][bus]["status"] = DISABLED
end
end

eng["line"][line_id]["status"] = DISABLED
end
end
end

return eng["line"]
end
101 changes: 101 additions & 0 deletions src/prob/opf_oltc.jl
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,22 @@ function solve_mc_opf_oltc(data::Union{Dict{String,<:Any},String}, model_type::T
end


"""
function solve_mn_mc_opf_oltc(
data::Union{Dict{String,<:Any},String},
model_type::Type,
solver;
kwargs...
)
Solve multinetwork oltc optimal power flow problem
"""
function solve_mn_mc_opf_oltc(data::Union{Dict{String,<:Any},String}, model_type::Type, solver; kwargs...)
return solve_mc_model(data, model_type, solver, build_mn_mc_opf_oltc; multinetwork=true, kwargs...)
end



"constructor for on-load tap-changer OPF"
function build_mc_opf_oltc(pm::AbstractUnbalancedPowerModel)
variable_mc_bus_voltage(pm)
Expand Down Expand Up @@ -137,3 +153,88 @@ function build_mc_opf_oltc(pm::AbstractUBFModels)
# Objective
objective_mc_min_fuel_cost(pm)
end


"""
function build_mn_mc_opf_oltc(
pm::AbstractUnbalancedPowerModel
)
Constructor for otlc Optimal Power Flow
"""
function build_mn_mc_opf_oltc(pm::AbstractUnbalancedPowerModel)
for (n, network) in nws(pm)
variable_mc_bus_voltage(pm; nw=n)
variable_mc_branch_power(pm; nw=n)
variable_mc_switch_power(pm; nw=n)
variable_mc_transformer_power(pm; nw=n)
variable_mc_oltc_transformer_tap(pm; nw=n)
variable_mc_generator_power(pm; nw=n)
variable_mc_load_power(pm; nw=n)
variable_mc_storage_power(pm; nw=n)

constraint_mc_model_voltage(pm; nw=n)

for i in ids(pm, n, :ref_buses)
constraint_mc_theta_ref(pm, i; nw=n)
end

# generators should be constrained before KCL, or Pd/Qd undefined
for id in ids(pm, n,:gen)
constraint_mc_generator_power(pm, id; nw=n)
end

# loads should be constrained before KCL, or Pd/Qd undefined
for id in ids(pm, n, :load)
constraint_mc_load_power(pm, id; nw=n)
end

for i in ids(pm, n, :bus)
constraint_mc_power_balance(pm, i; nw=n)
end

for i in ids(pm, n, :storage)
constraint_storage_complementarity_nl(pm, i; nw=n)
constraint_mc_storage_losses(pm, i; nw=n)
constraint_mc_storage_thermal_limit(pm, i; nw=n)
end

for i in ids(pm, n, :branch)
constraint_mc_ohms_yt_from(pm, i; nw=n)
constraint_mc_ohms_yt_to(pm, i; nw=n)
constraint_mc_voltage_angle_difference(pm, i; nw=n)
constraint_mc_thermal_limit_from(pm, i; nw=n)
constraint_mc_thermal_limit_to(pm, i; nw=n)
constraint_mc_ampacity_from(pm, i; nw=n)
constraint_mc_ampacity_to(pm, i; nw=n)
end

for i in ids(pm, n, :switch)
constraint_mc_switch_state(pm, i; nw=n)
constraint_mc_switch_thermal_limit(pm, i; nw=n)
constraint_mc_switch_ampacity(pm, i; nw=n)
end

for i in ids(pm, n, :transformer)
constraint_mc_transformer_power(pm, i, fix_taps=false; nw=n)
end
end

network_ids = sort(collect(nw_ids(pm)))

n_1 = network_ids[1]

for i in ids(pm, :storage; nw=n_1)
constraint_storage_state(pm, i; nw=n_1)
end

for n_2 in network_ids[2:end]
for i in ids(pm, :storage; nw=n_2)
constraint_storage_state(pm, i, n_1, n_2)
end

n_1 = n_2
end

objective_mc_min_fuel_cost(pm)
end
Loading

0 comments on commit 5a9702b

Please sign in to comment.