From bab5b64faee322767d6657c544d202f3ecd5ac4e Mon Sep 17 00:00:00 2001 From: samtalki <10187005+samtalki@users.noreply.github.com> Date: Wed, 24 Jun 2026 23:19:46 -0400 Subject: [PATCH] Add PowerIO parser backend --- Project.toml | 2 ++ src/ExaModelsPower.jl | 1 + src/dcopf.jl | 3 +- src/mpopf.jl | 70 +++++++++++++++++-------------------------- src/opf.jl | 3 +- src/parser.jl | 44 +++++---------------------- 6 files changed, 42 insertions(+), 81 deletions(-) diff --git a/Project.toml b/Project.toml index 57d74e6..3f49ff0 100644 --- a/Project.toml +++ b/Project.toml @@ -7,6 +7,7 @@ DelimitedFiles = "8bb1440f-4735-579b-a4ab-409b98df4dab" ExaModels = "1037b233-b668-4ce9-9b63-f9f681f55dd2" ExaPowerIO = "14903efe-9500-4d7f-a589-7ab7e15da6de" JSON = "682c06a0-de6a-54ab-a142-c8b1cf79cde6" +PowerIO = "05ed8b54-f668-4096-9d0d-e8c3dd9dc169" [compat] CUDA = "6" @@ -15,6 +16,7 @@ ExaPowerIO = "0.3" JSON = "0.21" MadNLP = "0.10" MadNLPGPU = "0.10" +PowerIO = "0.2.3" julia = "1.11" [extras] diff --git a/src/ExaModelsPower.jl b/src/ExaModelsPower.jl index b2562a9..413f300 100644 --- a/src/ExaModelsPower.jl +++ b/src/ExaModelsPower.jl @@ -4,6 +4,7 @@ import ExaModels: ExaModels, ExaCore, variable, parameter, constraint, ExaModel, using DelimitedFiles using ExaPowerIO using JSON +using PowerIO include("parser.jl") include("constraint.jl") diff --git a/src/dcopf.jl b/src/dcopf.jl index e927114..06cb193 100644 --- a/src/dcopf.jl +++ b/src/dcopf.jl @@ -87,10 +87,11 @@ function dcopf_model( filename; backend = nothing, T = Float64, + from = nothing, user_callback = dummy_extension, kwargs..., ) - data = parse_ac_power_data(filename, T) + data = parse_ac_power_data(filename, T; from = from) data = convert_data(data, backend) return build_dcopf(data, user_callback; backend = backend, T = T, kwargs...) diff --git a/src/mpopf.jl b/src/mpopf.jl index 6b445d1..8af7c25 100644 --- a/src/mpopf.jl +++ b/src/mpopf.jl @@ -1,6 +1,6 @@ -function parse_mp_power_data(filename, N, corrective_action_ratio, T = Float64) +function parse_mp_power_data(filename, N, corrective_action_ratio, T = Float64; from = nothing) - data = parse_ac_power_data(filename, T) + data = parse_ac_power_data(filename, T; from = from) nbus = length(data.bus) @@ -25,25 +25,17 @@ function update_load_data(busarray, curve) for t in eachindex(curve) for x in 1:size(busarray, 1) - b = busarray[x, t] + row = busarray[x, t] + b = row.b busarray[x, t] = ( - b=ExaPowerIO.BusData{typeof(b.b.pd)}( - b.b.i, - b.b.bus_i, - b.b.type, - b.b.pd*curve[t], - b.b.qd*curve[t], - b.b.gs*curve[t], - b.b.bs*curve[t], - b.b.area, - b.b.vm, - b.b.va, - b.b.baseKV, - b.b.zone, - b.b.vmax, - b.b.vmin, - ), t=t - ) + b = merge(b, (; + pd = b.pd * curve[t], + qd = b.qd * curve[t], + gs = b.gs * curve[t], + bs = b.bs * curve[t], + )), + t = row.t, + ) end end end @@ -51,25 +43,14 @@ end #Pd, Qd as input function update_load_data(busarray, pd, qd, baseMVA) for (idx ,pd_t) in pairs(pd) - b = busarray[idx[1], idx[2]] + row = busarray[idx[1], idx[2]] + b = row.b busarray[idx[1], idx[2]] = ( - b=ExaPowerIO.BusData{typeof(b.b.pd)}( - b.b.i, - b.b.bus_i, - b.b.type, - pd_t / baseMVA, - qd[idx[1], idx[2]] / baseMVA, - b.b.gs, - b.b.bs, - b.b.area, - b.b.vm, - b.b.va, - b.b.baseKV, - b.b.zone, - b.b.vmax, - b.b.vmin, - ), - t=idx[2], + b = merge(b, (; + pd = pd_t / baseMVA, + qd = qd[idx[1], idx[2]] / baseMVA, + )), + t = row.t, ) end end @@ -415,13 +396,14 @@ function mpopf_model( backend = nothing, form = :polar, T = Float64, + from = nothing, storage_complementarity_constraint = false, user_callback = dummy_extension, kwargs..., ) @assert length(curve) > 0 - data = parse_mp_power_data(filename, N, corrective_action_ratio, T) + data = parse_mp_power_data(filename, N, corrective_action_ratio, T; from = from) update_load_data(data.busarray, curve) data = convert_data(data,backend) Nbus = size(data.bus, 1) @@ -442,12 +424,13 @@ function mpopf_model( backend = nothing, form = :polar, T = Float64, + from = nothing, storage_complementarity_constraint = false, user_callback = dummy_extension, kwargs..., ) - data = parse_mp_power_data(filename, N, corrective_action_ratio, T) + data = parse_mp_power_data(filename, N, corrective_action_ratio, T; from = from) update_load_data(data.busarray, pd, qd, data.baseMVA[]) data = convert_data(data,backend) Nbus = size(data.bus, 1) @@ -468,12 +451,13 @@ function mpopf_model( backend = nothing, form = :polar, T = Float64, + from = nothing, user_callback = dummy_extension, kwargs..., ) @assert length(curve) > 0 - data = parse_mp_power_data(filename, N, corrective_action_ratio, T) + data = parse_mp_power_data(filename, N, corrective_action_ratio, T; from = from) update_load_data(data.busarray, curve) data = convert_data(data,backend) Nbus = size(data.bus, 1) @@ -494,13 +478,14 @@ function mpopf_model( backend = nothing, form = :polar, T = Float64, + from = nothing, storage_complementarity_constraint = false, user_callback = dummy_extension, kwargs..., ) - data = parse_mp_power_data(filename, N, corrective_action_ratio, T) + data = parse_mp_power_data(filename, N, corrective_action_ratio, T; from = from) update_load_data(data.busarray, pd, qd, data.baseMVA[]) data = convert_data(data,backend) Nbus = size(data.bus, 1) @@ -511,4 +496,3 @@ function mpopf_model( end return build_mpopf(data, Nbus, N, discharge_func, form,user_callback, backend = backend, T = T, kwargs...) end - diff --git a/src/opf.jl b/src/opf.jl index fbedbed..262ce92 100644 --- a/src/opf.jl +++ b/src/opf.jl @@ -218,11 +218,12 @@ function ac_opf_model( backend = nothing, T = Float64, form = :polar, + from = nothing, user_callback = dummy_extension, kwargs..., ) - data = parse_ac_power_data(filename, T) + data = parse_ac_power_data(filename, T; from = from) data = convert_data(data, backend) if form == :polar diff --git a/src/parser.jl b/src/parser.jl index e85361a..2a706ab 100644 --- a/src/parser.jl +++ b/src/parser.jl @@ -1,41 +1,13 @@ convert_data(data::N, backend) where {names,N<:NamedTuple{names}} = NamedTuple{names}(convert_array(d, backend) for d in data) -function parse_ac_power_data(filename, T = Float64) - _, f = splitdir(filename) - name, _ = splitext(f) - - @info "Loading matpower file" - - library = isfile(filename) ? nothing : :pglib - data = ExaPowerIO.parse_matpower(T, filename; library) - - data = ( - baseMVA = [data.baseMVA], - bus = data.bus, - gen = data.gen, - arc = data.arc, - branch = data.branch, - storage = isempty(data.storage) ? empty_data = Vector{NamedTuple{(:i,), Tuple{Int64}}}() : data.storage, - ref_buses = [i for i in 1:length(data.bus) if data.bus[i].type == 3], - vmax = [bu.vmax for bu in data.bus], - vmin = [bu.vmin for bu in data.bus], - pmax = [g.pmax for g in data.gen], - pmin = [g.pmin for g in data.gen], - qmax = [g.qmax for g in data.gen], - qmin = [g.qmin for g in data.gen], - angmax = [br.angmax for br in data.branch], - angmin = [br.angmin for br in data.branch], - rate_a = [a.rate_a for a in data.arc], - vm0 = [b.vm for b in data.bus], - va0 = [b.va for b in data.bus], - pg0 = [g.pg for g in data.gen], - qg0 = [g.qg for g in data.gen], - pdmax = isempty(data.storage) ? Vector{NamedTuple{(:i,), Tuple{Int64}}}() : [s.charge_rating for s in data.storage], - pcmax = isempty(data.storage) ? Vector{NamedTuple{(:i,), Tuple{Int64}}}() : [s.discharge_rating for s in data.storage], - srating = isempty(data.storage) ? Vector{NamedTuple{(:i,), Tuple{Int64}}}() : [s.thermal_rating for s in data.storage], - emax = isempty(data.storage) ? Vector{NamedTuple{(:i,), Tuple{Int64}}}() : [s.energy_rating for s in data.storage], - ) +function _resolve_power_data_path(filename) + isfile(filename) && return filename + return joinpath(ExaPowerIO.get_path(:pglib), filename) +end - return data +function parse_ac_power_data(filename, T = Float64; from = nothing) + path = _resolve_power_data_path(filename) + @info "Loading power case file" + return PowerIO.parse_ac_power_data(path; from = from, T = T) end