From 04aaa909548a3d5a476075d0313981ffa786af43 Mon Sep 17 00:00:00 2001 From: Juan Ospina Date: Tue, 26 Apr 2022 09:16:16 -0600 Subject: [PATCH 01/16] ADD: eng2math_passthrough parameter to all instantiate_model and solve_X functions. --- CHANGELOG.md | 6 +++ src/core/base.jl | 44 +++++++++++++++++---- src/prob/opfitd.jl | 10 +++-- src/prob/opfitd_dmld.jl | 10 +++-- src/prob/opfitd_oltc.jl | 10 +++-- src/prob/pfitd.jl | 5 ++- test/data/json/case5_case3_bal_battery.json | 6 +++ test/opfitd_pass.jl | 32 +++++++++++++++ test/runtests.jl | 37 ++++++++--------- 9 files changed, 120 insertions(+), 40 deletions(-) create mode 100755 test/data/json/case5_case3_bal_battery.json create mode 100644 test/opfitd_pass.jl diff --git a/CHANGELOG.md b/CHANGELOG.md index 8e0855b..3d63f08 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -65,6 +65,12 @@ - Added `Pull` request template to `.github`. - Fixed major bug in `transform_pmitd_solution_to_eng!` function that was causing boundary power flow values to be the same for all nw in multinetwork problems. - Added unit test to `opfitd_mn` (and new data files) that test that boundary power flow solution values differ when solving a multinetwork problem with different loading conditions. +- Added `eng2math_passthrough` parameter to all `instantiate_model(..)` and `solve_X(...)` functions. +- Added unit tests to `opftid_pass.jl` that test the correct operation of the `eng2math_passthrough` both in single network and multinetwork applications. +- Added test cases files for the `opftid_pass.jl` unit tests. +- Added new problem specifications `build_opfitd_storage` and `build_mn_opfitd_storage` to `prob/opfitd_storage.jl` that perform opf optimization taking into account storage costs. +- Added new objective functions to `objective_storage.jl` that consider energy storage costs in the objective function for the opf optimization. +- Added unit tests to `opftid_pass.jl` that test the new costs functions with energy storage costs added. ## v0.7.0 diff --git a/src/core/base.jl b/src/core/base.jl index 7e33b36..95eef6a 100755 --- a/src/core/base.jl +++ b/src/core/base.jl @@ -65,6 +65,7 @@ sol(pmitd::AbstractPowerModelITD, key::Symbol, idx::Any; nw::Int=nw_id_default) build_method::Function; multinetwork::Bool=false, pmitd_ref_extensions::Vector{<:Function}=Function[], + eng2math_passthrough::Dict{String,Vector{String}}=Dict{String,Vector{String}}(), auto_rename::Bool=false, kwargs... ) @@ -75,10 +76,12 @@ respectively. Here, `pmitd_type` is the integrated power transmission-distributi `build_method` is the build method for the problem specification being considered. `multinetwork` is the boolean that defines if the modeling object should be define as multinetwork. `pmitd_ref_extensions` are the arrays of power transmission and distribution modeling extensions. +`eng2math_passthrough` are the passthrough vectors to be considered by the PMD MATH models. """ function instantiate_model( pm_file::String, pmd_files::Vector{String}, pmitd_file::String, pmitd_type::Type, - build_method::Function; multinetwork::Bool=false, pmitd_ref_extensions::Vector{<:Function}=Function[], auto_rename::Bool=false, kwargs...) + build_method::Function; multinetwork::Bool=false, pmitd_ref_extensions::Vector{<:Function}=Function[], + eng2math_passthrough::Dict{String,Vector{String}}=Dict{String,Vector{String}}(), auto_rename::Bool=false, kwargs...) # Read power t&d and linkage data from files. pmitd_data = parse_files(pm_file, pmd_files, pmitd_file; multinetwork=multinetwork, auto_rename=auto_rename) @@ -87,7 +90,9 @@ function instantiate_model( return instantiate_model( pmitd_data, pmitd_type, build_method; multinetwork=multinetwork, - pmitd_ref_extensions=pmitd_ref_extensions, kwargs...) + pmitd_ref_extensions=pmitd_ref_extensions, + eng2math_passthrough=eng2math_passthrough, + kwargs...) end @@ -100,6 +105,7 @@ end build_method::Function; multinetwork::Bool=false, pmitd_ref_extensions::Vector{<:Function}=Function[], + eng2math_passthrough::Dict{String,Vector{String}}=Dict{String,Vector{String}}(), auto_rename::Bool=false, kwargs... ) @@ -110,10 +116,12 @@ respectively. Here, `pmitd_type` is the integrated power transmission-distributi `build_method` is the build method for the problem specification being considered. `multinetwork` is the boolean that defines if the modeling object should be define as multinetwork. `pmitd_ref_extensions` are the arrays of power transmission and distribution modeling extensions. +`eng2math_passthrough` are the passthrough vectors to be considered by the PMD MATH models. """ function instantiate_model( pm_file::String, pmd_file::String, pmitd_file::String, pmitd_type::Type, - build_method::Function; multinetwork::Bool=false, pmitd_ref_extensions::Vector{<:Function}=Function[], auto_rename::Bool=false, kwargs...) + build_method::Function; multinetwork::Bool=false, pmitd_ref_extensions::Vector{<:Function}=Function[], + eng2math_passthrough::Dict{String,Vector{String}}=Dict{String,Vector{String}}(), auto_rename::Bool=false, kwargs...) pmd_files = [pmd_file] # convert to vector @@ -124,7 +132,9 @@ function instantiate_model( return instantiate_model( pmitd_data, pmitd_type, build_method; multinetwork=multinetwork, - pmitd_ref_extensions=pmitd_ref_extensions, kwargs... + pmitd_ref_extensions=pmitd_ref_extensions, + eng2math_passthrough=eng2math_passthrough, + kwargs... ) end @@ -136,6 +146,7 @@ end build_method::Function; multinetwork::Bool=false, pmitd_ref_extensions::Vector{<:Function}=Function[], + eng2math_passthrough::Dict{String,Vector{String}}=Dict{String,Vector{String}}(), kwargs... ) @@ -145,17 +156,21 @@ transmission and distribution modeling type and `build_method` is the build meth specification being considered. `multinetwork` is the boolean that defines if the modeling object should be define as multinetwork. `pmitd_ref_extensions` is an array of power transmission and distribution modeling extensions. +`eng2math_passthrough` are the passthrough vectors to be considered by the PMD MATH models. """ function instantiate_model( pmitd_data::Dict{String,<:Any}, pmitd_type::Type, build_method::Function; - multinetwork::Bool=false, pmitd_ref_extensions::Vector{<:Function}=Function[], kwargs...) + multinetwork::Bool=false, pmitd_ref_extensions::Vector{<:Function}=Function[], + eng2math_passthrough::Dict{String,Vector{String}}=Dict{String,Vector{String}}(), kwargs...) # Extract PMD model/data pmd_data = pmitd_data["it"][_PMD.pmd_it_name] # transform PMD data (only) from ENG to MATH Model if (!_PMD.ismath(pmd_data)) - pmitd_data["it"][_PMD.pmd_it_name] = _PMD.transform_data_model(pmd_data; multinetwork=multinetwork) + pmitd_data["it"][_PMD.pmd_it_name] = _PMD.transform_data_model(pmd_data; + multinetwork=multinetwork, + eng2math_passthrough=eng2math_passthrough) end # Correct the network data and assign the respective boundary number values. @@ -181,6 +196,7 @@ end multinetwork::Bool=false, solution_processors::Vector{<:Function}=Function[], pmitd_ref_extensions::Vector{<:Function}=Function[], + eng2math_passthrough::Dict{String,Vector{String}}=Dict{String,Vector{String}}(), make_si::Bool=true, auto_rename::Bool=false, solution_model::String="eng", @@ -195,6 +211,7 @@ and `pmitd_file`, respectively. Here, `pmitd_type` is the integrated power trans specification being considered, `multinetwork` is the boolean that defines if the modeling object should be define as multinetwork, `solution_processors` is the vector of the model solution processors, `pmitd_ref_extensions` is the array of modeling extensions, and `make_si` is the boolean that determines if the results are returned in SI or per-unit. +`eng2math_passthrough` are the passthrough vectors to be considered by the PMD MATH models. The variable `auto_rename` indicates if the user wants PMITD to automatically rename distribution systems with repeated ckt names. `solution_model` is a string that determines in which model, ENG or MATH, the solutions are presented. Returns a dictionary of results. @@ -206,6 +223,7 @@ function solve_model( multinetwork::Bool=false, solution_processors::Vector{<:Function}=Function[], pmitd_ref_extensions::Vector{<:Function}=Function[], + eng2math_passthrough::Dict{String,Vector{String}}=Dict{String,Vector{String}}(), make_si::Bool=true, auto_rename::Bool=false, solution_model::String="eng", @@ -222,6 +240,7 @@ function solve_model( multinetwork=multinetwork, solution_processors=solution_processors, pmitd_ref_extensions=pmitd_ref_extensions, + eng2math_passthrough=eng2math_passthrough, make_si=make_si, solution_model=solution_model, kwargs... @@ -241,6 +260,7 @@ end multinetwork::Bool=false, solution_processors::Vector{<:Function}=Function[], pmitd_ref_extensions::Vector{<:Function}=Function[], + eng2math_passthrough::Dict{String,Vector{String}}=Dict{String,Vector{String}}(), make_si::Bool=true, auto_rename::Bool=false, solution_model::String="eng", @@ -255,6 +275,7 @@ the build method for the problem specification being considered, `multinetwork` modeling object should be define as multinetwork,`solution_processors` is the vector of the model solution processors, `pmitd_ref_extensions` is the array of modeling extensions, and `make_si` is the boolean that determines if the results are returned in SI or per-unit. +`eng2math_passthrough` are the passthrough vectors to be considered by the PMD MATH models. The variable `auto_rename` indicates if the user wants PMITD to automatically rename distribution systems with repeated ckt names. `solution_model` is a string that determines in which model, ENG or MATH, the solutions are presented. Returns a dictionary of results. @@ -266,6 +287,7 @@ function solve_model( multinetwork::Bool=false, solution_processors::Vector{<:Function}=Function[], pmitd_ref_extensions::Vector{<:Function}=Function[], + eng2math_passthrough::Dict{String,Vector{String}}=Dict{String,Vector{String}}(), make_si::Bool=true, auto_rename::Bool=false, solution_model::String="eng", @@ -280,6 +302,7 @@ function solve_model( multinetwork=multinetwork, solution_processors=solution_processors, pmitd_ref_extensions=pmitd_ref_extensions, + eng2math_passthrough=eng2math_passthrough, make_si=make_si, solution_model=solution_model, kwargs... @@ -296,6 +319,7 @@ end multinetwork::Bool=false, solution_processors::Vector{<:Function}=Function[], pmitd_ref_extensions::Vector{<:Function}=Function[], + eng2math_passthrough::Dict{String,Vector{String}}=Dict{String,Vector{String}}(), make_si::Bool=true, solution_model::String="eng", kwargs... @@ -307,7 +331,8 @@ is the integrated power transmission-distribution modeling type, `build_method` problem specification being considered, `multinetwork` is the boolean that defines if the modeling object should be define as multinetwork`, solution_processors` is the vector of the model solution processors, `pmitd_ref_extensions` is the array of modeling extensions, and `make_si` is the boolean that determines -if the results are returned in SI or per-unit. `solution_model` is a string that determines in which model, +if the results are returned in SI or per-unit. `eng2math_passthrough` are the passthrough vectors to be +considered by the PMD MATH models. `solution_model` is a string that determines in which model, ENG or MATH, the solutions are presented. Returns a dictionary of results. """ @@ -318,6 +343,7 @@ function solve_model( multinetwork::Bool=false, solution_processors::Vector{<:Function}=Function[], pmitd_ref_extensions::Vector{<:Function}=Function[], + eng2math_passthrough::Dict{String,Vector{String}}=Dict{String,Vector{String}}(), make_si::Bool=true, solution_model::String="eng", kwargs...) @@ -329,7 +355,9 @@ function solve_model( pmitd = instantiate_model( pmitd_data, pmitd_type, build_method; multinetwork=multinetwork, - pmitd_ref_extensions=pmitd_ref_extensions, kwargs...) + pmitd_ref_extensions=pmitd_ref_extensions, + eng2math_passthrough=eng2math_passthrough, + kwargs...) # Solve ITD Model result = _IM.optimize_model!( diff --git a/src/prob/opfitd.jl b/src/prob/opfitd.jl index 33c7d22..c3f331e 100755 --- a/src/prob/opfitd.jl +++ b/src/prob/opfitd.jl @@ -9,6 +9,7 @@ optimizer; solution_processors::Vector{<:Function}=Function[], pmitd_ref_extensions::Vector{<:Function}=Vector{Function}([]), + eng2math_passthrough::Dict{String,Vector{String}}=Dict{String,Vector{String}}(), make_si::Bool=true, auto_rename::Bool=false, solution_model::String="eng", @@ -17,8 +18,8 @@ Solve Integrated T&D Optimal Power Flow. """ -function solve_opfitd(pm_file, pmd_file, pmitd_file, pmitd_type, optimizer; solution_processors::Vector{<:Function}=Function[], pmitd_ref_extensions::Vector{<:Function}=Vector{Function}([]), make_si::Bool=true, auto_rename::Bool=false, solution_model::String="eng", kwargs...) - return solve_model(pm_file, pmd_file, pmitd_file, pmitd_type, optimizer, build_opfitd; solution_processors=solution_processors, pmitd_ref_extensions=pmitd_ref_extensions, make_si=make_si, auto_rename=auto_rename, solution_model=solution_model, kwargs...) +function solve_opfitd(pm_file, pmd_file, pmitd_file, pmitd_type, optimizer; solution_processors::Vector{<:Function}=Function[], pmitd_ref_extensions::Vector{<:Function}=Vector{Function}([]), eng2math_passthrough::Dict{String,Vector{String}}=Dict{String,Vector{String}}(), make_si::Bool=true, auto_rename::Bool=false, solution_model::String="eng", kwargs...) + return solve_model(pm_file, pmd_file, pmitd_file, pmitd_type, optimizer, build_opfitd; solution_processors=solution_processors, pmitd_ref_extensions=pmitd_ref_extensions, eng2math_passthrough=eng2math_passthrough, make_si=make_si, auto_rename=auto_rename, solution_model=solution_model, kwargs...) end @@ -31,6 +32,7 @@ end optimizer; solution_processors::Vector{<:Function}=Function[], pmitd_ref_extensions::Vector{<:Function}=Vector{Function}([]), + eng2math_passthrough::Dict{String,Vector{String}}=Dict{String,Vector{String}}(), make_si::Bool=true, auto_rename::Bool=false, solution_model::String="eng", @@ -39,8 +41,8 @@ end Solve Multinetwork Integrated T&D Optimal Power Flow. """ -function solve_mn_opfitd(pm_file, pmd_file, pmitd_file, pmitd_type, optimizer; solution_processors::Vector{<:Function}=Function[], pmitd_ref_extensions::Vector{<:Function}=Vector{Function}([]), make_si::Bool=true, auto_rename::Bool=false, solution_model::String="eng", kwargs...) - return solve_model(pm_file, pmd_file, pmitd_file, pmitd_type, optimizer, build_mn_opfitd; multinetwork=true, solution_processors=solution_processors, pmitd_ref_extensions=pmitd_ref_extensions, make_si=make_si, auto_rename=auto_rename, solution_model=solution_model, kwargs...) +function solve_mn_opfitd(pm_file, pmd_file, pmitd_file, pmitd_type, optimizer; solution_processors::Vector{<:Function}=Function[], pmitd_ref_extensions::Vector{<:Function}=Vector{Function}([]), eng2math_passthrough::Dict{String,Vector{String}}=Dict{String,Vector{String}}(), make_si::Bool=true, auto_rename::Bool=false, solution_model::String="eng", kwargs...) + return solve_model(pm_file, pmd_file, pmitd_file, pmitd_type, optimizer, build_mn_opfitd; multinetwork=true, solution_processors=solution_processors, pmitd_ref_extensions=pmitd_ref_extensions, eng2math_passthrough=eng2math_passthrough, make_si=make_si, auto_rename=auto_rename, solution_model=solution_model, kwargs...) end diff --git a/src/prob/opfitd_dmld.jl b/src/prob/opfitd_dmld.jl index dc651fa..41106c0 100755 --- a/src/prob/opfitd_dmld.jl +++ b/src/prob/opfitd_dmld.jl @@ -9,6 +9,7 @@ optimizer; solution_processors::Vector{<:Function}=Function[], pmitd_ref_extensions::Vector{<:Function}=Vector{Function}([]), + eng2math_passthrough::Dict{String,Vector{String}}=Dict{String,Vector{String}}(), make_si::Bool=true, auto_rename::Bool=false, solution_model::String="eng", @@ -17,8 +18,8 @@ Solve Integrated T&D Optimal Power Flow with minimum load delta (dmld). """ -function solve_dmld_opfitd(pm_file, pmd_file, pmitd_file, pmitd_type, optimizer; solution_processors::Vector{<:Function}=Function[], pmitd_ref_extensions::Vector{<:Function}=Vector{Function}([]), make_si::Bool=true, auto_rename::Bool=false, solution_model::String="eng", kwargs...) - return solve_model(pm_file, pmd_file, pmitd_file, pmitd_type, optimizer, build_dmld_opfitd; solution_processors=solution_processors, pmitd_ref_extensions=pmitd_ref_extensions, make_si=make_si, auto_rename=auto_rename, solution_model=solution_model, kwargs...) +function solve_dmld_opfitd(pm_file, pmd_file, pmitd_file, pmitd_type, optimizer; solution_processors::Vector{<:Function}=Function[], pmitd_ref_extensions::Vector{<:Function}=Vector{Function}([]), eng2math_passthrough::Dict{String,Vector{String}}=Dict{String,Vector{String}}(), make_si::Bool=true, auto_rename::Bool=false, solution_model::String="eng", kwargs...) + return solve_model(pm_file, pmd_file, pmitd_file, pmitd_type, optimizer, build_dmld_opfitd; solution_processors=solution_processors, pmitd_ref_extensions=pmitd_ref_extensions, eng2math_passthrough=eng2math_passthrough, make_si=make_si, auto_rename=auto_rename, solution_model=solution_model, kwargs...) end @@ -31,6 +32,7 @@ end optimizer; solution_processors::Vector{<:Function}=Function[], pmitd_ref_extensions::Vector{<:Function}=Vector{Function}([]), + eng2math_passthrough::Dict{String,Vector{String}}=Dict{String,Vector{String}}(), make_si::Bool=true, auto_rename::Bool=false, solution_model::String="eng", @@ -39,8 +41,8 @@ end Solve Multinetwork Integrated T&D Optimal Power Flow with minimum load delta (dmld continuous). """ -function solve_mn_dmld_opfitd_simple(pm_file, pmd_file, pmitd_file, pmitd_type, optimizer; solution_processors::Vector{<:Function}=Function[], pmitd_ref_extensions::Vector{<:Function}=Vector{Function}([]), make_si::Bool=true, auto_rename::Bool=false, solution_model::String="eng", kwargs...) - return solve_model(pm_file, pmd_file, pmitd_file, pmitd_type, optimizer, build_mn_dmld_opfitd_simple; multinetwork=true, solution_processors=solution_processors, pmitd_ref_extensions=pmitd_ref_extensions, make_si=make_si, auto_rename=auto_rename, solution_model=solution_model, kwargs...) +function solve_mn_dmld_opfitd_simple(pm_file, pmd_file, pmitd_file, pmitd_type, optimizer; solution_processors::Vector{<:Function}=Function[], pmitd_ref_extensions::Vector{<:Function}=Vector{Function}([]), eng2math_passthrough::Dict{String,Vector{String}}=Dict{String,Vector{String}}(), make_si::Bool=true, auto_rename::Bool=false, solution_model::String="eng", kwargs...) + return solve_model(pm_file, pmd_file, pmitd_file, pmitd_type, optimizer, build_mn_dmld_opfitd_simple; multinetwork=true, solution_processors=solution_processors, pmitd_ref_extensions=pmitd_ref_extensions, eng2math_passthrough=eng2math_passthrough, make_si=make_si, auto_rename=auto_rename, solution_model=solution_model, kwargs...) end diff --git a/src/prob/opfitd_oltc.jl b/src/prob/opfitd_oltc.jl index a4640d3..cbbd9c0 100644 --- a/src/prob/opfitd_oltc.jl +++ b/src/prob/opfitd_oltc.jl @@ -9,6 +9,7 @@ optimizer; solution_processors::Vector{<:Function}=Function[], pmitd_ref_extensions::Vector{<:Function}=Vector{Function}([]), + eng2math_passthrough::Dict{String,Vector{String}}=Dict{String,Vector{String}}(), make_si::Bool=true, auto_rename::Bool=false, solution_model::String="eng", @@ -17,8 +18,8 @@ Solve Integrated T&D OLTC Optimal Power Flow. """ -function solve_opfitd_oltc(pm_file, pmd_file, pmitd_file, pmitd_type, optimizer; solution_processors::Vector{<:Function}=Function[], pmitd_ref_extensions::Vector{<:Function}=Vector{Function}([]), make_si::Bool=true, auto_rename::Bool=false, solution_model::String="eng", kwargs...) - return solve_model(pm_file, pmd_file, pmitd_file, pmitd_type, optimizer, build_opfitd_oltc; solution_processors=solution_processors, pmitd_ref_extensions=pmitd_ref_extensions, make_si=make_si, auto_rename=auto_rename, solution_model=solution_model, kwargs...) +function solve_opfitd_oltc(pm_file, pmd_file, pmitd_file, pmitd_type, optimizer; solution_processors::Vector{<:Function}=Function[], pmitd_ref_extensions::Vector{<:Function}=Vector{Function}([]), eng2math_passthrough::Dict{String,Vector{String}}=Dict{String,Vector{String}}(), make_si::Bool=true, auto_rename::Bool=false, solution_model::String="eng", kwargs...) + return solve_model(pm_file, pmd_file, pmitd_file, pmitd_type, optimizer, build_opfitd_oltc; solution_processors=solution_processors, pmitd_ref_extensions=pmitd_ref_extensions, eng2math_passthrough=eng2math_passthrough, make_si=make_si, auto_rename=auto_rename, solution_model=solution_model, kwargs...) end @@ -31,6 +32,7 @@ end optimizer; solution_processors::Vector{<:Function}=Function[], pmitd_ref_extensions::Vector{<:Function}=Vector{Function}([]), + eng2math_passthrough::Dict{String,Vector{String}}=Dict{String,Vector{String}}(), make_si::Bool=true, auto_rename::Bool=false, solution_model::String="eng", @@ -39,8 +41,8 @@ end Solve Multinetwork Integrated T&D OLTC Optimal Power Flow. """ -function solve_mn_opfitd_oltc(pm_file, pmd_file, pmitd_file, pmitd_type, optimizer; solution_processors::Vector{<:Function}=Function[], pmitd_ref_extensions::Vector{<:Function}=Vector{Function}([]), make_si::Bool=true, auto_rename::Bool=false, solution_model::String="eng", kwargs...) - return solve_model(pm_file, pmd_file, pmitd_file, pmitd_type, optimizer, build_mn_opfitd_oltc; multinetwork=true, solution_processors=solution_processors, pmitd_ref_extensions=pmitd_ref_extensions, make_si=make_si, auto_rename=auto_rename, solution_model=solution_model, kwargs...) +function solve_mn_opfitd_oltc(pm_file, pmd_file, pmitd_file, pmitd_type, optimizer; solution_processors::Vector{<:Function}=Function[], pmitd_ref_extensions::Vector{<:Function}=Vector{Function}([]), eng2math_passthrough::Dict{String,Vector{String}}=Dict{String,Vector{String}}(), make_si::Bool=true, auto_rename::Bool=false, solution_model::String="eng", kwargs...) + return solve_model(pm_file, pmd_file, pmitd_file, pmitd_type, optimizer, build_mn_opfitd_oltc; multinetwork=true, solution_processors=solution_processors, pmitd_ref_extensions=pmitd_ref_extensions, eng2math_passthrough=eng2math_passthrough, make_si=make_si, auto_rename=auto_rename, solution_model=solution_model, kwargs...) end diff --git a/src/prob/pfitd.jl b/src/prob/pfitd.jl index 654adf8..d1d13ec 100755 --- a/src/prob/pfitd.jl +++ b/src/prob/pfitd.jl @@ -9,6 +9,7 @@ optimizer; solution_processors::Vector{<:Function}=Function[], pmitd_ref_extensions::Vector{<:Function}=Vector{Function}([]), + eng2math_passthrough::Dict{String,Vector{String}}=Dict{String,Vector{String}}(), make_si::Bool=true, auto_rename::Bool=false, solution_model::String="eng", @@ -17,8 +18,8 @@ Solve Integrated T&D Power Flow """ -function solve_pfitd(pm_file, pmd_file, pmitd_file, pmitd_type, optimizer; solution_processors::Vector{<:Function}=Function[], pmitd_ref_extensions::Vector{<:Function}=Vector{Function}([]), make_si::Bool=true, auto_rename::Bool=false, solution_model::String="eng", kwargs...) - return solve_model(pm_file, pmd_file, pmitd_file, pmitd_type, optimizer, build_pfitd; solution_processors=solution_processors, pmitd_ref_extensions=pmitd_ref_extensions, make_si=make_si, auto_rename=auto_rename, solution_model=solution_model, kwargs...) +function solve_pfitd(pm_file, pmd_file, pmitd_file, pmitd_type, optimizer; solution_processors::Vector{<:Function}=Function[], pmitd_ref_extensions::Vector{<:Function}=Vector{Function}([]), eng2math_passthrough::Dict{String,Vector{String}}=Dict{String,Vector{String}}(), make_si::Bool=true, auto_rename::Bool=false, solution_model::String="eng", kwargs...) + return solve_model(pm_file, pmd_file, pmitd_file, pmitd_type, optimizer, build_pfitd; solution_processors=solution_processors, pmitd_ref_extensions=pmitd_ref_extensions, eng2math_passthrough=eng2math_passthrough, make_si=make_si, auto_rename=auto_rename, solution_model=solution_model, kwargs...) end diff --git a/test/data/json/case5_case3_bal_battery.json b/test/data/json/case5_case3_bal_battery.json new file mode 100755 index 0000000..95a3dbe --- /dev/null +++ b/test/data/json/case5_case3_bal_battery.json @@ -0,0 +1,6 @@ +[ + { + "transmission_boundary": "5", + "distribution_boundary": "3bus_bal_battery.voltage_source.source" + } +] diff --git a/test/opfitd_pass.jl b/test/opfitd_pass.jl new file mode 100644 index 0000000..15c2cd8 --- /dev/null +++ b/test/opfitd_pass.jl @@ -0,0 +1,32 @@ +@info "running integrated transmission-distribution optimal power flow (opfitd) with passthroughs tests" + +@testset "test/opfitd_pass.jl" begin + + @testset "solve_model (with network inputs): Balanced case5-case3 Without Dist. Generator ACR-ACR" begin + pm_file = joinpath(dirname(trans_path), "case5_withload.m") + pmd_file = joinpath(dirname(dist_path), "case3_balanced_withBattery.dss") + pmitd_file = joinpath(dirname(bound_path), "case5_case3_bal_battery.json") + pmitd_type = NLPowerModelITD{ACRPowerModel, ACRUPowerModel} + pmitd_data = parse_files(pm_file, pmd_file, pmitd_file) + + # pmitd_inst_model = instantiate_model(pmitd_data, pmitd_type, build_opfitd) + eng2math_passthrough = Dict("storage"=>["cost", "0.25"]) + pmitd_inst_model = instantiate_model(pmitd_data, pmitd_type, build_opfitd; eng2math_passthrough=eng2math_passthrough) + # @info "$(pmitd_inst_model)" + + storage_ref = _IM.ref(pmitd_inst_model, _PMD.pmd_it_sym, nw=nw_id_default, :storage) + @info "$(storage_ref)" + + # result = solve_model(pmitd_data, pmitd_type, ipopt, build_opfitd; make_si=true) + # @test result["termination_status"] == LOCALLY_SOLVED + + + #### PMD tests + pmd_parsed = _PMD.parse_file(pmd_file) + eng2math_passthrough = Dict("storage"=>["cost", "0.25"]) + pmd_transformed = _PMD.transform_data_model(pmd_parsed; eng2math_passthrough=eng2math_passthrough) + @info "$(pmd_transformed["storage"]["cost"])" + # @info "$(pmd_transformed)" # passthrough not working! (ASK DAVID IF THIS IS A BUG). + end + +end diff --git a/test/runtests.jl b/test/runtests.jl index 554d229..bf7936a 100755 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -23,22 +23,23 @@ scs_solver = optimizer_with_attributes(SCS.Optimizer, "verbose"=>0) PowerModelsITD.silence!() @testset "PowerModelsITD.jl" begin - include("io.jl") - include("base.jl") - include("data.jl") - include("autorename.jl") - include("opfitd.jl") - include("opfitd_duals.jl") - include("pfitd.jl") - include("opfitd_ms.jl") - include("pfitd_ms.jl") - include("transformations_opfitd.jl") - include("largescale_opfitd.jl") - include("opfitd_hybrids.jl") - include("pfitd_hybrids.jl") - include("opfitd_oltc.jl") - include("opfitd_mn.jl") - include("opfitd_oltc_mn.jl") - include("opfitd_dmld.jl") - include("opfitd_solution.jl") + # include("io.jl") + # include("base.jl") + # include("data.jl") + # include("autorename.jl") + # include("opfitd.jl") + # include("opfitd_duals.jl") + # include("pfitd.jl") + # include("opfitd_ms.jl") + # include("pfitd_ms.jl") + # include("transformations_opfitd.jl") + # include("largescale_opfitd.jl") + # include("opfitd_hybrids.jl") + # include("pfitd_hybrids.jl") + # include("opfitd_oltc.jl") + # include("opfitd_mn.jl") + # include("opfitd_oltc_mn.jl") + # include("opfitd_dmld.jl") + # include("opfitd_solution.jl") + include("opfitd_pass.jl") end From 28b0d251605d54b9261a1b1065cd6f8369f02ee0 Mon Sep 17 00:00:00 2001 From: Juan Ospina Date: Tue, 26 Apr 2022 12:02:51 -0600 Subject: [PATCH 02/16] ADD: unit tests unit tests to opftid_pass.jl that test the correct operation of the eng2math_passthrough both in single network and multinetwork applications. --- .../case3_balanced_withBattery_mn.dss | 50 +++++++++++++++++++ .../data/json/case5_case3_bal_battery_mn.json | 6 +++ test/opfitd_pass.jl | 49 ++++++++++++------ test/runtests.jl | 36 ++++++------- 4 files changed, 109 insertions(+), 32 deletions(-) create mode 100755 test/data/distribution/case3_balanced_withBattery_mn.dss create mode 100755 test/data/json/case5_case3_bal_battery_mn.json diff --git a/test/data/distribution/case3_balanced_withBattery_mn.dss b/test/data/distribution/case3_balanced_withBattery_mn.dss new file mode 100755 index 0000000..7c41f76 --- /dev/null +++ b/test/data/distribution/case3_balanced_withBattery_mn.dss @@ -0,0 +1,50 @@ +!Clear +New Circuit.3bus_bal_battery_mn +! define a really stiff source +~ basekv=230 pu=1.00 MVAsc3=200000 MVAsc1=210000 + +! Substation Transformer +New Transformer.SubXF Phases=3 Windings=2 Xhl=0.01 +~ wdg=1 bus=sourcebus conn=wye kv=230 kva=25000 %r=0.0005 +~ wdg=2 bus=Substation conn=wye kv=13.8 kva=25000 %r=0.0005 + + +!Define Linecodes + +New linecode.556MCM nphases=3 basefreq=60 ! ohms per 5 mile +~ rmatrix = ( 0.1000 | 0.0400 0.1000 | 0.0400 0.0400 0.1000) +~ xmatrix = ( 0.0583 | 0.0233 0.0583 | 0.0233 0.0233 0.0583) +~ cmatrix = (50.92958178940651 | -0 50.92958178940651 | -0 -0 50.92958178940651 ) ! small capacitance + + +New linecode.4/0QUAD nphases=3 basefreq=60 ! ohms per 100ft +~ rmatrix = ( 0.1167 | 0.0467 0.1167 | 0.0467 0.0467 0.1167) +~ xmatrix = (0.0667 | 0.0267 0.0667 | 0.0267 0.0267 0.0667 ) +~ cmatrix = (50.92958178940651 | -0 50.92958178940651 | -0 -0 50.92958178940651 ) ! small capacitance + + +!Define lines + +New Line.OHLine bus1=Substation.1.2.3 Primary.1.2.3 linecode = 556MCM length=1 ! 5 mile line +New Line.Quad Bus1=Primary.1.2.3 loadbus.1.2.3 linecode = 4/0QUAD length=1 ! 100 ft + + +!Loads - single phase +New Loadshape.ls1 pmult=(file=load_profile.csv) + +New Load.L1 phases=1 loadbus.1.0 ( 13.8 3 sqrt / ) kW=3000 kvar=1500 model=1 daily=ls1 +New Load.L2 phases=1 loadbus.2.0 ( 13.8 3 sqrt / ) kW=3000 kvar=1500 model=1 daily=ls1 +New Load.L3 phases=1 loadbus.3.0 ( 13.8 3 sqrt / ) kW=3000 kvar=1500 model=1 daily=ls1 + + +!Battery System DEFINITIONS +New Storage.S1 phases=3 bus1=primary.1.2.3 kv=( 13.8 3 sqrt / ) kwhstored=50 kwhrated=50 kva=100 kvar=100 +~ %charge=100 %discharge=100 %effcharge=100 %effdischarge=100 %idlingkw=1 %r=0 %x=50 + + +Set VoltageBases = "230,13.8" +Set tolerance=0.000001 +set defaultbasefreq=60 +!Calcvoltagebases +!Solve + diff --git a/test/data/json/case5_case3_bal_battery_mn.json b/test/data/json/case5_case3_bal_battery_mn.json new file mode 100755 index 0000000..187606a --- /dev/null +++ b/test/data/json/case5_case3_bal_battery_mn.json @@ -0,0 +1,6 @@ +[ + { + "transmission_boundary": "5", + "distribution_boundary": "3bus_bal_battery_mn.voltage_source.source" + } +] diff --git a/test/opfitd_pass.jl b/test/opfitd_pass.jl index 15c2cd8..a8720b4 100644 --- a/test/opfitd_pass.jl +++ b/test/opfitd_pass.jl @@ -1,32 +1,53 @@ -@info "running integrated transmission-distribution optimal power flow (opfitd) with passthroughs tests" +@info "running ITD eng2math_passthrough tests" @testset "test/opfitd_pass.jl" begin - @testset "solve_model (with network inputs): Balanced case5-case3 Without Dist. Generator ACR-ACR" begin + @testset "Check that eng2math_passthrough value is being added to the instantiated model: " begin pm_file = joinpath(dirname(trans_path), "case5_withload.m") pmd_file = joinpath(dirname(dist_path), "case3_balanced_withBattery.dss") pmitd_file = joinpath(dirname(bound_path), "case5_case3_bal_battery.json") pmitd_type = NLPowerModelITD{ACRPowerModel, ACRUPowerModel} pmitd_data = parse_files(pm_file, pmd_file, pmitd_file) - # pmitd_inst_model = instantiate_model(pmitd_data, pmitd_type, build_opfitd) - eng2math_passthrough = Dict("storage"=>["cost", "0.25"]) + # add cost to storages in PMD + for (st_name, st_data) in pmitd_data["it"]["pmd"]["storage"] + st_data["cost"] = 0.25 + end + + # instantiate model with eng2math_passthrough + eng2math_passthrough = Dict("storage"=>["cost"]) pmitd_inst_model = instantiate_model(pmitd_data, pmitd_type, build_opfitd; eng2math_passthrough=eng2math_passthrough) - # @info "$(pmitd_inst_model)" + # get storage reference storage_ref = _IM.ref(pmitd_inst_model, _PMD.pmd_it_sym, nw=nw_id_default, :storage) - @info "$(storage_ref)" - # result = solve_model(pmitd_data, pmitd_type, ipopt, build_opfitd; make_si=true) - # @test result["termination_status"] == LOCALLY_SOLVED + # test that the "cost" value in storage exists. + @test storage_ref[1]["cost"] == 0.25 + end + + @testset "Check that eng2math_passthrough value is being added to the instantiated model (Multinetwork): " begin + pm_file = joinpath(dirname(trans_path), "case5_withload.m") + pmd_file = joinpath(dirname(dist_path), "case3_balanced_withBattery_mn.dss") + pmitd_file = joinpath(dirname(bound_path), "case5_case3_bal_battery_mn.json") + pmitd_type = NLPowerModelITD{ACRPowerModel, ACRUPowerModel} + pmitd_data = parse_files(pm_file, pmd_file, pmitd_file; multinetwork=true) + + # add cost to storages in PMD + for (nw_id, nw_data) in pmitd_data["it"]["pmd"]["nw"] + for (st_name, st_data) in nw_data["storage"] + st_data["cost"] = 0.25 + end + end + + # instantiate model with eng2math_passthrough + eng2math_passthrough = Dict("storage"=>["cost"]) + pmitd_inst_model = instantiate_model(pmitd_data, pmitd_type, build_mn_opfitd; multinetwork=true, eng2math_passthrough=eng2math_passthrough) + # get storage reference from nw=4 + storage_ref_nw4 = _IM.ref(pmitd_inst_model, _PMD.pmd_it_sym, nw=4, :storage) - #### PMD tests - pmd_parsed = _PMD.parse_file(pmd_file) - eng2math_passthrough = Dict("storage"=>["cost", "0.25"]) - pmd_transformed = _PMD.transform_data_model(pmd_parsed; eng2math_passthrough=eng2math_passthrough) - @info "$(pmd_transformed["storage"]["cost"])" - # @info "$(pmd_transformed)" # passthrough not working! (ASK DAVID IF THIS IS A BUG). + # test that the "cost" value in storage nw=4 exists. + @test storage_ref_nw4[1]["cost"] == 0.25 end end diff --git a/test/runtests.jl b/test/runtests.jl index bf7936a..cfd0f23 100755 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -23,23 +23,23 @@ scs_solver = optimizer_with_attributes(SCS.Optimizer, "verbose"=>0) PowerModelsITD.silence!() @testset "PowerModelsITD.jl" begin - # include("io.jl") - # include("base.jl") - # include("data.jl") - # include("autorename.jl") - # include("opfitd.jl") - # include("opfitd_duals.jl") - # include("pfitd.jl") - # include("opfitd_ms.jl") - # include("pfitd_ms.jl") - # include("transformations_opfitd.jl") - # include("largescale_opfitd.jl") - # include("opfitd_hybrids.jl") - # include("pfitd_hybrids.jl") - # include("opfitd_oltc.jl") - # include("opfitd_mn.jl") - # include("opfitd_oltc_mn.jl") - # include("opfitd_dmld.jl") - # include("opfitd_solution.jl") + include("io.jl") + include("base.jl") + include("data.jl") + include("autorename.jl") + include("opfitd.jl") + include("opfitd_duals.jl") + include("pfitd.jl") + include("opfitd_ms.jl") + include("pfitd_ms.jl") + include("transformations_opfitd.jl") + include("largescale_opfitd.jl") + include("opfitd_hybrids.jl") + include("pfitd_hybrids.jl") + include("opfitd_oltc.jl") + include("opfitd_mn.jl") + include("opfitd_oltc_mn.jl") + include("opfitd_dmld.jl") + include("opfitd_solution.jl") include("opfitd_pass.jl") end From 6edfeb3d44653323366279b7d9d34978e9559d0b Mon Sep 17 00:00:00 2001 From: Juan Ospina Date: Tue, 26 Apr 2022 12:45:23 -0600 Subject: [PATCH 03/16] ADD: new problem specifications build_opfitd_storage and build_mn_opfitd_storage to perform opf considering storage costs. --- src/PowerModelsITD.jl | 1 + src/prob/opfitd_storage.jl | 1290 ++++++++++++++++++++++++++++++++++++ test/opfitd_pass.jl | 38 ++ test/runtests.jl | 36 +- 4 files changed, 1347 insertions(+), 18 deletions(-) create mode 100755 src/prob/opfitd_storage.jl diff --git a/src/PowerModelsITD.jl b/src/PowerModelsITD.jl index 4155715..03e8468 100755 --- a/src/PowerModelsITD.jl +++ b/src/PowerModelsITD.jl @@ -62,6 +62,7 @@ module PowerModelsITD include("prob/opfitd.jl") include("prob/opfitd_oltc.jl") include("prob/opfitd_dmld.jl") + include("prob/opfitd_storage.jl") # This must come last to support automated export. include("core/export.jl") diff --git a/src/prob/opfitd_storage.jl b/src/prob/opfitd_storage.jl new file mode 100755 index 0000000..9fee75a --- /dev/null +++ b/src/prob/opfitd_storage.jl @@ -0,0 +1,1290 @@ +# Definitions for solving the integrated T&D opf problem with storage opf dispatch + +""" + function solve_opfitd_storage( + pm_file, + pmd_file, + pmitd_file, + pmitd_type, + optimizer; + solution_processors::Vector{<:Function}=Function[], + pmitd_ref_extensions::Vector{<:Function}=Vector{Function}([]), + eng2math_passthrough::Dict{String,Vector{String}}=Dict{String,Vector{String}}(), + make_si::Bool=true, + auto_rename::Bool=false, + solution_model::String="eng", + kwargs... + ) + +Solve Integrated T&D Optimal Power Flow with Storage OPF Dispatch. +""" +function solve_opfitd_storage(pm_file, pmd_file, pmitd_file, pmitd_type, optimizer; solution_processors::Vector{<:Function}=Function[], pmitd_ref_extensions::Vector{<:Function}=Vector{Function}([]), eng2math_passthrough::Dict{String,Vector{String}}=Dict{String,Vector{String}}(), make_si::Bool=true, auto_rename::Bool=false, solution_model::String="eng", kwargs...) + return solve_model(pm_file, pmd_file, pmitd_file, pmitd_type, optimizer, build_opfitd_storage; solution_processors=solution_processors, pmitd_ref_extensions=pmitd_ref_extensions, eng2math_passthrough=eng2math_passthrough, make_si=make_si, auto_rename=auto_rename, solution_model=solution_model, kwargs...) +end + + +""" + function solve_mn_opfitd_storage( + pm_file, + pmd_file, + pmitd_file, + pmitd_type, + optimizer; + solution_processors::Vector{<:Function}=Function[], + pmitd_ref_extensions::Vector{<:Function}=Vector{Function}([]), + eng2math_passthrough::Dict{String,Vector{String}}=Dict{String,Vector{String}}(), + make_si::Bool=true, + auto_rename::Bool=false, + solution_model::String="eng", + kwargs... + ) + +Solve Multinetwork Integrated T&D Optimal Power Flow with Storage OPF Dispatch. +""" +function solve_mn_opfitd_storage(pm_file, pmd_file, pmitd_file, pmitd_type, optimizer; solution_processors::Vector{<:Function}=Function[], pmitd_ref_extensions::Vector{<:Function}=Vector{Function}([]), eng2math_passthrough::Dict{String,Vector{String}}=Dict{String,Vector{String}}(), make_si::Bool=true, auto_rename::Bool=false, solution_model::String="eng", kwargs...) + return solve_model(pm_file, pmd_file, pmitd_file, pmitd_type, optimizer, build_mn_opfitd_storage; multinetwork=true, solution_processors=solution_processors, pmitd_ref_extensions=pmitd_ref_extensions, eng2math_passthrough=eng2math_passthrough, make_si=make_si, auto_rename=auto_rename, solution_model=solution_model, kwargs...) +end + + +""" + function build_opfitd_storage( + pmitd::AbstractPowerModelITD + ) +Constructor for Integrated T&D Optimal Power Flow with Storage OPF Dispatch. +""" +function build_opfitd_storage(pmitd::AbstractPowerModelITD) + + # Get Models + pm_model = _get_powermodel_from_powermodelitd(pmitd) + pmd_model = _get_powermodeldistribution_from_powermodelitd(pmitd) + + # PM(Transmission) Variables + _PM.variable_bus_voltage(pm_model) + _PM.variable_gen_power(pm_model) + _PM.variable_branch_power(pm_model) + _PM.variable_dcline_power(pm_model) + + # PMD(Distribution) Variables + _PMD.variable_mc_bus_voltage(pmd_model) + _PMD.variable_mc_branch_power(pmd_model) + _PMD.variable_mc_transformer_power(pmd_model) + _PMD.variable_mc_switch_power(pmd_model) + _PMD.variable_mc_generator_power(pmd_model) + _PMD.variable_mc_load_power(pmd_model) + _PMD.variable_mc_storage_power(pmd_model) + + # PMITD (Boundary) Variables + variable_boundary_power(pmitd) + + # --- PM(Transmission) Constraints --- + _PM.constraint_model_voltage(pm_model) + + # reference buses (this only needs to happen for pm(transmission)) + for i in _PM.ids(pm_model, :ref_buses) + _PM.constraint_theta_ref(pm_model, i) + end + + + # PM branches + for i in _PM.ids(pm_model, :branch) + _PM.constraint_ohms_yt_from(pm_model, i) + _PM.constraint_ohms_yt_to(pm_model, i) + + _PM.constraint_voltage_angle_difference(pm_model, i) + + _PM.constraint_thermal_limit_from(pm_model, i) + _PM.constraint_thermal_limit_to(pm_model, i) + end + + # PM DC lines + for i in _PM.ids(pm_model, :dcline) + _PM.constraint_dcline_power_losses(pm_model, i) + end + + # ------------------------------------------------- + # --- PMD(Distribution) Constraints --- + _PMD.constraint_mc_model_voltage(pmd_model) + + # generators should be constrained before KCL, or Pd/Qd undefined + for id in _PMD.ids(pmd_model, :gen) + _PMD.constraint_mc_generator_power(pmd_model, id) + end + + # loads should be constrained before KCL, or Pd/Qd undefined + for id in _PMD.ids(pmd_model, :load) + _PMD.constraint_mc_load_power(pmd_model, id) + end + + for i in _PMD.ids(pmd_model, :storage) + _PMD.constraint_storage_state(pmd_model, i) + _PMD.constraint_storage_complementarity_nl(pmd_model, i) + _PMD.constraint_mc_storage_losses(pmd_model, i) + _PMD.constraint_mc_storage_thermal_limit(pmd_model, i) + end + + for i in _PMD.ids(pmd_model, :branch) + _PMD.constraint_mc_ohms_yt_from(pmd_model, i) + _PMD.constraint_mc_ohms_yt_to(pmd_model, i) + + _PMD.constraint_mc_voltage_angle_difference(pmd_model, i) + + _PMD.constraint_mc_thermal_limit_from(pmd_model, i) + _PMD.constraint_mc_thermal_limit_to(pmd_model, i) + _PMD.constraint_mc_ampacity_from(pmd_model, i) + _PMD.constraint_mc_ampacity_to(pmd_model, i) + end + + for i in _PMD.ids(pmd_model, :switch) + _PMD.constraint_mc_switch_state(pmd_model, i) + _PMD.constraint_mc_switch_thermal_limit(pmd_model, i) + _PMD.constraint_mc_switch_ampacity(pmd_model, i) + end + + for i in _PMD.ids(pmd_model, :transformer) + _PMD.constraint_mc_transformer_power(pmd_model, i) + end + + # ------------------------------------------------- + # --- PMITD(T&D) INDEPENDENT Constraints ---------- + + for i in ids(pmitd, :boundary) + constraint_boundary_power(pmitd, i) + constraint_boundary_voltage_magnitude(pmitd, i) + constraint_boundary_voltage_angle(pmitd, i) + end + + # Note: Both of these need to consider flow on boundaries if bus is connected to boundary + # # ---- Transmission Power Balance --- + boundary_buses = Vector{Int}() # empty vector that stores the boundary buses, so they are not repeated by the other constraint + for i in _PM.ids(pm_model, :bus) + for j in ids(pmitd, :boundary) + constraint_transmission_power_balance_boundary(pmitd, i, j, boundary_buses) + end + if !(i in boundary_buses) + _PM.constraint_power_balance(pm_model, i) + end + end + + # # ---- Distribution Power Balance --- + boundary_buses = Vector{Int}() # empty vector that stores the boundary buses, so they are not repeated by the other constraint + for i in _PMD.ids(pmd_model, :bus) + for j in ids(pmitd, :boundary) + constraint_distribution_power_balance_boundary(pmitd, i, j, boundary_buses) + end + if !(i in boundary_buses) + _PMD.constraint_mc_power_balance(pmd_model, i) + end + end + + # ------------------------------------------------- + # --- PMITD(T&D) Cost Functions ------------------- + objective_itd_min_fuel_cost(pmitd) + +end + + +""" + function build_opfitd_storage( + pmitd::AbstractIVRPowerModelITD + ) +Constructor for Integrated T&D Optimal Power Flow in current-voltage (IV) variable space with Storage OPF Dispatch. +""" +function build_opfitd_storage(pmitd::AbstractIVRPowerModelITD) + + # Get Models + pm_model = _get_powermodel_from_powermodelitd(pmitd) + pmd_model = _get_powermodeldistribution_from_powermodelitd(pmitd) + + # PM(Transmission) Variables + _PM.variable_bus_voltage(pm_model) + _PM.variable_branch_current(pm_model) + _PM.variable_gen_current(pm_model) + _PM.variable_dcline_current(pm_model) + + # PMD(Distribution) Variables + _PMD.variable_mc_bus_voltage(pmd_model) + _PMD.variable_mc_branch_current(pmd_model) + _PMD.variable_mc_switch_current(pmd_model) + _PMD.variable_mc_transformer_current(pmd_model) + _PMD.variable_mc_generator_current(pmd_model) + _PMD.variable_mc_load_current(pmd_model) + + # PMITD (Boundary) Current Variables + variable_boundary_current(pmitd) + + # --- PM(Transmission) Constraints --- + # reference buses (this only needs to happen for pm(transmission)) + for i in _PM.ids(pm_model, :ref_buses) + _PM.constraint_theta_ref(pm_model, i) + end + + for i in _PM.ids(pm_model, :branch) + _PM.constraint_current_from(pm_model, i) + _PM.constraint_current_to(pm_model, i) + + _PM.constraint_voltage_drop(pm_model, i) + _PM.constraint_voltage_angle_difference(pm_model, i) + + _PM.constraint_thermal_limit_from(pm_model, i) + _PM.constraint_thermal_limit_to(pm_model, i) + end + + for i in _PM.ids(pm_model, :dcline) + _PM.constraint_dcline_power_losses(pm_model, i) + end + + + # --- PMD(Distribution) Constraints --- + # gens should be constrained before KCL, or Pd/Qd undefined + for id in _PMD.ids(pmd_model, :gen) + _PMD.constraint_mc_generator_power(pmd_model, id) + end + + # loads should be constrained before KCL, or Pd/Qd undefined + for id in _PMD.ids(pmd_model, :load) + _PMD.constraint_mc_load_power(pmd_model, id) + end + + + for i in _PMD.ids(pmd_model, :branch) + _PMD.constraint_mc_current_from(pmd_model, i) + _PMD.constraint_mc_current_to(pmd_model, i) + _PMD.constraint_mc_bus_voltage_drop(pmd_model, i) + _PMD.constraint_mc_voltage_angle_difference(pmd_model, i) + _PMD.constraint_mc_thermal_limit_from(pmd_model, i) + _PMD.constraint_mc_thermal_limit_to(pmd_model, i) + end + + for i in _PMD.ids(pmd_model, :switch) + _PMD.constraint_mc_switch_state(pmd_model, i) + _PMD.constraint_mc_switch_current_limit(pmd_model, i) + end + + for i in _PMD.ids(pmd_model, :transformer) + _PMD.constraint_mc_transformer_power(pmd_model, i) + end + + + # ------------------------------------------------- + # --- PMITD(T&D) INDEPENDENT Constraints ---------- + + for i in ids(pmitd, :boundary) + constraint_boundary_current(pmitd, i) + constraint_boundary_voltage_magnitude(pmitd, i) + constraint_boundary_voltage_angle(pmitd, i) + end + + # Note: Both of these need to consider flow on boundaries if bus is connected to boundary + # # ---- Transmission Power Balance --- + boundary_buses = Vector{Int}() # empty vector that stores the boundary buses, so they are not repeated by the other constraint + for i in _PM.ids(pm_model, :bus) + for j in ids(pmitd, :boundary) + constraint_transmission_current_balance_boundary(pmitd, i, j, boundary_buses) + end + if !(i in boundary_buses) + _PM.constraint_current_balance(pm_model, i) + end + end + + # # ---- Distribution Power Balance --- + boundary_buses = Vector{Int}() # empty vector that stores the boundary buses, so they are not repeated by the other constraint + for i in _PMD.ids(pmd_model, :bus) + for j in ids(pmitd, :boundary) + constraint_distribution_current_balance_boundary(pmitd, i, j, boundary_buses) + end + if !(i in boundary_buses) + _PMD.constraint_mc_current_balance(pmd_model, i) + end + end + + # ------------------------------------------------- + # --- PMITD(T&D) Cost Functions ------------------- + objective_itd_min_fuel_cost(pmitd) + +end + + +""" + function build_opfitd_storage( + pmitd::AbstractBFPowerModelITD + ) +Constructor for Integrated T&D Optimal Power Flow for BF Models with Storage OPF Dispatch. +""" +function build_opfitd_storage(pmitd::AbstractBFPowerModelITD) + + # Get Models + pm_model = _get_powermodel_from_powermodelitd(pmitd) + pmd_model = _get_powermodeldistribution_from_powermodelitd(pmitd) + + # PM(Transmission) Variables + _PM.variable_bus_voltage(pm_model) + _PM.variable_gen_power(pm_model) + _PM.variable_branch_power(pm_model) + _PM.variable_branch_current(pm_model) + _PM.variable_dcline_power(pm_model) + + + # PMD(Distribution) Variables + _PMD.variable_mc_bus_voltage(pmd_model) + _PMD.variable_mc_branch_current(pmd_model) + _PMD.variable_mc_branch_power(pmd_model) + _PMD.variable_mc_transformer_power(pmd_model) + _PMD.variable_mc_switch_power(pmd_model) + _PMD.variable_mc_generator_power(pmd_model) + _PMD.variable_mc_load_power(pmd_model) + _PMD.variable_mc_storage_power(pmd_model) + + # PMITD (Boundary) Variables + variable_boundary_power(pmitd) + + # --- PM(Transmission) Constraints --- + _PM.constraint_model_current(pm_model) + + # reference buses (this only needs to happen for pm(transmission)) + for i in _PM.ids(pm_model, :ref_buses) + _PM.constraint_theta_ref(pm_model, i) + end + + + # PM branches + for i in _PM.ids(pm_model, :branch) + _PM.constraint_power_losses(pm_model, i) + _PM.constraint_voltage_magnitude_difference(pm_model, i) + + _PM.constraint_voltage_angle_difference(pm_model, i) + + _PM.constraint_thermal_limit_from(pm_model, i) + _PM.constraint_thermal_limit_to(pm_model, i) + end + + # PM DC lines + for i in _PM.ids(pm_model, :dcline) + _PM.constraint_dcline_power_losses(pm_model, i) + end + + # ------------------------------------------------- + # --- PMD(Distribution) Constraints --- + _PMD.constraint_mc_model_current(pmd_model) + + # generators should be constrained before KCL, or Pd/Qd undefined + for id in _PMD.ids(pmd_model, :gen) + _PMD.constraint_mc_generator_power(pmd_model, id) + end + + # loads should be constrained before KCL, or Pd/Qd undefined + for id in _PMD.ids(pmd_model, :load) + _PMD.constraint_mc_load_power(pmd_model, id) + end + + for i in _PMD.ids(pmd_model, :storage) + _PMD.constraint_storage_state(pmd_model, i) + _PMD.constraint_storage_complementarity_nl(pmd_model, i) + _PMD.constraint_mc_storage_losses(pmd_model, i) + _PMD.constraint_mc_storage_thermal_limit(pmd_model, i) + end + + for i in _PMD.ids(pmd_model, :branch) + _PMD.constraint_mc_power_losses(pmd_model, i) + _PMD.constraint_mc_model_voltage_magnitude_difference(pmd_model, i) + + _PMD.constraint_mc_voltage_angle_difference(pmd_model, i) + + _PMD.constraint_mc_thermal_limit_from(pmd_model, i) + _PMD.constraint_mc_thermal_limit_to(pmd_model, i) + _PMD.constraint_mc_ampacity_from(pmd_model, i) + _PMD.constraint_mc_ampacity_to(pmd_model, i) + end + + for i in _PMD.ids(pmd_model, :switch) + _PMD.constraint_mc_switch_state(pmd_model, i) + _PMD.constraint_mc_switch_thermal_limit(pmd_model, i) + _PMD.constraint_mc_switch_ampacity(pmd_model, i) + end + + + for i in _PMD.ids(pmd_model, :transformer) + _PMD.constraint_mc_transformer_power(pmd_model, i) + end + + + # ------------------------------------------------- + # --- PMITD(T&D) INDEPENDENT Constraints ---------- + + for i in ids(pmitd, :boundary) + constraint_boundary_power(pmitd, i) + constraint_boundary_voltage_magnitude(pmitd, i) + constraint_boundary_voltage_angle(pmitd, i) + end + + # Note: Both of these need to consider flow on boundaries if bus is connected to boundary + # # ---- Transmission Power Balance --- + boundary_buses = Vector{Int}() # empty vector that stores the boundary buses, so they are not repeated by the other constraint + for i in _PM.ids(pm_model, :bus) + for j in ids(pmitd, :boundary) + constraint_transmission_power_balance_boundary(pmitd, i, j, boundary_buses) + end + if !(i in boundary_buses) + _PM.constraint_power_balance(pm_model, i) + end + end + + # # ---- Distribution Power Balance --- + boundary_buses = Vector{Int}() # empty vector that stores the boundary buses, so they are not repeated by the other constraint + for i in _PMD.ids(pmd_model, :bus) + for j in ids(pmitd, :boundary) + constraint_distribution_power_balance_boundary(pmitd, i, j, boundary_buses) + end + if !(i in boundary_buses) + _PMD.constraint_mc_power_balance(pmd_model, i) + end + end + + # ------------------------------------------------- + # --- PMITD(T&D) Cost Functions ------------------- + objective_itd_min_fuel_cost(pmitd) + +end + + +# -- Combined (Hybrid) Formulations +""" + function build_opfitd_storage( + pmitd::AbstractLNLBFPowerModelITD + ) +Constructor for Integrated T&D Optimal Power Flow for L/NL to BF with Storage OPF Dispatch. +""" +function build_opfitd_storage(pmitd::AbstractLNLBFPowerModelITD) + + # Get Models + pm_model = _get_powermodel_from_powermodelitd(pmitd) + pmd_model = _get_powermodeldistribution_from_powermodelitd(pmitd) + + # PM(Transmission) Variables + _PM.variable_bus_voltage(pm_model) + _PM.variable_gen_power(pm_model) + _PM.variable_branch_power(pm_model) + _PM.variable_dcline_power(pm_model) + + # PMD(Distribution) Variables + _PMD.variable_mc_bus_voltage(pmd_model) + _PMD.variable_mc_branch_current(pmd_model) + _PMD.variable_mc_branch_power(pmd_model) + _PMD.variable_mc_transformer_power(pmd_model) + _PMD.variable_mc_switch_power(pmd_model) + _PMD.variable_mc_generator_power(pmd_model) + _PMD.variable_mc_load_power(pmd_model) + _PMD.variable_mc_storage_power(pmd_model) + + # PMITD (Boundary) Variables + variable_boundary_power(pmitd) + + # --- PM(Transmission) Constraints --- + _PM.constraint_model_voltage(pm_model) + + # reference buses (this only needs to happen for pm(transmission)) + for i in _PM.ids(pm_model, :ref_buses) + _PM.constraint_theta_ref(pm_model, i) + end + + + # PM branches + for i in _PM.ids(pm_model, :branch) + _PM.constraint_ohms_yt_from(pm_model, i) + _PM.constraint_ohms_yt_to(pm_model, i) + + _PM.constraint_voltage_angle_difference(pm_model, i) + + _PM.constraint_thermal_limit_from(pm_model, i) + _PM.constraint_thermal_limit_to(pm_model, i) + end + + # PM DC lines + for i in _PM.ids(pm_model, :dcline) + _PM.constraint_dcline_power_losses(pm_model, i) + end + + # ------------------------------------------------- + # --- PMD(Distribution) Constraints --- + _PMD.constraint_mc_model_current(pmd_model) + + # generators should be constrained before KCL, or Pd/Qd undefined + for id in _PMD.ids(pmd_model, :gen) + _PMD.constraint_mc_generator_power(pmd_model, id) + end + + # loads should be constrained before KCL, or Pd/Qd undefined + for id in _PMD.ids(pmd_model, :load) + _PMD.constraint_mc_load_power(pmd_model, id) + end + + for i in _PMD.ids(pmd_model, :storage) + _PMD.constraint_storage_state(pmd_model, i) + _PMD.constraint_storage_complementarity_nl(pmd_model, i) + _PMD.constraint_mc_storage_losses(pmd_model, i) + _PMD.constraint_mc_storage_thermal_limit(pmd_model, i) + end + + for i in _PMD.ids(pmd_model, :branch) + _PMD.constraint_mc_power_losses(pmd_model, i) + _PMD.constraint_mc_model_voltage_magnitude_difference(pmd_model, i) + + _PMD.constraint_mc_voltage_angle_difference(pmd_model, i) + + _PMD.constraint_mc_thermal_limit_from(pmd_model, i) + _PMD.constraint_mc_thermal_limit_to(pmd_model, i) + _PMD.constraint_mc_ampacity_from(pmd_model, i) + _PMD.constraint_mc_ampacity_to(pmd_model, i) + end + + for i in _PMD.ids(pmd_model, :switch) + _PMD.constraint_mc_switch_state(pmd_model, i) + _PMD.constraint_mc_switch_thermal_limit(pmd_model, i) + _PMD.constraint_mc_switch_ampacity(pmd_model, i) + end + + for i in _PMD.ids(pmd_model, :transformer) + _PMD.constraint_mc_transformer_power(pmd_model, i) + end + + # ------------------------------------------------- + # --- PMITD(T&D) INDEPENDENT Constraints ---------- + + for i in ids(pmitd, :boundary) + constraint_boundary_power(pmitd, i) + constraint_boundary_voltage_magnitude(pmitd, i) + constraint_boundary_voltage_angle(pmitd, i) + end + + # Note: Both of these need to consider flow on boundaries if bus is connected to boundary + # # ---- Transmission Power Balance --- + boundary_buses = Vector{Int}() # empty vector that stores the boundary buses, so they are not repeated by the other constraint + for i in _PM.ids(pm_model, :bus) + for j in ids(pmitd, :boundary) + constraint_transmission_power_balance_boundary(pmitd, i, j, boundary_buses) + end + if !(i in boundary_buses) + _PM.constraint_power_balance(pm_model, i) + end + end + + # # ---- Distribution Power Balance --- + boundary_buses = Vector{Int}() # empty vector that stores the boundary buses, so they are not repeated by the other constraint + for i in _PMD.ids(pmd_model, :bus) + for j in ids(pmitd, :boundary) + constraint_distribution_power_balance_boundary(pmitd, i, j, boundary_buses) + end + if !(i in boundary_buses) + _PMD.constraint_mc_power_balance(pmd_model, i) + end + end + + # ------------------------------------------------- + # --- PMITD(T&D) Cost Functions ------------------- + objective_itd_min_fuel_cost(pmitd) + +end + + +# ---------------------------------------------------------------------------------------- +# --- Multinetwork OPFITD Problem Specifications +# ---------------------------------------------------------------------------------------- + +""" + function build_mn_opfitd_storage( + pmitd::AbstractPowerModelITD + ) +Constructor for Multinetwork Integrated T&D Optimal Power Flow with Storage OPF Dispatch. +""" +function build_mn_opfitd_storage(pmitd::AbstractPowerModelITD) + + # Get Models + pm_model = _get_powermodel_from_powermodelitd(pmitd) + pmd_model = _get_powermodeldistribution_from_powermodelitd(pmitd) + + for (n, network) in nws(pmitd) + # PM(Transmission) Variables + _PM.variable_bus_voltage(pm_model, nw=n) + _PM.variable_gen_power(pm_model, nw=n) + _PM.variable_branch_power(pm_model, nw=n) + _PM.variable_dcline_power(pm_model, nw=n) + _PM.variable_storage_power_mi(pm_model, nw=n) + + # PMD(Distribution) Variables + _PMD.variable_mc_bus_voltage(pmd_model; nw=n) + _PMD.variable_mc_branch_power(pmd_model; nw=n) + _PMD.variable_mc_transformer_power(pmd_model; nw=n) + _PMD.variable_mc_switch_power(pmd_model; nw=n) + _PMD.variable_mc_generator_power(pmd_model; nw=n) + _PMD.variable_mc_load_power(pmd_model; nw=n) + _PMD.variable_mc_storage_power(pmd_model; nw=n) + + # PMITD (Boundary) Variables + variable_boundary_power(pmitd; nw=n) + + # --- PM(Transmission) Constraints --- + _PM.constraint_model_voltage(pm_model, nw=n) + + # reference buses (this only needs to happen for pm(transmission)) + for i in _PM.ids(pm_model, :ref_buses, nw=n) + _PM.constraint_theta_ref(pm_model, i, nw=n) + end + + for i in _PM.ids(pm_model, :storage, nw=n) + _PM.constraint_storage_complementarity_mi(pm_model, i, nw=n) + _PM.constraint_storage_losses(pm_model, i, nw=n) + _PM.constraint_storage_thermal_limit(pm_model, i, nw=n) + end + + # PM branches + for i in _PM.ids(pm_model, :branch, nw=n) + _PM.constraint_ohms_yt_from(pm_model, i, nw=n) + _PM.constraint_ohms_yt_to(pm_model, i, nw=n) + + _PM.constraint_voltage_angle_difference(pm_model, i, nw=n) + + _PM.constraint_thermal_limit_from(pm_model, i, nw=n) + _PM.constraint_thermal_limit_to(pm_model, i, nw=n) + end + + # PM DC lines + for i in _PM.ids(pm_model, :dcline, nw=n) + _PM.constraint_dcline_power_losses(pm_model, i, nw=n) + end + + # ------------------------------------------------- + # --- PMD(Distribution) Constraints --- + _PMD.constraint_mc_model_voltage(pmd_model; nw=n) + + # generators should be constrained before KCL, or Pd/Qd undefined + for i in _PMD.ids(pmd_model, n, :gen) + _PMD.constraint_mc_generator_power(pmd_model, i; nw=n) + end + + # loads should be constrained before KCL, or Pd/Qd undefined + for i in _PMD.ids(pmd_model, n, :load) + _PMD.constraint_mc_load_power(pmd_model, i; nw=n) + end + + for i in _PMD.ids(pmd_model, n, :storage) + _PMD.constraint_storage_complementarity_nl(pmd_model, i; nw=n) + _PMD.constraint_mc_storage_losses(pmd_model, i; nw=n) + _PMD.constraint_mc_storage_thermal_limit(pmd_model, i; nw=n) + end + + for i in _PMD.ids(pmd_model, n, :branch) + _PMD.constraint_mc_ohms_yt_from(pmd_model, i; nw=n) + _PMD.constraint_mc_ohms_yt_to(pmd_model, i; nw=n) + + _PMD.constraint_mc_voltage_angle_difference(pmd_model, i; nw=n) + + _PMD.constraint_mc_thermal_limit_from(pmd_model, i; nw=n) + _PMD.constraint_mc_thermal_limit_to(pmd_model, i; nw=n) + _PMD.constraint_mc_ampacity_from(pmd_model, i; nw=n) + _PMD.constraint_mc_ampacity_to(pmd_model, i; nw=n) + end + + for i in _PMD.ids(pmd_model, n, :switch) + _PMD.constraint_mc_switch_state(pmd_model, i; nw=n) + _PMD.constraint_mc_switch_thermal_limit(pmd_model, i; nw=n) + _PMD.constraint_mc_switch_ampacity(pmd_model, i; nw=n) + end + + for i in _PMD.ids(pmd_model, n, :transformer) + _PMD.constraint_mc_transformer_power(pmd_model, i; nw=n) + end + + # ------------------------------------------------- + # --- PMITD(T&D) INDEPENDENT Constraints ---------- + + for i in ids(pmitd, :boundary; nw=n) + constraint_boundary_power(pmitd, i; nw=n) + constraint_boundary_voltage_magnitude(pmitd, i; nw=n) + constraint_boundary_voltage_angle(pmitd, i; nw=n) + end + + # Note: Both of these need to consider flow on boundaries if bus is connected to boundary + # # ---- Transmission Power Balance --- + boundary_buses = Vector{Int}() # empty vector that stores the boundary buses, so they are not repeated by the other constraint + for i in _PM.ids(pm_model, :bus, nw=n) + for j in ids(pmitd, :boundary; nw=n) + constraint_transmission_power_balance_boundary(pmitd, i, j, boundary_buses; nw_pmitd=n) + end + if !(i in boundary_buses) + _PM.constraint_power_balance(pm_model, i, nw=n) + end + end + + # # ---- Distribution Power Balance --- + boundary_buses = Vector{Int}() # empty vector that stores the boundary buses, so they are not repeated by the other constraint + for i in _PMD.ids(pmd_model, n, :bus) + for j in ids(pmitd, :boundary; nw=n) + constraint_distribution_power_balance_boundary(pmitd, i, j, boundary_buses; nw_pmitd=n) + end + if !(i in boundary_buses) + _PMD.constraint_mc_power_balance(pmd_model, i; nw=n) + end + end + + + end + + # --- PM energy storage state constraint --- + network_ids_pm = sort(collect(_PM.nw_ids(pm_model))) + + n_1_pm = network_ids_pm[1] + for i in _PM.ids(pm_model, :storage, nw=n_1_pm) + _PM.constraint_storage_state(pm_model, i, nw=n_1_pm) + end + + for n_2_pm in network_ids_pm[2:end] + for i in _PM.ids(pm_model, :storage, nw=n_2_pm) + _PM.constraint_storage_state(pm_model, i, n_1_pm, n_2_pm) + end + n_1_pm = n_2_pm + end + + # --- PMD energy storage state constraint --- + network_ids_pmd = sort(collect(_PMD.nw_ids(pmd_model))) + + n_1_pmd = network_ids_pmd[1] + + for i in _PMD.ids(pmd_model, :storage; nw=n_1_pmd) + _PMD.constraint_storage_state(pmd_model, i; nw=n_1_pmd) + end + + for n_2_pmd in network_ids_pmd[2:end] + for i in _PMD.ids(pmd_model, :storage; nw=n_2_pmd) + _PMD.constraint_storage_state(pmd_model, i, n_1_pmd, n_2_pmd) + end + + n_1_pmd = n_2_pmd + end + + # ------------------------------------------------- + # --- PMITD(T&D) Cost Functions ------------------- + objective_itd_min_fuel_cost(pmitd) + +end + + +""" + function build_mn_opfitd_storage( + pmitd::AbstractIVRPowerModelITD + ) +Constructor for Multinetwork Integrated T&D Optimal Power Flow in current-voltage (IV) variable space with Storage OPF Dispatch. +""" +function build_mn_opfitd_storage(pmitd::AbstractIVRPowerModelITD) + + # Get Models + pm_model = _get_powermodel_from_powermodelitd(pmitd) + pmd_model = _get_powermodeldistribution_from_powermodelitd(pmitd) + + for (n, network) in nws(pmitd) + # PM(Transmission) Variables + _PM.variable_bus_voltage(pm_model, nw=n) + _PM.variable_branch_current(pm_model, nw=n) + _PM.variable_gen_current(pm_model, nw=n) + _PM.variable_dcline_current(pm_model, nw=n) + _PM.variable_storage_power_mi(pm_model, nw=n) + + # PMD(Distribution) Variables + _PMD.variable_mc_bus_voltage(pmd_model; nw=n) + _PMD.variable_mc_branch_current(pmd_model; nw=n) + _PMD.variable_mc_switch_current(pmd_model; nw=n) + _PMD.variable_mc_transformer_current(pmd_model; nw=n) + _PMD.variable_mc_generator_current(pmd_model; nw=n) + _PMD.variable_mc_load_current(pmd_model; nw=n) + _PMD.variable_mc_storage_power(pmd_model; nw=n) + + # PMITD (Boundary) Current Variables + variable_boundary_current(pmitd; nw=n) + + # reference buses (this only needs to happen for pm(transmission)) + for i in _PM.ids(pm_model, :ref_buses, nw=n) + _PM.constraint_theta_ref(pm_model, i, nw=n) + end + + for i in _PM.ids(pm_model, :storage, nw=n) + _PM.constraint_storage_complementarity_mi(pm_model, i, nw=n) + _PM.constraint_storage_losses(pm_model, i, nw=n) + _PM.constraint_storage_thermal_limit(pm_model, i, nw=n) + end + + # PM branches + for i in _PM.ids(pm_model, :branch, nw=n) + _PM.constraint_current_from(pm_model, i, nw=n) + _PM.constraint_current_to(pm_model, i, nw=n) + + _PM.constraint_voltage_drop(pm_model, i, nw=n) + _PM.constraint_voltage_angle_difference(pm_model, i, nw=n) + + _PM.constraint_thermal_limit_from(pm_model, i, nw=n) + _PM.constraint_thermal_limit_to(pm_model, i, nw=n) + end + + # PM DC lines + for i in _PM.ids(pm_model, :dcline, nw=n) + _PM.constraint_dcline_power_losses(pm_model, i, nw=n) + end + + # ------------------------------------------------- + # --- PMD(Distribution) Constraints --- + + # generators should be constrained before KCL, or Pd/Qd undefined + for i in _PMD.ids(pmd_model, n, :gen) + _PMD.constraint_mc_generator_power(pmd_model, i; nw=n) + end + + # loads should be constrained before KCL, or Pd/Qd undefined + for i in _PMD.ids(pmd_model, n, :load) + _PMD.constraint_mc_load_power(pmd_model, i; nw=n) + end + + for i in _PMD.ids(pmd_model, n, :storage) + _PMD.constraint_storage_complementarity_nl(pmd_model, i; nw=n) + _PMD.constraint_mc_storage_losses(pmd_model, i; nw=n) + _PMD.constraint_mc_storage_thermal_limit(pmd_model, i; nw=n) + end + + for i in _PMD.ids(pmd_model, n, :branch) + _PMD.constraint_mc_current_from(pmd_model, i; nw=n) + _PMD.constraint_mc_current_to(pmd_model, i; nw=n) + _PMD.constraint_mc_bus_voltage_drop(pmd_model, i; nw=n) + _PMD.constraint_mc_voltage_angle_difference(pmd_model, i; nw=n) + _PMD.constraint_mc_thermal_limit_from(pmd_model, i; nw=n) + _PMD.constraint_mc_thermal_limit_to(pmd_model, i; nw=n) + end + + for i in _PMD.ids(pmd_model, n, :switch) + _PMD.constraint_mc_switch_state(pmd_model, i; nw=n) + _PMD.constraint_mc_switch_current_limit(pmd_model, i; nw=n) + end + + for i in _PMD.ids(pmd_model, n, :transformer) + _PMD.constraint_mc_transformer_power(pmd_model, i; nw=n) + end + + # ------------------------------------------------- + # --- PMITD(T&D) INDEPENDENT Constraints ---------- + + for i in ids(pmitd, :boundary; nw=n) + constraint_boundary_current(pmitd, i; nw=n) + constraint_boundary_voltage_magnitude(pmitd, i; nw=n) + constraint_boundary_voltage_angle(pmitd, i; nw=n) + end + + # Note: Both of these need to consider flow on boundaries if bus is connected to boundary + # # ---- Transmission Power Balance --- + boundary_buses = Vector{Int}() # empty vector that stores the boundary buses, so they are not repeated by the other constraint + for i in _PM.ids(pm_model, :bus, nw=n) + for j in ids(pmitd, :boundary; nw=n) + constraint_transmission_current_balance_boundary(pmitd, i, j, boundary_buses; nw_pmitd=n) + end + if !(i in boundary_buses) + _PM.constraint_current_balance(pm_model, i, nw=n) + end + end + + # # ---- Distribution Power Balance --- + boundary_buses = Vector{Int}() # empty vector that stores the boundary buses, so they are not repeated by the other constraint + for i in _PMD.ids(pmd_model, n, :bus) + for j in ids(pmitd, :boundary; nw=n) + constraint_distribution_current_balance_boundary(pmitd, i, j, boundary_buses; nw_pmitd=n) + end + if !(i in boundary_buses) + _PMD.constraint_mc_current_balance(pmd_model, i; nw=n) + end + end + end + + # --- PM energy storage state constraint --- + network_ids_pm = sort(collect(_PM.nw_ids(pm_model))) + + n_1_pm = network_ids_pm[1] + for i in _PM.ids(pm_model, :storage, nw=n_1_pm) + _PM.constraint_storage_state(pm_model, i, nw=n_1_pm) + end + + for n_2_pm in network_ids_pm[2:end] + for i in _PM.ids(pm_model, :storage, nw=n_2_pm) + _PM.constraint_storage_state(pm_model, i, n_1_pm, n_2_pm) + end + n_1_pm = n_2_pm + end + + # --- PMD energy storage state constraint --- + network_ids_pmd = sort(collect(_PMD.nw_ids(pmd_model))) + + n_1_pmd = network_ids_pmd[1] + + for i in _PMD.ids(pmd_model, :storage; nw=n_1_pmd) + _PMD.constraint_storage_state(pmd_model, i; nw=n_1_pmd) + end + + for n_2_pmd in network_ids_pmd[2:end] + for i in _PMD.ids(pmd_model, :storage; nw=n_2_pmd) + _PMD.constraint_storage_state(pmd_model, i, n_1_pmd, n_2_pmd) + end + + n_1_pmd = n_2_pmd + end + + # ------------------------------------------------- + # --- PMITD(T&D) Cost Functions ------------------- + objective_itd_min_fuel_cost(pmitd) + +end + + +""" + function build_mn_opfitd_storage( + pmitd::AbstractBFPowerModelITD + ) +Constructor for Multinetwork Integrated T&D Optimal Power Flow for BF Models with Storage OPF Dispatch. +""" +function build_mn_opfitd_storage(pmitd::AbstractBFPowerModelITD) + + # Get Models + pm_model = _get_powermodel_from_powermodelitd(pmitd) + pmd_model = _get_powermodeldistribution_from_powermodelitd(pmitd) + + for (n, network) in nws(pmitd) + # PM(Transmission) Variables + _PM.variable_bus_voltage(pm_model, nw=n) + _PM.variable_gen_power(pm_model, nw=n) + _PM.variable_branch_power(pm_model, nw=n) + _PM.variable_branch_current(pm_model, nw=n) + _PM.variable_dcline_power(pm_model, nw=n) + _PM.variable_storage_power_mi(pm_model, nw=n) + + # PMD(Distribution) Variables + _PMD.variable_mc_bus_voltage(pmd_model; nw=n) + _PMD.variable_mc_branch_current(pmd_model; nw=n) + _PMD.variable_mc_branch_power(pmd_model; nw=n) + _PMD.variable_mc_switch_power(pmd_model; nw=n) + _PMD.variable_mc_transformer_power(pmd_model; nw=n) + _PMD.variable_mc_generator_power(pmd_model; nw=n) + _PMD.variable_mc_load_power(pmd_model; nw=n) + _PMD.variable_mc_storage_power(pmd_model; nw=n) + + # PMITD (Boundary) Current Variables + variable_boundary_power(pmitd; nw=n) + + # --- PM(Transmission) Constraints --- + _PM.constraint_model_current(pm_model; nw=n) + + # reference buses (this only needs to happen for pm(transmission)) + for i in _PM.ids(pm_model, :ref_buses, nw=n) + _PM.constraint_theta_ref(pm_model, i, nw=n) + end + + for i in _PM.ids(pm_model, :storage, nw=n) + _PM.constraint_storage_complementarity_mi(pm_model, i, nw=n) + _PM.constraint_storage_losses(pm_model, i, nw=n) + _PM.constraint_storage_thermal_limit(pm_model, i, nw=n) + end + + # PM branches + for i in _PM.ids(pm_model, :branch, nw=n) + _PM.constraint_power_losses(pm_model, i, nw=n) + _PM.constraint_voltage_magnitude_difference(pm_model, i, nw=n) + + _PM.constraint_voltage_angle_difference(pm_model, i, nw=n) + + _PM.constraint_thermal_limit_from(pm_model, i, nw=n) + _PM.constraint_thermal_limit_to(pm_model, i, nw=n) + end + + # PM DC lines + for i in _PM.ids(pm_model, :dcline, nw=n) + _PM.constraint_dcline_power_losses(pm_model, i, nw=n) + end + + # ------------------------------------------------- + # --- PMD(Distribution) Constraints --- + _PMD.constraint_mc_model_current(pmd_model; nw=n) + + # generators should be constrained before KCL, or Pd/Qd undefined + for i in _PMD.ids(pmd_model, n, :gen) + _PMD.constraint_mc_generator_power(pmd_model, i; nw=n) + end + + # loads should be constrained before KCL, or Pd/Qd undefined + for i in _PMD.ids(pmd_model, n, :load) + _PMD.constraint_mc_load_power(pmd_model, i; nw=n) + end + + for i in _PMD.ids(pmd_model, n, :storage) + _PMD.constraint_storage_complementarity_nl(pmd_model, i; nw=n) + _PMD.constraint_mc_storage_losses(pmd_model, i; nw=n) + _PMD.constraint_mc_storage_thermal_limit(pmd_model, i; nw=n) + end + + for i in _PMD.ids(pmd_model, n, :branch) + _PMD.constraint_mc_power_losses(pmd_model, i; nw=n) + _PMD.constraint_mc_model_voltage_magnitude_difference(pmd_model, i; nw=n) + + _PMD.constraint_mc_voltage_angle_difference(pmd_model, i; nw=n) + + _PMD.constraint_mc_thermal_limit_from(pmd_model, i; nw=n) + _PMD.constraint_mc_thermal_limit_to(pmd_model, i; nw=n) + _PMD.constraint_mc_ampacity_from(pmd_model, i; nw=n) + _PMD.constraint_mc_ampacity_to(pmd_model, i; nw=n) + end + + for i in _PMD.ids(pmd_model, n, :switch) + _PMD.constraint_mc_switch_state(pmd_model, i; nw=n) + _PMD.constraint_mc_switch_thermal_limit(pmd_model, i; nw=n) + _PMD.constraint_mc_switch_ampacity(pmd_model, i; nw=n) + end + + for i in _PMD.ids(pmd_model, n, :transformer) + _PMD.constraint_mc_transformer_power(pmd_model, i; nw=n) + end + + # ------------------------------------------------- + # --- PMITD(T&D) INDEPENDENT Constraints ---------- + + for i in ids(pmitd, :boundary; nw=n) + constraint_boundary_power(pmitd, i; nw=n) + constraint_boundary_voltage_magnitude(pmitd, i; nw=n) + constraint_boundary_voltage_angle(pmitd, i; nw=n) + end + + # Note: Both of these need to consider flow on boundaries if bus is connected to boundary + # # ---- Transmission Power Balance --- + boundary_buses = Vector{Int}() # empty vector that stores the boundary buses, so they are not repeated by the other constraint + for i in _PM.ids(pm_model, :bus, nw=n) + for j in ids(pmitd, :boundary; nw=n) + constraint_transmission_power_balance_boundary(pmitd, i, j, boundary_buses; nw_pmitd=n) + end + if !(i in boundary_buses) + _PM.constraint_power_balance(pm_model, i, nw=n) + end + end + + # # ---- Distribution Power Balance --- + boundary_buses = Vector{Int}() # empty vector that stores the boundary buses, so they are not repeated by the other constraint + for i in _PMD.ids(pmd_model, n, :bus) + for j in ids(pmitd, :boundary; nw=n) + constraint_distribution_power_balance_boundary(pmitd, i, j, boundary_buses; nw_pmitd=n) + end + if !(i in boundary_buses) + _PMD.constraint_mc_power_balance(pmd_model, i; nw=n) + end + end + end + + # --- PM energy storage state constraint --- + network_ids_pm = sort(collect(_PM.nw_ids(pm_model))) + + n_1_pm = network_ids_pm[1] + for i in _PM.ids(pm_model, :storage, nw=n_1_pm) + _PM.constraint_storage_state(pm_model, i, nw=n_1_pm) + end + + for n_2_pm in network_ids_pm[2:end] + for i in _PM.ids(pm_model, :storage, nw=n_2_pm) + _PM.constraint_storage_state(pm_model, i, n_1_pm, n_2_pm) + end + n_1_pm = n_2_pm + end + + # --- PMD energy storage state constraint --- + network_ids_pmd = sort(collect(_PMD.nw_ids(pmd_model))) + + n_1_pmd = network_ids_pmd[1] + + for i in _PMD.ids(pmd_model, :storage; nw=n_1_pmd) + _PMD.constraint_storage_state(pmd_model, i; nw=n_1_pmd) + end + + for n_2_pmd in network_ids_pmd[2:end] + for i in _PMD.ids(pmd_model, :storage; nw=n_2_pmd) + _PMD.constraint_storage_state(pmd_model, i, n_1_pmd, n_2_pmd) + end + + n_1_pmd = n_2_pmd + end + + # ------------------------------------------------- + # --- PMITD(T&D) Cost Functions ------------------- + objective_itd_min_fuel_cost(pmitd) + +end + + +""" + function build_mn_opfitd_storage( + pmitd::AbstractLNLBFPowerModelITD + ) +Constructor for Multinetwork Integrated T&D Optimal Power Flow for L/NL to BF with Storage OPF Dispatch. +""" +function build_mn_opfitd_storage(pmitd::AbstractLNLBFPowerModelITD) + + # Get Models + pm_model = _get_powermodel_from_powermodelitd(pmitd) + pmd_model = _get_powermodeldistribution_from_powermodelitd(pmitd) + + for (n, network) in nws(pmitd) + # PM(Transmission) Variables + _PM.variable_bus_voltage(pm_model, nw=n) + _PM.variable_gen_power(pm_model, nw=n) + _PM.variable_branch_power(pm_model, nw=n) + _PM.variable_dcline_power(pm_model, nw=n) + _PM.variable_storage_power_mi(pm_model, nw=n) + + # PMD(Distribution) Variables + _PMD.variable_mc_bus_voltage(pmd_model; nw=n) + _PMD.variable_mc_branch_current(pmd_model; nw=n) + _PMD.variable_mc_branch_power(pmd_model; nw=n) + _PMD.variable_mc_transformer_power(pmd_model; nw=n) + _PMD.variable_mc_switch_power(pmd_model; nw=n) + _PMD.variable_mc_generator_power(pmd_model; nw=n) + _PMD.variable_mc_load_power(pmd_model; nw=n) + _PMD.variable_mc_storage_power(pmd_model; nw=n) + + # PMITD (Boundary) Variables + variable_boundary_power(pmitd; nw=n) + + # --- PM(Transmission) Constraints --- + _PM.constraint_model_voltage(pm_model, nw=n) + + # reference buses (this only needs to happen for pm(transmission)) + for i in _PM.ids(pm_model, :ref_buses, nw=n) + _PM.constraint_theta_ref(pm_model, i, nw=n) + end + + for i in _PM.ids(pm_model, :storage, nw=n) + _PM.constraint_storage_complementarity_mi(pm_model, i, nw=n) + _PM.constraint_storage_losses(pm_model, i, nw=n) + _PM.constraint_storage_thermal_limit(pm_model, i, nw=n) + end + + # PM branches + for i in _PM.ids(pm_model, :branch, nw=n) + _PM.constraint_ohms_yt_from(pm_model, i, nw=n) + _PM.constraint_ohms_yt_to(pm_model, i, nw=n) + + _PM.constraint_voltage_angle_difference(pm_model, i, nw=n) + + _PM.constraint_thermal_limit_from(pm_model, i, nw=n) + _PM.constraint_thermal_limit_to(pm_model, i, nw=n) + end + + # PM DC lines + for i in _PM.ids(pm_model, :dcline, nw=n) + _PM.constraint_dcline_power_losses(pm_model, i, nw=n) + end + + # ------------------------------------------------- + # --- PMD(Distribution) Constraints --- + _PMD.constraint_mc_model_current(pmd_model; nw=n) + + # generators should be constrained before KCL, or Pd/Qd undefined + for i in _PMD.ids(pmd_model, n, :gen) + _PMD.constraint_mc_generator_power(pmd_model, i; nw=n) + end + + # loads should be constrained before KCL, or Pd/Qd undefined + for i in _PMD.ids(pmd_model, n, :load) + _PMD.constraint_mc_load_power(pmd_model, i; nw=n) + end + + for i in _PMD.ids(pmd_model, n, :storage) + _PMD.constraint_storage_complementarity_nl(pmd_model, i; nw=n) + _PMD.constraint_mc_storage_losses(pmd_model, i; nw=n) + _PMD.constraint_mc_storage_thermal_limit(pmd_model, i; nw=n) + end + + for i in _PMD.ids(pmd_model, n, :branch) + _PMD.constraint_mc_power_losses(pmd_model, i; nw=n) + _PMD.constraint_mc_model_voltage_magnitude_difference(pmd_model, i; nw=n) + + _PMD.constraint_mc_voltage_angle_difference(pmd_model, i; nw=n) + + _PMD.constraint_mc_thermal_limit_from(pmd_model, i; nw=n) + _PMD.constraint_mc_thermal_limit_to(pmd_model, i; nw=n) + _PMD.constraint_mc_ampacity_from(pmd_model, i; nw=n) + _PMD.constraint_mc_ampacity_to(pmd_model, i; nw=n) + end + + for i in _PMD.ids(pmd_model, n, :switch) + _PMD.constraint_mc_switch_state(pmd_model, i; nw=n) + _PMD.constraint_mc_switch_thermal_limit(pmd_model, i; nw=n) + _PMD.constraint_mc_switch_ampacity(pmd_model, i; nw=n) + end + + for i in _PMD.ids(pmd_model, n, :transformer) + _PMD.constraint_mc_transformer_power(pmd_model, i; nw=n) + end + + # ------------------------------------------------- + # --- PMITD(T&D) INDEPENDENT Constraints ---------- + + for i in ids(pmitd, :boundary; nw=n) + constraint_boundary_power(pmitd, i; nw=n) + constraint_boundary_voltage_magnitude(pmitd, i; nw=n) + constraint_boundary_voltage_angle(pmitd, i; nw=n) + end + + # Note: Both of these need to consider flow on boundaries if bus is connected to boundary + # # ---- Transmission Power Balance --- + boundary_buses = Vector{Int}() # empty vector that stores the boundary buses, so they are not repeated by the other constraint + for i in _PM.ids(pm_model, :bus, nw=n) + for j in ids(pmitd, :boundary; nw=n) + constraint_transmission_power_balance_boundary(pmitd, i, j, boundary_buses; nw_pmitd=n) + end + if !(i in boundary_buses) + _PM.constraint_power_balance(pm_model, i, nw=n) + end + end + + # # ---- Distribution Power Balance --- + boundary_buses = Vector{Int}() # empty vector that stores the boundary buses, so they are not repeated by the other constraint + for i in _PMD.ids(pmd_model, n, :bus) + for j in ids(pmitd, :boundary; nw=n) + constraint_distribution_power_balance_boundary(pmitd, i, j, boundary_buses; nw_pmitd=n) + end + if !(i in boundary_buses) + _PMD.constraint_mc_power_balance(pmd_model, i; nw=n) + end + end + end + + # --- PM energy storage state constraint --- + network_ids_pm = sort(collect(_PM.nw_ids(pm_model))) + + n_1_pm = network_ids_pm[1] + for i in _PM.ids(pm_model, :storage, nw=n_1_pm) + _PM.constraint_storage_state(pm_model, i, nw=n_1_pm) + end + + for n_2_pm in network_ids_pm[2:end] + for i in _PM.ids(pm_model, :storage, nw=n_2_pm) + _PM.constraint_storage_state(pm_model, i, n_1_pm, n_2_pm) + end + n_1_pm = n_2_pm + end + + # --- PMD energy storage state constraint --- + network_ids_pmd = sort(collect(_PMD.nw_ids(pmd_model))) + + n_1_pmd = network_ids_pmd[1] + + for i in _PMD.ids(pmd_model, :storage; nw=n_1_pmd) + _PMD.constraint_storage_state(pmd_model, i; nw=n_1_pmd) + end + + for n_2_pmd in network_ids_pmd[2:end] + for i in _PMD.ids(pmd_model, :storage; nw=n_2_pmd) + _PMD.constraint_storage_state(pmd_model, i, n_1_pmd, n_2_pmd) + end + + n_1_pmd = n_2_pmd + end + + # ------------------------------------------------- + # --- PMITD(T&D) Cost Functions ------------------- + objective_itd_min_fuel_cost(pmitd) +end diff --git a/test/opfitd_pass.jl b/test/opfitd_pass.jl index a8720b4..ead3634 100644 --- a/test/opfitd_pass.jl +++ b/test/opfitd_pass.jl @@ -50,4 +50,42 @@ @test storage_ref_nw4[1]["cost"] == 0.25 end + @testset "solve_model (build_opfitd_storage): Balanced case5-case3 With Battery Generator ACP-ACPU " begin + pm_file = joinpath(dirname(trans_path), "case5_withload.m") + pmd_file = joinpath(dirname(dist_path), "case3_balanced_withBattery.dss") + pmitd_file = joinpath(dirname(bound_path), "case5_case3_bal_battery.json") + pmitd_type = NLPowerModelITD{ACPPowerModel, ACPUPowerModel} + pmitd_data = parse_files(pm_file, pmd_file, pmitd_file) + + # add cost to storages in PMD + for (st_name, st_data) in pmitd_data["it"]["pmd"]["storage"] + st_data["cost"] = 0.25 + end + + # instantiate model with eng2math_passthrough + eng2math_passthrough = Dict("storage"=>["cost"]) + pmitd_inst_model = solve_model(pmitd_data, pmitd_type, ipopt, build_opfitd_storage; eng2math_passthrough=eng2math_passthrough) + + end + + @testset "solve_model (build_mn_opfitd_storage): Multinetwork Balanced case5-case3 With Battery Generator ACP-ACPU " begin + pm_file = joinpath(dirname(trans_path), "case5_withload.m") + pmd_file = joinpath(dirname(dist_path), "case3_balanced_withBattery_mn.dss") + pmitd_file = joinpath(dirname(bound_path), "case5_case3_bal_battery_mn.json") + pmitd_type = NLPowerModelITD{ACPPowerModel, ACPUPowerModel} + pmitd_data = parse_files(pm_file, pmd_file, pmitd_file; multinetwork=true) + + # add cost to storages in PMD + for (nw_id, nw_data) in pmitd_data["it"]["pmd"]["nw"] + for (st_name, st_data) in nw_data["storage"] + st_data["cost"] = 0.25 + end + end + + # instantiate model with eng2math_passthrough + eng2math_passthrough = Dict("storage"=>["cost"]) + pmitd_inst_model = solve_model(pmitd_data, pmitd_type, ipopt, build_mn_opfitd_storage; multinetwork=true, eng2math_passthrough=eng2math_passthrough) + + end + end diff --git a/test/runtests.jl b/test/runtests.jl index cfd0f23..bf7936a 100755 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -23,23 +23,23 @@ scs_solver = optimizer_with_attributes(SCS.Optimizer, "verbose"=>0) PowerModelsITD.silence!() @testset "PowerModelsITD.jl" begin - include("io.jl") - include("base.jl") - include("data.jl") - include("autorename.jl") - include("opfitd.jl") - include("opfitd_duals.jl") - include("pfitd.jl") - include("opfitd_ms.jl") - include("pfitd_ms.jl") - include("transformations_opfitd.jl") - include("largescale_opfitd.jl") - include("opfitd_hybrids.jl") - include("pfitd_hybrids.jl") - include("opfitd_oltc.jl") - include("opfitd_mn.jl") - include("opfitd_oltc_mn.jl") - include("opfitd_dmld.jl") - include("opfitd_solution.jl") + # include("io.jl") + # include("base.jl") + # include("data.jl") + # include("autorename.jl") + # include("opfitd.jl") + # include("opfitd_duals.jl") + # include("pfitd.jl") + # include("opfitd_ms.jl") + # include("pfitd_ms.jl") + # include("transformations_opfitd.jl") + # include("largescale_opfitd.jl") + # include("opfitd_hybrids.jl") + # include("pfitd_hybrids.jl") + # include("opfitd_oltc.jl") + # include("opfitd_mn.jl") + # include("opfitd_oltc_mn.jl") + # include("opfitd_dmld.jl") + # include("opfitd_solution.jl") include("opfitd_pass.jl") end From 28795f71c5cc002713c6c30066bde963fd068e29 Mon Sep 17 00:00:00 2001 From: Juan Ospina Date: Tue, 26 Apr 2022 15:11:47 -0600 Subject: [PATCH 04/16] ADD: new objective functions to objective_storage.jl that consider energy storage costs in the objective function for the opf optimization --- src/PowerModelsITD.jl | 1 + src/core/objective_storage.jl | 390 ++++++++++++++++++++++++++++++++++ src/prob/opfitd_storage.jl | 16 +- test/opfitd_pass.jl | 25 ++- test/runtests.jl | 36 ++-- 5 files changed, 434 insertions(+), 34 deletions(-) create mode 100644 src/core/objective_storage.jl diff --git a/src/PowerModelsITD.jl b/src/PowerModelsITD.jl index 03e8468..53614de 100755 --- a/src/PowerModelsITD.jl +++ b/src/PowerModelsITD.jl @@ -43,6 +43,7 @@ module PowerModelsITD include("core/objective.jl") include("core/objective_dmld.jl") include("core/objective_dmld_simple.jl") + include("core/objective_storage.jl") include("core/solution.jl") include("data_model/transformations.jl") diff --git a/src/core/objective_storage.jl b/src/core/objective_storage.jl new file mode 100644 index 0000000..1598eab --- /dev/null +++ b/src/core/objective_storage.jl @@ -0,0 +1,390 @@ +""" + function objective_itd_min_fuel_cost_storage( + pmitd::AbstractPowerModelITD + ) + +Standard fuel cost minimization objective with storage. +""" +function objective_itd_min_fuel_cost_storage(pmitd::AbstractPowerModelITD) + + # Extract the transmission model + pm_model = _get_powermodel_from_powermodelitd(pmitd) + + # Extract the distribution model + pmd_model = _get_powermodeldistribution_from_powermodelitd(pmitd) + + # PM cost models + pm_cost_model = _PM.check_cost_models(pm_model) + + # PMD cost models + pmd_cost_model = _PMD.check_gen_cost_models(pmd_model) + + if pm_cost_model == 1 # && pmd_cost_model == 1 # pmd_cost_model may not be needed (see cases when no gen is in dist. system.) + @error "cost models of type 1 are currently not supported for problems with storage devices." + elseif pm_cost_model == 2 # && pmd_cost_model == 2 # pmd_cost_model may not be needed (see cases when no gen is in dist. system.) + return objective_itd_min_fuel_cost_polynomial_storage(pmitd, pm_model, pmd_model) + else + @error "Only cost models of types 1 and 2 are supported at this time, given cost model type of $(pm_cost_model) and $(pmd_cost_model)" + end + +end + + +""" + function objective_itd_min_fuel_cost_polynomial_storage( + pmitd::AbstractPowerModelITD, + pm::_PM.AbstractPowerModel, + pmd::_PMD.AbstractUnbalancedPowerModel + ) + +Fuel cost minimization objective for polynomial terms with storage. +""" +function objective_itd_min_fuel_cost_polynomial_storage(pmitd::AbstractPowerModelITD, pm::_PM.AbstractPowerModel, pmd::_PMD.AbstractUnbalancedPowerModel) + + # Extract the specific dictionary + pm_data = pmitd.data["it"]["pm"] + pmd_data = pmitd.data["it"]["pmd"] + + # PM + pm_order = _PM.calc_max_cost_index(pm_data)-1 + # PMD + pmd_order = _PMD.calc_max_cost_index(pmd_data)-1 + + if pm_order <= 2 && pmd_order <= 2 + return _objective_itd_min_fuel_cost_polynomial_linquad_storage(pmitd, pm, pmd) + else + return _objective_itd_min_fuel_cost_polynomial_nl_storage(pmitd, pm, pmd) + end + +end + + +""" + function _objective_itd_min_fuel_cost_polynomial_linquad_storage( + pmitd::AbstractPowerModelITD, + pm::_PM.AbstractPowerModel, + pmd::_PMD.AbstractUnbalancedPowerModel + ) + +Fuel cost minimization objective for polynomial terms linear-quadratic with storage. +""" +function _objective_itd_min_fuel_cost_polynomial_linquad_storage(pmitd::AbstractPowerModelITD, pm::_PM.AbstractPowerModel, pmd::_PMD.AbstractUnbalancedPowerModel) + + # PM + pm_gen_cost = Dict() + + for (n, nw_ref) in _PM.nws(pm) + for (i,gen) in nw_ref[:gen] + pg = sum( _PM.var(pm, n, :pg, i)[c] for c in _PM.conductor_ids(pm, n) ) + + if length(gen["cost"]) == 1 + pm_gen_cost[(n,i)] = gen["cost"][1] + elseif length(gen["cost"]) == 2 + pm_gen_cost[(n,i)] = gen["cost"][1]*pg + gen["cost"][2] + elseif length(gen["cost"]) == 3 + pm_gen_cost[(n,i)] = gen["cost"][1]*pg^2 + gen["cost"][2]*pg + gen["cost"][3] + else + pm_gen_cost[(n,i)] = 0.0 + end + end + end + + + # PMD + pmd_gen_cost = Dict() + + for (n, nw_ref) in _PMD.nws(pmd) + for (i,gen) in nw_ref[:gen] + pg = sum( _PMD.var(pmd, n, :pg, i)[c] for c in gen["connections"] ) + + if length(gen["cost"]) == 1 + pmd_gen_cost[(n,i)] = gen["cost"][1] + elseif length(gen["cost"]) == 2 + pmd_gen_cost[(n,i)] = gen["cost"][1]*pg + gen["cost"][2] + elseif length(gen["cost"]) == 3 + pmd_gen_cost[(n,i)] = gen["cost"][1]*pg^2 + gen["cost"][2]*pg + gen["cost"][3] + else + pmd_gen_cost[(n,i)] = 0.0 + end + end + end + + + # PMD Storage + pmd_strg_cost = Dict() + + for (n, nw_ref) in _PMD.nws(pmd) + for (i, strg) in nw_ref[:storage] + dsch = _PMD.var(pmd, n, :sd, i) # get discharge power value + pmd_strg_cost[(n,i)] = strg["cost"][1]*dsch # compute discharge cost + end + end + + + # ITD (Combined objective) + return JuMP.@objective(pmitd.model, Min, + sum( + sum( pm_gen_cost[(n,i)] for (i,gen) in nw_ref[:gen] ) + for (n, nw_ref) in _PM.nws(pm)) + + sum( + sum( pmd_gen_cost[(n,i)] for (i,gen) in nw_ref[:gen] ) + for (n, nw_ref) in _PMD.nws(pmd)) + + sum( + sum( pmd_strg_cost[(n,i)] for (i,strg) in nw_ref[:storage] ) + for (n, nw_ref) in _PMD.nws(pmd)) + ) + +end + + +""" + function _objective_itd_min_fuel_cost_polynomial_linquad_storage( + pmitd::AbstractIVRPowerModelITD, + pm::_PM.AbstractIVRModel, + pmd::_PMD.AbstractUnbalancedIVRModel + ) + +Fuel cost minimization objective for polynomial terms linear-quadratic (IVR formulation) with storage. +""" +function _objective_itd_min_fuel_cost_polynomial_linquad_storage(pmitd::AbstractIVRPowerModelITD, pm::_PM.AbstractIVRModel, pmd::_PMD.AbstractUnbalancedIVRModel) + + # PM + pm_gen_cost = Dict() + + for (n, nw_ref) in _PM.nws(pm) + for (i,gen) in nw_ref[:gen] + bus = gen["gen_bus"] + + #to avoid function calls inside of @NLconstraint: + pg = _PM.var(pm, n, :pg, i) + if length(gen["cost"]) == 1 + pm_gen_cost[(n,i)] = gen["cost"][1] + elseif length(gen["cost"]) == 2 + pm_gen_cost[(n,i)] = JuMP.@NLexpression(pm.model, gen["cost"][1]*pg + gen["cost"][2]) + elseif length(gen["cost"]) == 3 + pm_gen_cost[(n,i)] = JuMP.@NLexpression(pm.model, gen["cost"][1]*pg^2 + gen["cost"][2]*pg + gen["cost"][3]) + else + pm_gen_cost[(n,i)] = 0.0 + end + end + end + + # PMD + pmd_gen_cost = Dict() + + for (n, nw_ref) in _PMD.nws(pmd) + for (i,gen) in nw_ref[:gen] + bus = gen["gen_bus"] + + #to avoid function calls inside of @NLconstraint: + pg = _PMD.var(pmd, n, :pg, i) + if length(gen["cost"]) == 1 + pmd_gen_cost[(n,i)] = gen["cost"][1] + elseif length(gen["cost"]) == 2 + pmd_gen_cost[(n,i)] = JuMP.@NLexpression(pmd.model, gen["cost"][1]*sum(pg[c] for c in gen["connections"]) + gen["cost"][2]) + elseif length(gen["cost"]) == 3 + pmd_gen_cost[(n,i)] = JuMP.@NLexpression(pmd.model, gen["cost"][1]*sum(pg[c] for c in gen["connections"])^2 + gen["cost"][2]*sum(pg[c] for c in gen["connections"]) + gen["cost"][3]) + else + pmd_gen_cost[(n,i)] = 0.0 + end + end + end + + # PMD Storage + pmd_strg_cost = Dict() + + for (n, nw_ref) in _PMD.nws(pmd) + for (i, strg) in nw_ref[:storage] + dsch = _PMD.var(pmd, n, :sd, i) # get discharge power value + pmd_strg_cost[(n,i)] = strg["cost"][1]*dsch # compute discharge cost + end + end + + + # ITD (Combined objective) + return JuMP.@NLobjective(pmitd.model, Min, + sum( + sum( pm_gen_cost[(n,i)] for (i,gen) in nw_ref[:gen] ) + for (n, nw_ref) in _PM.nws(pm)) + + sum( + sum( pmd_gen_cost[(n,i)] for (i,gen) in nw_ref[:gen] ) + for (n, nw_ref) in _PMD.nws(pmd)) + + sum( + sum( pmd_strg_cost[(n,i)] for (i,strg) in nw_ref[:storage] ) + for (n, nw_ref) in _PMD.nws(pmd)) + ) + +end + + +""" + function _objective_itd_min_fuel_cost_polynomial_nl_storage( + pmitd::AbstractIVRPowerModelITD, + pm::_PM.AbstractIVRModel, + pmd::_PMD.AbstractUnbalancedIVRModel + ) + +Fuel cost minimization objective for polynomial terms non-linear (IVR formulation) with storage. +""" +function _objective_itd_min_fuel_cost_polynomial_nl_storage(pmitd::AbstractIVRPowerModelITD, pm::_PM.AbstractIVRModel, pmd::_PMD.AbstractUnbalancedIVRModel) + + # PM + pm_gen_cost = Dict() + for (n, nw_ref) in _PM.nws(pm) + for (i,gen) in nw_ref[:gen] + bus = gen["gen_bus"] + + #to avoid function calls inside of @NLconstraint: + pg = _PM.var(pm, n, :pg, i) + cost_rev = reverse(gen["cost"]) + if length(cost_rev) == 1 + pm_gen_cost[(n,i)] = JuMP.@NLexpression(pmitd.model, cost_rev[1]) + elseif length(cost_rev) == 2 + pm_gen_cost[(n,i)] = JuMP.@NLexpression(pmitd.model, cost_rev[1] + cost_rev[2]*pg) + elseif length(cost_rev) == 3 + pm_gen_cost[(n,i)] = JuMP.@NLexpression(pmitd.model, cost_rev[1] + cost_rev[2]*pg + cost_rev[3]*pg^2) + elseif length(cost_rev) >= 4 + cost_rev_nl = cost_rev[4:end] + pm_gen_cost[(n,i)] = JuMP.@NLexpression(pmitd.model, cost_rev[1] + cost_rev[2]*pg + cost_rev[3]*pg^2 + sum( v*pg^(d+3) for (d,v) in enumerate(cost_rev_nl)) ) + else + pm_gen_cost[(n,i)] = JuMP.@NLexpression(pmitd.model, 0.0) + end + end + end + + + # PMD + pmd_gen_cost = Dict() + for (n, nw_ref) in _PMD.nws(pmd) + for (i,gen) in nw_ref[:gen] + + pg = _PMD.var(pmd, n, :pg, i) + cost_rev = reverse(gen["cost"]) + + if length(cost_rev) == 1 + pmd_gen_cost[(n,i)] = JuMP.@NLexpression(pmitd.model, cost_rev[1]) + elseif length(cost_rev) == 2 + pmd_gen_cost[(n,i)] = JuMP.@NLexpression(pmitd.model, cost_rev[1] + cost_rev[2]*sum(pg[c] for c in gen["connections"])) + elseif length(cost_rev) == 3 + pmd_gen_cost[(n,i)] = JuMP.@NLexpression(pmitd.model, cost_rev[1] + cost_rev[2]*sum(pg[c] for c in gen["connections"]) + cost_rev[3]*sum(pg[c] for c in gen["connections"])^2) + elseif length(cost_rev) >= 4 + cost_rev_nl = cost_rev[4:end] + pmd_gen_cost[(n,i)] = JuMP.@NLexpression(pmitd.model, cost_rev[1] + cost_rev[2]*sum(pg[c] for c in gen["connections"]) + cost_rev[3]*sum(pg[c] for c in gen["connections"])^2 + sum( v*sum(pg[c] for c in gen["connections"])^(d+3) for (d,v) in enumerate(cost_rev_nl)) ) + else + pmd_gen_cost[(n,i)] = JuMP.@NLexpression(pmitd.model, 0.0) + end + end + end + + # PMD Storage + pmd_strg_cost = Dict() + + for (n, nw_ref) in _PMD.nws(pmd) + for (i, strg) in nw_ref[:storage] + dsch = _PMD.var(pmd, n, :sd, i) # get discharge power value + pmd_strg_cost[(n,i)] = strg["cost"][1]*dsch # compute discharge cost + end + end + + + # ITD (Combined objective) + return JuMP.@NLobjective(pmitd.model, Min, + sum( + sum( pm_gen_cost[(n,i)] for (i,gen) in nw_ref[:gen] ) + for (n, nw_ref) in _PM.nws(pm)) + + sum( + sum( pmd_gen_cost[(n,i)] for (i,gen) in nw_ref[:gen] ) + for (n, nw_ref) in _PMD.nws(pmd)) + + sum( + sum( pmd_strg_cost[(n,i)] for (i,strg) in nw_ref[:storage] ) + for (n, nw_ref) in _PMD.nws(pmd)) + ) + +end + + +""" + function _objective_itd_min_fuel_cost_polynomial_nl_storage( + pmitd::AbstractPowerModelITD, + pm::_PM.AbstractPowerModel, + pmd::_PMD.AbstractUnbalancedPowerModel + ) + +Fuel cost minimization objective for polynomial terms non-linear with storage. +""" +function _objective_itd_min_fuel_cost_polynomial_nl_storage(pmitd::AbstractPowerModelITD, pm::_PM.AbstractPowerModel, pmd::_PMD.AbstractUnbalancedPowerModel) + + # PM + pm_gen_cost = Dict() + for (n, nw_ref) in _PM.nws(pm) + for (i,gen) in nw_ref[:gen] + pg = sum( _PM.var(pm, n, :pg, i)[c] for c in _PM.conductor_ids(pm, n)) + + cost_rev = reverse(gen["cost"]) + if length(cost_rev) == 1 + pm_gen_cost[(n,i)] = JuMP.@NLexpression(pmitd.model, cost_rev[1]) + elseif length(cost_rev) == 2 + pm_gen_cost[(n,i)] = JuMP.@NLexpression(pmitd.model, cost_rev[1] + cost_rev[2]*pg) + elseif length(cost_rev) == 3 + pm_gen_cost[(n,i)] = JuMP.@NLexpression(pmitd.model, cost_rev[1] + cost_rev[2]*pg + cost_rev[3]*pg^2) + elseif length(cost_rev) >= 4 + cost_rev_nl = cost_rev[4:end] + pm_gen_cost[(n,i)] = JuMP.@NLexpression(pmitd.model, cost_rev[1] + cost_rev[2]*pg + cost_rev[3]*pg^2 + sum( v*pg^(d+3) for (d,v) in enumerate(cost_rev_nl)) ) + else + pm_gen_cost[(n,i)] = JuMP.@NLexpression(pmitd.model, 0.0) + end + end + end + + + # PMD + pmd_gen_cost = Dict() + for (n, nw_ref) in _PMD.nws(pmd) + for (i,gen) in nw_ref[:gen] + pg = sum( _PMD.var(pmd, n, :pg, i)[c] for c in gen["connections"] ) + + cost_rev = reverse(gen["cost"]) + + # This is needed to get around error: "unexpected affine expression in nl objective", See JuMP Documentation: 'AffExpr and QuadExpr cannot be used' + pg_aux = JuMP.@variable(pmitd.model, base_name="$(n)_pg_aux_dist_$(i)") + JuMP.@constraint(pmitd.model, pg_aux == pg) + + if length(cost_rev) == 1 + pmd_gen_cost[(n,i)] = JuMP.@NLexpression(pmitd.model, cost_rev[1]) + elseif length(cost_rev) == 2 + pmd_gen_cost[(n,i)] = JuMP.@NLexpression(pmitd.model, cost_rev[1] + cost_rev[2]*pg_aux) + elseif length(cost_rev) == 3 + pmd_gen_cost[(n,i)] = JuMP.@NLexpression(pmitd.model, cost_rev[1] + cost_rev[2]*pg_aux + cost_rev[3]*pg_aux^2) + elseif length(cost_rev) >= 4 + cost_rev_nl = cost_rev[4:end] + pmd_gen_cost[(n,i)] = JuMP.@NLexpression(pmitd.model, cost_rev[1] + cost_rev[2]*pg_aux + cost_rev[3]*pg_aux^2 + sum( v*pg_aux^(d+3) for (d,v) in enumerate(cost_rev_nl)) ) + else + pmd_gen_cost[(n,i)] = JuMP.@NLexpression(pmitd.model, 0.0) + end + end + end + + # PMD Storage + pmd_strg_cost = Dict() + + for (n, nw_ref) in _PMD.nws(pmd) + for (i, strg) in nw_ref[:storage] + dsch = _PMD.var(pmd, n, :sd, i) # get discharge power value + pmd_strg_cost[(n,i)] = strg["cost"][1]*dsch # compute discharge cost + end + end + + # ITD (Combined objective) + return JuMP.@NLobjective(pmitd.model, Min, + sum( + sum( pm_gen_cost[(n,i)] for (i,gen) in nw_ref[:gen] ) + for (n, nw_ref) in _PM.nws(pm)) + + sum( + sum( pmd_gen_cost[(n,i)] for (i,gen) in nw_ref[:gen] ) + for (n, nw_ref) in _PMD.nws(pmd)) + + sum( + sum( pmd_strg_cost[(n,i)] for (i,strg) in nw_ref[:storage] ) + for (n, nw_ref) in _PMD.nws(pmd)) + ) + +end diff --git a/src/prob/opfitd_storage.jl b/src/prob/opfitd_storage.jl index 9fee75a..9f7cbe2 100755 --- a/src/prob/opfitd_storage.jl +++ b/src/prob/opfitd_storage.jl @@ -178,7 +178,7 @@ function build_opfitd_storage(pmitd::AbstractPowerModelITD) # ------------------------------------------------- # --- PMITD(T&D) Cost Functions ------------------- - objective_itd_min_fuel_cost(pmitd) + objective_itd_min_fuel_cost_storage(pmitd) end @@ -299,7 +299,7 @@ function build_opfitd_storage(pmitd::AbstractIVRPowerModelITD) # ------------------------------------------------- # --- PMITD(T&D) Cost Functions ------------------- - objective_itd_min_fuel_cost(pmitd) + objective_itd_min_fuel_cost_storage(pmitd) end @@ -441,7 +441,7 @@ function build_opfitd_storage(pmitd::AbstractBFPowerModelITD) # ------------------------------------------------- # --- PMITD(T&D) Cost Functions ------------------- - objective_itd_min_fuel_cost(pmitd) + objective_itd_min_fuel_cost_storage(pmitd) end @@ -580,7 +580,7 @@ function build_opfitd_storage(pmitd::AbstractLNLBFPowerModelITD) # ------------------------------------------------- # --- PMITD(T&D) Cost Functions ------------------- - objective_itd_min_fuel_cost(pmitd) + objective_itd_min_fuel_cost_storage(pmitd) end @@ -762,7 +762,7 @@ function build_mn_opfitd_storage(pmitd::AbstractPowerModelITD) # ------------------------------------------------- # --- PMITD(T&D) Cost Functions ------------------- - objective_itd_min_fuel_cost(pmitd) + objective_itd_min_fuel_cost_storage(pmitd) end @@ -931,7 +931,7 @@ function build_mn_opfitd_storage(pmitd::AbstractIVRPowerModelITD) # ------------------------------------------------- # --- PMITD(T&D) Cost Functions ------------------- - objective_itd_min_fuel_cost(pmitd) + objective_itd_min_fuel_cost_storage(pmitd) end @@ -1109,7 +1109,7 @@ function build_mn_opfitd_storage(pmitd::AbstractBFPowerModelITD) # ------------------------------------------------- # --- PMITD(T&D) Cost Functions ------------------- - objective_itd_min_fuel_cost(pmitd) + objective_itd_min_fuel_cost_storage(pmitd) end @@ -1286,5 +1286,5 @@ function build_mn_opfitd_storage(pmitd::AbstractLNLBFPowerModelITD) # ------------------------------------------------- # --- PMITD(T&D) Cost Functions ------------------- - objective_itd_min_fuel_cost(pmitd) + objective_itd_min_fuel_cost_storage(pmitd) end diff --git a/test/opfitd_pass.jl b/test/opfitd_pass.jl index ead3634..bda5ad5 100644 --- a/test/opfitd_pass.jl +++ b/test/opfitd_pass.jl @@ -50,42 +50,51 @@ @test storage_ref_nw4[1]["cost"] == 0.25 end - @testset "solve_model (build_opfitd_storage): Balanced case5-case3 With Battery Generator ACP-ACPU " begin + @testset "solve_model (build_opfitd_storage): Balanced case5-case3 With Battery ACP-ACPU " begin pm_file = joinpath(dirname(trans_path), "case5_withload.m") pmd_file = joinpath(dirname(dist_path), "case3_balanced_withBattery.dss") pmitd_file = joinpath(dirname(bound_path), "case5_case3_bal_battery.json") pmitd_type = NLPowerModelITD{ACPPowerModel, ACPUPowerModel} pmitd_data = parse_files(pm_file, pmd_file, pmitd_file) + # cost to assing to energy storage + strg_cost = 250 + # add cost to storages in PMD for (st_name, st_data) in pmitd_data["it"]["pmd"]["storage"] - st_data["cost"] = 0.25 + st_data["cost"] = strg_cost end - # instantiate model with eng2math_passthrough + # eng2math_passthrough eng2math_passthrough = Dict("storage"=>["cost"]) - pmitd_inst_model = solve_model(pmitd_data, pmitd_type, ipopt, build_opfitd_storage; eng2math_passthrough=eng2math_passthrough) + + # with storage cost problem + pmitd_result_strg = solve_model(pmitd_data, pmitd_type, ipopt, build_opfitd_storage; eng2math_passthrough=eng2math_passthrough) + @test isapprox(pmitd_result_strg["objective"], 17977.4682739707+(pmitd_result_strg["solution"]["it"]["pmd"]["storage"]["3bus_bal_battery.s1"]["sd"]*strg_cost)/100000; atol = 1e-4) end - @testset "solve_model (build_mn_opfitd_storage): Multinetwork Balanced case5-case3 With Battery Generator ACP-ACPU " begin + @testset "solve_model (build_mn_opfitd_storage): Multinetwork Balanced case5-case3 With Battery ACP-ACPU " begin pm_file = joinpath(dirname(trans_path), "case5_withload.m") pmd_file = joinpath(dirname(dist_path), "case3_balanced_withBattery_mn.dss") pmitd_file = joinpath(dirname(bound_path), "case5_case3_bal_battery_mn.json") pmitd_type = NLPowerModelITD{ACPPowerModel, ACPUPowerModel} pmitd_data = parse_files(pm_file, pmd_file, pmitd_file; multinetwork=true) + # cost to assing to energy storage + strg_cost = 250 + # add cost to storages in PMD for (nw_id, nw_data) in pmitd_data["it"]["pmd"]["nw"] for (st_name, st_data) in nw_data["storage"] - st_data["cost"] = 0.25 + st_data["cost"] = strg_cost end end # instantiate model with eng2math_passthrough eng2math_passthrough = Dict("storage"=>["cost"]) - pmitd_inst_model = solve_model(pmitd_data, pmitd_type, ipopt, build_mn_opfitd_storage; multinetwork=true, eng2math_passthrough=eng2math_passthrough) - + pmitd_result_strg = solve_model(pmitd_data, pmitd_type, ipopt, build_mn_opfitd_storage; multinetwork=true, eng2math_passthrough=eng2math_passthrough) + @test isapprox(pmitd_result_strg["objective"], 71226.94293293+0.12500248519; atol = 1e-4) end end diff --git a/test/runtests.jl b/test/runtests.jl index bf7936a..cfd0f23 100755 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -23,23 +23,23 @@ scs_solver = optimizer_with_attributes(SCS.Optimizer, "verbose"=>0) PowerModelsITD.silence!() @testset "PowerModelsITD.jl" begin - # include("io.jl") - # include("base.jl") - # include("data.jl") - # include("autorename.jl") - # include("opfitd.jl") - # include("opfitd_duals.jl") - # include("pfitd.jl") - # include("opfitd_ms.jl") - # include("pfitd_ms.jl") - # include("transformations_opfitd.jl") - # include("largescale_opfitd.jl") - # include("opfitd_hybrids.jl") - # include("pfitd_hybrids.jl") - # include("opfitd_oltc.jl") - # include("opfitd_mn.jl") - # include("opfitd_oltc_mn.jl") - # include("opfitd_dmld.jl") - # include("opfitd_solution.jl") + include("io.jl") + include("base.jl") + include("data.jl") + include("autorename.jl") + include("opfitd.jl") + include("opfitd_duals.jl") + include("pfitd.jl") + include("opfitd_ms.jl") + include("pfitd_ms.jl") + include("transformations_opfitd.jl") + include("largescale_opfitd.jl") + include("opfitd_hybrids.jl") + include("pfitd_hybrids.jl") + include("opfitd_oltc.jl") + include("opfitd_mn.jl") + include("opfitd_oltc_mn.jl") + include("opfitd_dmld.jl") + include("opfitd_solution.jl") include("opfitd_pass.jl") end From e0e3c1971544e2bb99097aebba1ac77715bede82 Mon Sep 17 00:00:00 2001 From: Juan Ospina Date: Wed, 27 Apr 2022 16:32:25 -0600 Subject: [PATCH 05/16] DOC: correct CHANGELOG.md information for staged changes. --- CHANGELOG.md | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3d63f08..f7ebccc 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,14 @@ # PowerModelsITD.jl Change Log +## staged LAAExperimental + +- Added `eng2math_passthrough` parameter to all `instantiate_model(..)` and `solve_X(...)` functions. +- Added unit tests to `opftid_pass.jl` that test the correct operation of the `eng2math_passthrough` both in single network and multinetwork applications. +- Added test cases files for the `opftid_pass.jl` unit tests. +- Added new problem specifications `build_opfitd_storage` and `build_mn_opfitd_storage` to `prob/opfitd_storage.jl` that perform opf optimization taking into account storage costs. +- Added new objective functions to `objective_storage.jl` that consider energy storage costs in the objective function for the opf optimization. +- Added unit tests to `opftid_pass.jl` that test the new costs functions with energy storage costs added. + ## staged - none. @@ -65,12 +74,6 @@ - Added `Pull` request template to `.github`. - Fixed major bug in `transform_pmitd_solution_to_eng!` function that was causing boundary power flow values to be the same for all nw in multinetwork problems. - Added unit test to `opfitd_mn` (and new data files) that test that boundary power flow solution values differ when solving a multinetwork problem with different loading conditions. -- Added `eng2math_passthrough` parameter to all `instantiate_model(..)` and `solve_X(...)` functions. -- Added unit tests to `opftid_pass.jl` that test the correct operation of the `eng2math_passthrough` both in single network and multinetwork applications. -- Added test cases files for the `opftid_pass.jl` unit tests. -- Added new problem specifications `build_opfitd_storage` and `build_mn_opfitd_storage` to `prob/opfitd_storage.jl` that perform opf optimization taking into account storage costs. -- Added new objective functions to `objective_storage.jl` that consider energy storage costs in the objective function for the opf optimization. -- Added unit tests to `opftid_pass.jl` that test the new costs functions with energy storage costs added. ## v0.7.0 From c368b64e1fd18a04c6560ced85a175ff32d56def Mon Sep 17 00:00:00 2001 From: Juan Ospina Date: Tue, 3 May 2022 16:46:44 -0600 Subject: [PATCH 06/16] DOC: Added comment that explains the units of the storage cost to opfitd_pass.jl unit tests. --- test/opfitd_pass.jl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/opfitd_pass.jl b/test/opfitd_pass.jl index bda5ad5..e6f6996 100644 --- a/test/opfitd_pass.jl +++ b/test/opfitd_pass.jl @@ -58,6 +58,7 @@ pmitd_data = parse_files(pm_file, pmd_file, pmitd_file) # cost to assing to energy storage + # Units $/pu, to convert from your wanted $/MWh just multiply by MVABase (e.g., 2.5 $/MWh x 100 MWh/1pu = 250 $/pu) strg_cost = 250 # add cost to storages in PMD @@ -82,6 +83,7 @@ pmitd_data = parse_files(pm_file, pmd_file, pmitd_file; multinetwork=true) # cost to assing to energy storage + # Units $/pu, to convert from your wanted $/MWh just multiply by MVABase (e.g., 2.5 $/MWh x 100 MWh/1pu = 250 $/pu) strg_cost = 250 # add cost to storages in PMD From 07f018af444b052fedb5185193d4e01e30baa70c Mon Sep 17 00:00:00 2001 From: Juan Ospina Date: Mon, 13 Nov 2023 16:11:11 -0700 Subject: [PATCH 07/16] ADD: new default costs to storage devices at both transmission and distribution systems. --- CHANGELOG.md | 14 +- Project.toml | 5 +- src/core/data.jl | 62 ++++++++ src/core/objective_storage.jl | 111 +++++++++++---- src/io/common.jl | 26 ++++ src/prob/opfitd.jl | 46 ++++-- src/prob/opfitd_oltc.jl | 34 ++++- src/prob/opfitd_storage.jl | 62 ++++++-- test/data/transmission/case5_withload_strg.m | 64 +++++++++ test/opfitd_pass.jl | 102 -------------- test/opfitd_storage.jl | 140 +++++++++++++++++++ test/runtests.jl | 38 ++--- 12 files changed, 529 insertions(+), 175 deletions(-) create mode 100755 test/data/transmission/case5_withload_strg.m delete mode 100644 test/opfitd_pass.jl create mode 100644 test/opfitd_storage.jl diff --git a/CHANGELOG.md b/CHANGELOG.md index f7ebccc..05c754d 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,13 +1,19 @@ # PowerModelsITD.jl Change Log -## staged LAAExperimental +## staged Energy-Storage - Added `eng2math_passthrough` parameter to all `instantiate_model(..)` and `solve_X(...)` functions. -- Added unit tests to `opftid_pass.jl` that test the correct operation of the `eng2math_passthrough` both in single network and multinetwork applications. -- Added test cases files for the `opftid_pass.jl` unit tests. +- Added unit tests to `opftid_storage.jl` that test the correct operation of the `eng2math_passthrough` both in single network and multinetwork applications. +- Added test cases files for the `opftid_storage.jl` unit tests. - Added new problem specifications `build_opfitd_storage` and `build_mn_opfitd_storage` to `prob/opfitd_storage.jl` that perform opf optimization taking into account storage costs. - Added new objective functions to `objective_storage.jl` that consider energy storage costs in the objective function for the opf optimization. -- Added unit tests to `opftid_pass.jl` that test the new costs functions with energy storage costs added. +- Added unit tests to `opftid_storage.jl` that test the new costs functions with energy storage costs added. +- Added functions `_compute_default_strg_cost_transmission` and `_compute_default_strg_cost_distribution` to `data.jl` that compute default costs for storage devices based on dafault parameters. Users are encouraged to use their own costs. +- Added code to `common.jl` functions `parse_power_distribution_file` and `parse_power_transmission_file` that adds default costs to distribution and transmission system(s) storage devices when parsing their individual files. +- Modified `solve_opfitd_storage` and `solve_mn_opfitd_storage` functions in `opfitd_storage.jl`, so that the `eng2math_passthrough = Dict("storage"=>["cost"])` is done automatically. PMITD assumes that if an user is running the `solve_opfitd_storage(...)` function, then storage costs should be passed from `ENG` to `MATH` models. +- Added the required variables and constraints for Transmission system-related storage devices to all problem specifications in `opfitd_storage.jl`. +- Fixed storage variables and constraints being used in `opfitd` and `opfitd_oltc.jl` where the versions used were the `_mi` mixed integer versions. +- Modified all objectives in `objective_storage.jl` to take into account the cost of discharging the storage devices in the transmission system. Also, added in-situ conversion for the PMD cost that converts the $/kWh -> $/pu cost, so that the user can provide the $/kWh in the `ENG` model, avoiding confusions. ## staged diff --git a/Project.toml b/Project.toml index ac4cbe6..b55201b 100755 --- a/Project.toml +++ b/Project.toml @@ -6,22 +6,25 @@ version = "0.7.9" [deps] InfrastructureModels = "2030c09a-7f63-5d83-885d-db604e0e9cc0" +Ipopt = "b6b21f68-93f8-5de0-b562-5493be1d77c9" JSON = "682c06a0-de6a-54ab-a142-c8b1cf79cde6" JuMP = "4076af6c-e467-56ae-b986-b466b2749572" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" PowerModels = "c36e90e8-916a-50a6-bd94-075b64ef4655" PowerModelsDistribution = "d7431456-977f-11e9-2de3-97ff7677985e" +SCS = "c946c3f1-0d1f-5ce8-9dea-7daa1f7e2d13" +Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" [compat] InfrastructureModels = "0.7.8" Ipopt = "0.9, 1.0.2" JSON = "~0.18, ~0.19, ~0.20, ~0.21" JuMP = "~0.22, ~0.23, 1" +LinearAlgebra = "1.6" PowerModels = "0.19.9" PowerModelsDistribution = "0.15.1" SCS = "~1.0, ~1.1" julia = "1.6" -LinearAlgebra = "1.6" [extras] Ipopt = "b6b21f68-93f8-5de0-b562-5493be1d77c9" diff --git a/src/core/data.jl b/src/core/data.jl index be637d5..112824a 100644 --- a/src/core/data.jl +++ b/src/core/data.jl @@ -179,3 +179,65 @@ function calc_transmission_branch_flow_ac!(result::Dict{String,<:Any}, pmitd_dat # updates the network data with transmission power flows calculated _IM.update_data!(pmitd_data["it"]["pm"], flows) end + + +""" + function _compute_default_strg_cost_transmission( + strg_data::Dict{String,<:Any}, + baseMVA::Float64 + ) + +Computes a default cost for the energy storage (transmission). +Returns the default storage cost to be added to the data dictionary. +""" +function _compute_default_strg_cost_transmission(strg_data::Dict{String,<:Any}, baseMVA::Float64) + + # Obtained from 2019 U.S. utility-scale storage NREL data ($/MWh of energy rating). + cost_utility_scale_strg_dollar_per_mwh = 345000 + + # Calculate cost in $ for entire strg system + cost_strg_system_dollars = cost_utility_scale_strg_dollar_per_mwh*strg_data["energy_rating"]*baseMVA + + # Calculate cost to be used in optimization (in $/MWh) + cycles = 2000 # Total number of cycles under warranty (default value) + DoD = 1.0 # Default Depth of Discharge (100%) + cost_strg_dollars_per_MWh = cost_strg_system_dollars/(cycles*(strg_data["energy_rating"]*baseMVA)*DoD*(strg_data["charge_efficiency"]*strg_data["discharge_efficiency"])) + + # Convert cost from $/MWh -> $/pu: to convert from $/MWh to $/pu just multiply by MVA Base (e.g., 2.5 $/MWh x 100 MWh/1pu = 250 $/pu) + cost_strg_dollars_per_pu = cost_strg_dollars_per_MWh*baseMVA + + # Round to 4 digits the strg cost + cost_strg_dollars_per_pu = round(cost_strg_dollars_per_pu, digits=4) + + return cost_strg_dollars_per_pu + +end + + +""" + function _compute_default_strg_cost_distribution( + strg_data::Dict{String,<:Any} + ) + +Computes a default cost for the energy storage (distribution). +Returns the default storage cost to be added to the data dictionary. +""" +function _compute_default_strg_cost_distribution(strg_data::Dict{String,<:Any}) + + # Obtained from 2019 U.S. residential-scale storage NREL data ($/kWh of energy rating). + cost_utility_scale_strg_dollar_per_kwh = 1100 + + # Calculate cost in $ for entire strg system + cost_strg_system_dollars = cost_utility_scale_strg_dollar_per_kwh*strg_data["energy_ub"] + + # Calculate cost to be used in optimization (in $/kWh) + cycles = 2000 # Total number of cycles under warranty (default value) + DoD = 1.0 # Default Depth of Discharge (100%) + cost_strg_dollars_per_kWh = cost_strg_system_dollars/(cycles*strg_data["energy_ub"]*DoD*((strg_data["charge_efficiency"]/100)*(strg_data["discharge_efficiency"]/100))) + + # Round to 4 digits the strg cost + cost_strg_dollars_per_kWh = round(cost_strg_dollars_per_kWh, digits=4) + + return cost_strg_dollars_per_kWh + +end diff --git a/src/core/objective_storage.jl b/src/core/objective_storage.jl index 1598eab..aa8ed5f 100644 --- a/src/core/objective_storage.jl +++ b/src/core/objective_storage.jl @@ -89,6 +89,15 @@ function _objective_itd_min_fuel_cost_polynomial_linquad_storage(pmitd::Abstract end end + # PM Storage + pm_strg_cost = Dict() + + for (n, nw_ref) in _PM.nws(pm) + for (i, strg) in nw_ref[:storage] + dsch = _PM.var(pm, n, :sd, i) # get discharge power value + pm_strg_cost[(n,i)] = strg["cost"][1]*dsch # compute discharge cost (no cost conversion is needed, cost must be in $/pu) + end + end # PMD pmd_gen_cost = Dict() @@ -115,8 +124,10 @@ function _objective_itd_min_fuel_cost_polynomial_linquad_storage(pmitd::Abstract for (n, nw_ref) in _PMD.nws(pmd) for (i, strg) in nw_ref[:storage] - dsch = _PMD.var(pmd, n, :sd, i) # get discharge power value - pmd_strg_cost[(n,i)] = strg["cost"][1]*dsch # compute discharge cost + dsch = _PMD.var(pmd, n, :sd, i) # get discharge power value + strg_cost_dollar_per_pu = strg["cost"][1]*nw_ref[:settings]["sbase_default"] # convert from $/kWh -> $/pu + strg_cost_dollar_per_pu = round(strg_cost_dollar_per_pu, digits=4) + pmd_strg_cost[(n,i)] = strg_cost_dollar_per_pu*dsch # compute discharge cost end end @@ -126,6 +137,9 @@ function _objective_itd_min_fuel_cost_polynomial_linquad_storage(pmitd::Abstract sum( sum( pm_gen_cost[(n,i)] for (i,gen) in nw_ref[:gen] ) for (n, nw_ref) in _PM.nws(pm)) + + sum( + sum( pm_strg_cost[(n,i)] for (i,strg) in nw_ref[:storage] ) + for (n, nw_ref) in _PM.nws(pm)) + sum( sum( pmd_gen_cost[(n,i)] for (i,gen) in nw_ref[:gen] ) for (n, nw_ref) in _PMD.nws(pmd)) @@ -169,6 +183,16 @@ function _objective_itd_min_fuel_cost_polynomial_linquad_storage(pmitd::Abstract end end + # PM Storage + pm_strg_cost = Dict() + + for (n, nw_ref) in _PM.nws(pm) + for (i, strg) in nw_ref[:storage] + dsch = _PM.var(pm, n, :sd, i) # get discharge power value + pm_strg_cost[(n,i)] = strg["cost"][1]*dsch # compute discharge cost (no cost conversion is needed, cost must be in $/pu) + end + end + # PMD pmd_gen_cost = Dict() @@ -190,15 +214,17 @@ function _objective_itd_min_fuel_cost_polynomial_linquad_storage(pmitd::Abstract end end - # PMD Storage - pmd_strg_cost = Dict() + # PMD Storage + pmd_strg_cost = Dict() - for (n, nw_ref) in _PMD.nws(pmd) - for (i, strg) in nw_ref[:storage] - dsch = _PMD.var(pmd, n, :sd, i) # get discharge power value - pmd_strg_cost[(n,i)] = strg["cost"][1]*dsch # compute discharge cost - end - end + for (n, nw_ref) in _PMD.nws(pmd) + for (i, strg) in nw_ref[:storage] + dsch = _PMD.var(pmd, n, :sd, i) # get discharge power value + strg_cost_dollar_per_pu = strg["cost"][1]*nw_ref[:settings]["sbase_default"] # convert from $/kWh -> $/pu + strg_cost_dollar_per_pu = round(strg_cost_dollar_per_pu, digits=4) + pmd_strg_cost[(n,i)] = strg_cost_dollar_per_pu*dsch # compute discharge cost + end + end # ITD (Combined objective) @@ -206,6 +232,9 @@ function _objective_itd_min_fuel_cost_polynomial_linquad_storage(pmitd::Abstract sum( sum( pm_gen_cost[(n,i)] for (i,gen) in nw_ref[:gen] ) for (n, nw_ref) in _PM.nws(pm)) + + sum( + sum( pm_strg_cost[(n,i)] for (i,strg) in nw_ref[:storage] ) + for (n, nw_ref) in _PM.nws(pm)) + sum( sum( pmd_gen_cost[(n,i)] for (i,gen) in nw_ref[:gen] ) for (n, nw_ref) in _PMD.nws(pmd)) @@ -252,6 +281,16 @@ function _objective_itd_min_fuel_cost_polynomial_nl_storage(pmitd::AbstractIVRPo end end + # PM Storage + pm_strg_cost = Dict() + + for (n, nw_ref) in _PM.nws(pm) + for (i, strg) in nw_ref[:storage] + dsch = _PM.var(pm, n, :sd, i) # get discharge power value + pm_strg_cost[(n,i)] = strg["cost"][1]*dsch # compute discharge cost (no cost conversion is needed, cost must be in $/pu) + end + end + # PMD pmd_gen_cost = Dict() @@ -276,15 +315,17 @@ function _objective_itd_min_fuel_cost_polynomial_nl_storage(pmitd::AbstractIVRPo end end - # PMD Storage - pmd_strg_cost = Dict() + # PMD Storage + pmd_strg_cost = Dict() - for (n, nw_ref) in _PMD.nws(pmd) - for (i, strg) in nw_ref[:storage] - dsch = _PMD.var(pmd, n, :sd, i) # get discharge power value - pmd_strg_cost[(n,i)] = strg["cost"][1]*dsch # compute discharge cost - end - end + for (n, nw_ref) in _PMD.nws(pmd) + for (i, strg) in nw_ref[:storage] + dsch = _PMD.var(pmd, n, :sd, i) # get discharge power value + strg_cost_dollar_per_pu = strg["cost"][1]*nw_ref[:settings]["sbase_default"] # convert from $/kWh -> $/pu + strg_cost_dollar_per_pu = round(strg_cost_dollar_per_pu, digits=4) + pmd_strg_cost[(n,i)] = strg_cost_dollar_per_pu*dsch # compute discharge cost + end + end # ITD (Combined objective) @@ -292,6 +333,9 @@ function _objective_itd_min_fuel_cost_polynomial_nl_storage(pmitd::AbstractIVRPo sum( sum( pm_gen_cost[(n,i)] for (i,gen) in nw_ref[:gen] ) for (n, nw_ref) in _PM.nws(pm)) + + sum( + sum( pm_strg_cost[(n,i)] for (i,strg) in nw_ref[:storage] ) + for (n, nw_ref) in _PM.nws(pm)) + sum( sum( pmd_gen_cost[(n,i)] for (i,gen) in nw_ref[:gen] ) for (n, nw_ref) in _PMD.nws(pmd)) @@ -336,6 +380,16 @@ function _objective_itd_min_fuel_cost_polynomial_nl_storage(pmitd::AbstractPower end end + # PM Storage + pm_strg_cost = Dict() + + for (n, nw_ref) in _PM.nws(pm) + for (i, strg) in nw_ref[:storage] + dsch = _PM.var(pm, n, :sd, i) # get discharge power value + pm_strg_cost[(n,i)] = strg["cost"][1]*dsch # compute discharge cost (no cost conversion is needed, cost must be in $/pu) + end + end + # PMD pmd_gen_cost = Dict() @@ -364,21 +418,26 @@ function _objective_itd_min_fuel_cost_polynomial_nl_storage(pmitd::AbstractPower end end - # PMD Storage - pmd_strg_cost = Dict() + # PMD Storage + pmd_strg_cost = Dict() - for (n, nw_ref) in _PMD.nws(pmd) - for (i, strg) in nw_ref[:storage] - dsch = _PMD.var(pmd, n, :sd, i) # get discharge power value - pmd_strg_cost[(n,i)] = strg["cost"][1]*dsch # compute discharge cost - end - end + for (n, nw_ref) in _PMD.nws(pmd) + for (i, strg) in nw_ref[:storage] + dsch = _PMD.var(pmd, n, :sd, i) # get discharge power value + strg_cost_dollar_per_pu = strg["cost"][1]*nw_ref[:settings]["sbase_default"] # convert from $/kWh -> $/pu + strg_cost_dollar_per_pu = round(strg_cost_dollar_per_pu, digits=4) + pmd_strg_cost[(n,i)] = strg_cost_dollar_per_pu*dsch # compute discharge cost + end + end # ITD (Combined objective) return JuMP.@NLobjective(pmitd.model, Min, sum( sum( pm_gen_cost[(n,i)] for (i,gen) in nw_ref[:gen] ) for (n, nw_ref) in _PM.nws(pm)) + + sum( + sum( pm_strg_cost[(n,i)] for (i,strg) in nw_ref[:storage] ) + for (n, nw_ref) in _PM.nws(pm)) + sum( sum( pmd_gen_cost[(n,i)] for (i,gen) in nw_ref[:gen] ) for (n, nw_ref) in _PMD.nws(pmd)) diff --git a/src/io/common.jl b/src/io/common.jl index 409dcbe..d0233aa 100755 --- a/src/io/common.jl +++ b/src/io/common.jl @@ -93,6 +93,14 @@ function parse_power_transmission_file(pm_file::String; skip_correct::Bool=true, # Parse file data = _PM.parse_file(pm_file; validate = !skip_correct) + # Add default cost to storage devices in transmission. + # (Users can assign their own cost values by modifying these costs from the dictionary). + if haskey(data, "storage") + for (strg_name, strg_data) in data["storage"] + strg_data["cost"] = _compute_default_strg_cost_transmission(strg_data, data["baseMVA"]) + end + end + # replicate if multinetwork if multinetwork data = _PM.replicate(data, number_multinetworks) @@ -139,6 +147,24 @@ function parse_power_distribution_file(pmd_file::String, base_data::Dict{String, data = _PMD.parse_file(pmd_file; multinetwork=multinetwork) end + # Add default cost to storage devices in distribution. + # (Users can assign their own cost values by modifying these costs from the dictionary). + if multinetwork + for (nw_id, nw_data) in data["nw"] + if haskey(nw_data, "storage") + for (strg_name, strg_data) in nw_data["storage"] + strg_data["cost"] = _compute_default_strg_cost_distribution(strg_data) + end + end + end + else + if haskey(data, "storage") + for (strg_name, strg_data) in data["storage"] + strg_data["cost"] = _compute_default_strg_cost_distribution(strg_data) + end + end + end + if (unique == false) # checks the circuit names are not the same, and rename them only if auto_rename=true diff --git a/src/prob/opfitd.jl b/src/prob/opfitd.jl index c3f331e..1950aa3 100755 --- a/src/prob/opfitd.jl +++ b/src/prob/opfitd.jl @@ -63,6 +63,7 @@ function build_opfitd(pmitd::AbstractPowerModelITD) _PM.variable_gen_power(pm_model) _PM.variable_branch_power(pm_model) _PM.variable_dcline_power(pm_model) + _PM.variable_storage_power(pm_model) # PMD(Distribution) Variables _PMD.variable_mc_bus_voltage(pmd_model) @@ -84,6 +85,12 @@ function build_opfitd(pmitd::AbstractPowerModelITD) _PM.constraint_theta_ref(pm_model, i) end + for i in _PM.ids(pm_model, :storage) + _PM.constraint_storage_state(pm_model, i) + _PM.constraint_storage_complementarity_nl(pm_model, i) + _PM.constraint_storage_losses(pm_model, i) + _PM.constraint_storage_thermal_limit(pm_model, i) + end # PM branches for i in _PM.ids(pm_model, :branch) @@ -200,6 +207,7 @@ function build_opfitd(pmitd::AbstractIVRPowerModelITD) _PM.variable_branch_current(pm_model) _PM.variable_gen_current(pm_model) _PM.variable_dcline_current(pm_model) + _PM.variable_storage_power(pm_model) # PMD(Distribution) Variables _PMD.variable_mc_bus_voltage(pmd_model) @@ -218,6 +226,13 @@ function build_opfitd(pmitd::AbstractIVRPowerModelITD) _PM.constraint_theta_ref(pm_model, i) end + for i in _PM.ids(pm_model, :storage) + _PM.constraint_storage_state(pm_model, i) + _PM.constraint_storage_complementarity_nl(pm_model, i) + _PM.constraint_storage_losses(pm_model, i) + _PM.constraint_storage_thermal_limit(pm_model, i) + end + for i in _PM.ids(pm_model, :branch) _PM.constraint_current_from(pm_model, i) _PM.constraint_current_to(pm_model, i) @@ -322,7 +337,7 @@ function build_opfitd(pmitd::AbstractBFPowerModelITD) _PM.variable_branch_power(pm_model) _PM.variable_branch_current(pm_model) _PM.variable_dcline_power(pm_model) - + _PM.variable_storage_power(pm_model) # PMD(Distribution) Variables _PMD.variable_mc_bus_voltage(pmd_model) @@ -345,6 +360,12 @@ function build_opfitd(pmitd::AbstractBFPowerModelITD) _PM.constraint_theta_ref(pm_model, i) end + for i in _PM.ids(pm_model, :storage) + _PM.constraint_storage_state(pm_model, i) + _PM.constraint_storage_complementarity_nl(pm_model, i) + _PM.constraint_storage_losses(pm_model, i) + _PM.constraint_storage_thermal_limit(pm_model, i) + end # PM branches for i in _PM.ids(pm_model, :branch) @@ -464,6 +485,7 @@ function build_opfitd(pmitd::AbstractLNLBFPowerModelITD) _PM.variable_gen_power(pm_model) _PM.variable_branch_power(pm_model) _PM.variable_dcline_power(pm_model) + _PM.variable_storage_power(pm_model) # PMD(Distribution) Variables _PMD.variable_mc_bus_voltage(pmd_model) @@ -486,6 +508,12 @@ function build_opfitd(pmitd::AbstractLNLBFPowerModelITD) _PM.constraint_theta_ref(pm_model, i) end + for i in _PM.ids(pm_model, :storage) + _PM.constraint_storage_state(pm_model, i) + _PM.constraint_storage_complementarity_nl(pm_model, i) + _PM.constraint_storage_losses(pm_model, i) + _PM.constraint_storage_thermal_limit(pm_model, i) + end # PM branches for i in _PM.ids(pm_model, :branch) @@ -607,7 +635,7 @@ function build_mn_opfitd(pmitd::AbstractPowerModelITD) _PM.variable_gen_power(pm_model, nw=n) _PM.variable_branch_power(pm_model, nw=n) _PM.variable_dcline_power(pm_model, nw=n) - _PM.variable_storage_power_mi(pm_model, nw=n) + _PM.variable_storage_power(pm_model, nw=n) # PMD(Distribution) Variables _PMD.variable_mc_bus_voltage(pmd_model; nw=n) @@ -630,7 +658,7 @@ function build_mn_opfitd(pmitd::AbstractPowerModelITD) end for i in _PM.ids(pm_model, :storage, nw=n) - _PM.constraint_storage_complementarity_mi(pm_model, i, nw=n) + _PM.constraint_storage_complementarity_nl(pm_model, i, nw=n) _PM.constraint_storage_losses(pm_model, i, nw=n) _PM.constraint_storage_thermal_limit(pm_model, i, nw=n) end @@ -785,7 +813,7 @@ function build_mn_opfitd(pmitd::AbstractIVRPowerModelITD) _PM.variable_branch_current(pm_model, nw=n) _PM.variable_gen_current(pm_model, nw=n) _PM.variable_dcline_current(pm_model, nw=n) - _PM.variable_storage_power_mi(pm_model, nw=n) + _PM.variable_storage_power(pm_model, nw=n) # PMD(Distribution) Variables _PMD.variable_mc_bus_voltage(pmd_model; nw=n) @@ -805,7 +833,7 @@ function build_mn_opfitd(pmitd::AbstractIVRPowerModelITD) end for i in _PM.ids(pm_model, :storage, nw=n) - _PM.constraint_storage_complementarity_mi(pm_model, i, nw=n) + _PM.constraint_storage_complementarity_nl(pm_model, i, nw=n) _PM.constraint_storage_losses(pm_model, i, nw=n) _PM.constraint_storage_thermal_limit(pm_model, i, nw=n) end @@ -955,7 +983,7 @@ function build_mn_opfitd(pmitd::AbstractBFPowerModelITD) _PM.variable_branch_power(pm_model, nw=n) _PM.variable_branch_current(pm_model, nw=n) _PM.variable_dcline_power(pm_model, nw=n) - _PM.variable_storage_power_mi(pm_model, nw=n) + _PM.variable_storage_power(pm_model, nw=n) # PMD(Distribution) Variables _PMD.variable_mc_bus_voltage(pmd_model; nw=n) @@ -979,7 +1007,7 @@ function build_mn_opfitd(pmitd::AbstractBFPowerModelITD) end for i in _PM.ids(pm_model, :storage, nw=n) - _PM.constraint_storage_complementarity_mi(pm_model, i, nw=n) + _PM.constraint_storage_complementarity_nl(pm_model, i, nw=n) _PM.constraint_storage_losses(pm_model, i, nw=n) _PM.constraint_storage_thermal_limit(pm_model, i, nw=n) end @@ -1132,7 +1160,7 @@ function build_mn_opfitd(pmitd::AbstractLNLBFPowerModelITD) _PM.variable_gen_power(pm_model, nw=n) _PM.variable_branch_power(pm_model, nw=n) _PM.variable_dcline_power(pm_model, nw=n) - _PM.variable_storage_power_mi(pm_model, nw=n) + _PM.variable_storage_power(pm_model, nw=n) # PMD(Distribution) Variables _PMD.variable_mc_bus_voltage(pmd_model; nw=n) @@ -1156,7 +1184,7 @@ function build_mn_opfitd(pmitd::AbstractLNLBFPowerModelITD) end for i in _PM.ids(pm_model, :storage, nw=n) - _PM.constraint_storage_complementarity_mi(pm_model, i, nw=n) + _PM.constraint_storage_complementarity_nl(pm_model, i, nw=n) _PM.constraint_storage_losses(pm_model, i, nw=n) _PM.constraint_storage_thermal_limit(pm_model, i, nw=n) end diff --git a/src/prob/opfitd_oltc.jl b/src/prob/opfitd_oltc.jl index cbbd9c0..fb26d55 100644 --- a/src/prob/opfitd_oltc.jl +++ b/src/prob/opfitd_oltc.jl @@ -62,6 +62,7 @@ function build_opfitd_oltc(pmitd::AbstractPowerModelITD) _PM.variable_gen_power(pm_model) _PM.variable_branch_power(pm_model) _PM.variable_dcline_power(pm_model) + _PM.variable_storage_power(pm_model) # PMD(Distribution) Variables _PMD.variable_mc_bus_voltage(pmd_model) @@ -84,6 +85,13 @@ function build_opfitd_oltc(pmitd::AbstractPowerModelITD) _PM.constraint_theta_ref(pm_model, i) end + for i in _PM.ids(pm_model, :storage) + _PM.constraint_storage_state(pm_model, i) + _PM.constraint_storage_complementarity_nl(pm_model, i) + _PM.constraint_storage_losses(pm_model, i) + _PM.constraint_storage_thermal_limit(pm_model, i) + end + # PM branches for i in _PM.ids(pm_model, :branch) @@ -200,6 +208,7 @@ function build_opfitd_oltc(pmitd::AbstractBFPowerModelITD) _PM.variable_branch_power(pm_model) _PM.variable_branch_current(pm_model) _PM.variable_dcline_power(pm_model) + _PM.variable_storage_power(pm_model) # PMD(Distribution) Variables _PMD.variable_mc_bus_voltage(pmd_model) @@ -223,6 +232,12 @@ function build_opfitd_oltc(pmitd::AbstractBFPowerModelITD) _PM.constraint_theta_ref(pm_model, i) end + for i in _PM.ids(pm_model, :storage) + _PM.constraint_storage_state(pm_model, i) + _PM.constraint_storage_complementarity_nl(pm_model, i) + _PM.constraint_storage_losses(pm_model, i) + _PM.constraint_storage_thermal_limit(pm_model, i) + end # PM branches for i in _PM.ids(pm_model, :branch) @@ -341,6 +356,7 @@ function build_opfitd_oltc(pmitd::AbstractLNLBFPowerModelITD) _PM.variable_gen_power(pm_model) _PM.variable_branch_power(pm_model) _PM.variable_dcline_power(pm_model) + _PM.variable_storage_power(pm_model) # PMD(Distribution) Variables _PMD.variable_mc_bus_voltage(pmd_model) @@ -364,6 +380,12 @@ function build_opfitd_oltc(pmitd::AbstractLNLBFPowerModelITD) _PM.constraint_theta_ref(pm_model, i) end + for i in _PM.ids(pm_model, :storage) + _PM.constraint_storage_state(pm_model, i) + _PM.constraint_storage_complementarity_nl(pm_model, i) + _PM.constraint_storage_losses(pm_model, i) + _PM.constraint_storage_thermal_limit(pm_model, i) + end # PM branches for i in _PM.ids(pm_model, :branch) @@ -482,7 +504,7 @@ function build_mn_opfitd_oltc(pmitd::AbstractPowerModelITD) _PM.variable_gen_power(pm_model, nw=n) _PM.variable_branch_power(pm_model, nw=n) _PM.variable_dcline_power(pm_model, nw=n) - _PM.variable_storage_power_mi(pm_model, nw=n) + _PM.variable_storage_power(pm_model, nw=n) # PMD(Distribution) Variables _PMD.variable_mc_bus_voltage(pmd_model; nw=n) @@ -506,7 +528,7 @@ function build_mn_opfitd_oltc(pmitd::AbstractPowerModelITD) end for i in _PM.ids(pm_model, :storage, nw=n) - _PM.constraint_storage_complementarity_mi(pm_model, i, nw=n) + _PM.constraint_storage_complementarity_nl(pm_model, i, nw=n) _PM.constraint_storage_losses(pm_model, i, nw=n) _PM.constraint_storage_thermal_limit(pm_model, i, nw=n) end @@ -659,7 +681,7 @@ function build_mn_opfitd_oltc(pmitd::AbstractBFPowerModelITD) _PM.variable_branch_power(pm_model, nw=n) _PM.variable_branch_current(pm_model, nw=n) _PM.variable_dcline_power(pm_model, nw=n) - _PM.variable_storage_power_mi(pm_model, nw=n) + _PM.variable_storage_power(pm_model, nw=n) # PMD(Distribution) Variables _PMD.variable_mc_bus_voltage(pmd_model; nw=n) @@ -684,7 +706,7 @@ function build_mn_opfitd_oltc(pmitd::AbstractBFPowerModelITD) end for i in _PM.ids(pm_model, :storage, nw=n) - _PM.constraint_storage_complementarity_mi(pm_model, i, nw=n) + _PM.constraint_storage_complementarity_nl(pm_model, i, nw=n) _PM.constraint_storage_losses(pm_model, i, nw=n) _PM.constraint_storage_thermal_limit(pm_model, i, nw=n) end @@ -836,7 +858,7 @@ function build_mn_opfitd_oltc(pmitd::AbstractLNLBFPowerModelITD) _PM.variable_gen_power(pm_model, nw=n) _PM.variable_branch_power(pm_model, nw=n) _PM.variable_dcline_power(pm_model, nw=n) - _PM.variable_storage_power_mi(pm_model, nw=n) + _PM.variable_storage_power(pm_model, nw=n) # PMD(Distribution) Variables _PMD.variable_mc_bus_voltage(pmd_model; nw=n) @@ -861,7 +883,7 @@ function build_mn_opfitd_oltc(pmitd::AbstractLNLBFPowerModelITD) end for i in _PM.ids(pm_model, :storage, nw=n) - _PM.constraint_storage_complementarity_mi(pm_model, i, nw=n) + _PM.constraint_storage_complementarity_nl(pm_model, i, nw=n) _PM.constraint_storage_losses(pm_model, i, nw=n) _PM.constraint_storage_thermal_limit(pm_model, i, nw=n) end diff --git a/src/prob/opfitd_storage.jl b/src/prob/opfitd_storage.jl index 9f7cbe2..906d1ef 100755 --- a/src/prob/opfitd_storage.jl +++ b/src/prob/opfitd_storage.jl @@ -19,6 +19,14 @@ Solve Integrated T&D Optimal Power Flow with Storage OPF Dispatch. """ function solve_opfitd_storage(pm_file, pmd_file, pmitd_file, pmitd_type, optimizer; solution_processors::Vector{<:Function}=Function[], pmitd_ref_extensions::Vector{<:Function}=Vector{Function}([]), eng2math_passthrough::Dict{String,Vector{String}}=Dict{String,Vector{String}}(), make_si::Bool=true, auto_rename::Bool=false, solution_model::String="eng", kwargs...) + + if isempty(eng2math_passthrough) + eng2math_passthrough = Dict("storage"=>["cost"]) # by default, pass the eng2math passthrough + else + eng2math_pass_strg = "storage"=>["cost"] + push!(eng2math_passthrough, eng2math_pass_strg) + end + return solve_model(pm_file, pmd_file, pmitd_file, pmitd_type, optimizer, build_opfitd_storage; solution_processors=solution_processors, pmitd_ref_extensions=pmitd_ref_extensions, eng2math_passthrough=eng2math_passthrough, make_si=make_si, auto_rename=auto_rename, solution_model=solution_model, kwargs...) end @@ -42,6 +50,14 @@ end Solve Multinetwork Integrated T&D Optimal Power Flow with Storage OPF Dispatch. """ function solve_mn_opfitd_storage(pm_file, pmd_file, pmitd_file, pmitd_type, optimizer; solution_processors::Vector{<:Function}=Function[], pmitd_ref_extensions::Vector{<:Function}=Vector{Function}([]), eng2math_passthrough::Dict{String,Vector{String}}=Dict{String,Vector{String}}(), make_si::Bool=true, auto_rename::Bool=false, solution_model::String="eng", kwargs...) + + if isempty(eng2math_passthrough) + eng2math_passthrough = Dict("storage"=>["cost"]) # by default, pass the eng2math passthrough + else + eng2math_pass_strg = "storage"=>["cost"] + push!(eng2math_passthrough, eng2math_pass_strg) + end + return solve_model(pm_file, pmd_file, pmitd_file, pmitd_type, optimizer, build_mn_opfitd_storage; multinetwork=true, solution_processors=solution_processors, pmitd_ref_extensions=pmitd_ref_extensions, eng2math_passthrough=eng2math_passthrough, make_si=make_si, auto_rename=auto_rename, solution_model=solution_model, kwargs...) end @@ -63,6 +79,7 @@ function build_opfitd_storage(pmitd::AbstractPowerModelITD) _PM.variable_gen_power(pm_model) _PM.variable_branch_power(pm_model) _PM.variable_dcline_power(pm_model) + _PM.variable_storage_power(pm_model) # PMD(Distribution) Variables _PMD.variable_mc_bus_voltage(pmd_model) @@ -84,6 +101,12 @@ function build_opfitd_storage(pmitd::AbstractPowerModelITD) _PM.constraint_theta_ref(pm_model, i) end + for i in _PM.ids(pm_model, :storage) + _PM.constraint_storage_state(pm_model, i) + _PM.constraint_storage_complementarity_nl(pm_model, i) + _PM.constraint_storage_losses(pm_model, i) + _PM.constraint_storage_thermal_limit(pm_model, i) + end # PM branches for i in _PM.ids(pm_model, :branch) @@ -200,6 +223,7 @@ function build_opfitd_storage(pmitd::AbstractIVRPowerModelITD) _PM.variable_branch_current(pm_model) _PM.variable_gen_current(pm_model) _PM.variable_dcline_current(pm_model) + _PM.variable_storage_power(pm_model) # PMD(Distribution) Variables _PMD.variable_mc_bus_voltage(pmd_model) @@ -218,6 +242,13 @@ function build_opfitd_storage(pmitd::AbstractIVRPowerModelITD) _PM.constraint_theta_ref(pm_model, i) end + for i in _PM.ids(pm_model, :storage) + _PM.constraint_storage_state(pm_model, i) + _PM.constraint_storage_complementarity_nl(pm_model, i) + _PM.constraint_storage_losses(pm_model, i) + _PM.constraint_storage_thermal_limit(pm_model, i) + end + for i in _PM.ids(pm_model, :branch) _PM.constraint_current_from(pm_model, i) _PM.constraint_current_to(pm_model, i) @@ -322,6 +353,7 @@ function build_opfitd_storage(pmitd::AbstractBFPowerModelITD) _PM.variable_branch_power(pm_model) _PM.variable_branch_current(pm_model) _PM.variable_dcline_power(pm_model) + _PM.variable_storage_power(pm_model) # PMD(Distribution) Variables @@ -345,6 +377,12 @@ function build_opfitd_storage(pmitd::AbstractBFPowerModelITD) _PM.constraint_theta_ref(pm_model, i) end + for i in _PM.ids(pm_model, :storage) + _PM.constraint_storage_state(pm_model, i) + _PM.constraint_storage_complementarity_nl(pm_model, i) + _PM.constraint_storage_losses(pm_model, i) + _PM.constraint_storage_thermal_limit(pm_model, i) + end # PM branches for i in _PM.ids(pm_model, :branch) @@ -464,6 +502,8 @@ function build_opfitd_storage(pmitd::AbstractLNLBFPowerModelITD) _PM.variable_gen_power(pm_model) _PM.variable_branch_power(pm_model) _PM.variable_dcline_power(pm_model) + _PM.variable_storage_power(pm_model) + # PMD(Distribution) Variables _PMD.variable_mc_bus_voltage(pmd_model) @@ -486,6 +526,12 @@ function build_opfitd_storage(pmitd::AbstractLNLBFPowerModelITD) _PM.constraint_theta_ref(pm_model, i) end + for i in _PM.ids(pm_model, :storage) + _PM.constraint_storage_state(pm_model, i) + _PM.constraint_storage_complementarity_nl(pm_model, i) + _PM.constraint_storage_losses(pm_model, i) + _PM.constraint_storage_thermal_limit(pm_model, i) + end # PM branches for i in _PM.ids(pm_model, :branch) @@ -607,7 +653,7 @@ function build_mn_opfitd_storage(pmitd::AbstractPowerModelITD) _PM.variable_gen_power(pm_model, nw=n) _PM.variable_branch_power(pm_model, nw=n) _PM.variable_dcline_power(pm_model, nw=n) - _PM.variable_storage_power_mi(pm_model, nw=n) + _PM.variable_storage_power(pm_model, nw=n) # PMD(Distribution) Variables _PMD.variable_mc_bus_voltage(pmd_model; nw=n) @@ -630,7 +676,7 @@ function build_mn_opfitd_storage(pmitd::AbstractPowerModelITD) end for i in _PM.ids(pm_model, :storage, nw=n) - _PM.constraint_storage_complementarity_mi(pm_model, i, nw=n) + _PM.constraint_storage_complementarity_nl(pm_model, i, nw=n) _PM.constraint_storage_losses(pm_model, i, nw=n) _PM.constraint_storage_thermal_limit(pm_model, i, nw=n) end @@ -785,7 +831,7 @@ function build_mn_opfitd_storage(pmitd::AbstractIVRPowerModelITD) _PM.variable_branch_current(pm_model, nw=n) _PM.variable_gen_current(pm_model, nw=n) _PM.variable_dcline_current(pm_model, nw=n) - _PM.variable_storage_power_mi(pm_model, nw=n) + _PM.variable_storage_power(pm_model, nw=n) # PMD(Distribution) Variables _PMD.variable_mc_bus_voltage(pmd_model; nw=n) @@ -805,7 +851,7 @@ function build_mn_opfitd_storage(pmitd::AbstractIVRPowerModelITD) end for i in _PM.ids(pm_model, :storage, nw=n) - _PM.constraint_storage_complementarity_mi(pm_model, i, nw=n) + _PM.constraint_storage_complementarity_nl(pm_model, i, nw=n) _PM.constraint_storage_losses(pm_model, i, nw=n) _PM.constraint_storage_thermal_limit(pm_model, i, nw=n) end @@ -955,7 +1001,7 @@ function build_mn_opfitd_storage(pmitd::AbstractBFPowerModelITD) _PM.variable_branch_power(pm_model, nw=n) _PM.variable_branch_current(pm_model, nw=n) _PM.variable_dcline_power(pm_model, nw=n) - _PM.variable_storage_power_mi(pm_model, nw=n) + _PM.variable_storage_power(pm_model, nw=n) # PMD(Distribution) Variables _PMD.variable_mc_bus_voltage(pmd_model; nw=n) @@ -979,7 +1025,7 @@ function build_mn_opfitd_storage(pmitd::AbstractBFPowerModelITD) end for i in _PM.ids(pm_model, :storage, nw=n) - _PM.constraint_storage_complementarity_mi(pm_model, i, nw=n) + _PM.constraint_storage_complementarity_nl(pm_model, i, nw=n) _PM.constraint_storage_losses(pm_model, i, nw=n) _PM.constraint_storage_thermal_limit(pm_model, i, nw=n) end @@ -1132,7 +1178,7 @@ function build_mn_opfitd_storage(pmitd::AbstractLNLBFPowerModelITD) _PM.variable_gen_power(pm_model, nw=n) _PM.variable_branch_power(pm_model, nw=n) _PM.variable_dcline_power(pm_model, nw=n) - _PM.variable_storage_power_mi(pm_model, nw=n) + _PM.variable_storage_power(pm_model, nw=n) # PMD(Distribution) Variables _PMD.variable_mc_bus_voltage(pmd_model; nw=n) @@ -1156,7 +1202,7 @@ function build_mn_opfitd_storage(pmitd::AbstractLNLBFPowerModelITD) end for i in _PM.ids(pm_model, :storage, nw=n) - _PM.constraint_storage_complementarity_mi(pm_model, i, nw=n) + _PM.constraint_storage_complementarity_nl(pm_model, i, nw=n) _PM.constraint_storage_losses(pm_model, i, nw=n) _PM.constraint_storage_thermal_limit(pm_model, i, nw=n) end diff --git a/test/data/transmission/case5_withload_strg.m b/test/data/transmission/case5_withload_strg.m new file mode 100755 index 0000000..debc219 --- /dev/null +++ b/test/data/transmission/case5_withload_strg.m @@ -0,0 +1,64 @@ +% used in tests of, +% - non-contiguous bus ids +% - tranformer orentation swapping +% - dual values +% - clipping cost functions using ncost +% - linear objective function +% - bus type correction + +function mpc = case5 +mpc.version = '2'; +mpc.baseMVA = 100.0; + +%% bus data +% bus_i type Pd Qd Gs Bs area Vm Va baseKV zone Vmax Vmin +mpc.bus = [ + 1 2 0.0 0.0 0.0 0.0 1 1.07762 2.80377 230.0 1 1.10000 0.90000; + 2 1 300.0 98.61 0.0 0.0 1 1.08407 -0.73465 230.0 1 1.10000 0.90000; + 3 2 300.0 98.61 0.0 0.0 1 1.10000 -0.55972 230.0 1 1.10000 0.90000; + 4 3 390.0 131.47 0.0 0.0 1 1.06414 0.00000 230.0 1 1.10000 0.90000; + 5 1 8.0 1.2 0.0 0.0 1 1.00000 0.00000 230.0 1 1.10000 0.90000; + 10 2 0.0 0.0 0.0 0.0 1 1.06907 3.59033 230.0 1 1.10000 0.90000; +]; + +%% generator data +% bus Pg Qg Qmax Qmin Vg mBase status Pmax Pmin +mpc.gen = [ + 1 40.0 30.0 30.0 -30.0 1.07762 100.0 1 40.0 0.0; + 1 170.0 127.5 127.5 -127.5 1.07762 100.0 1 170.0 0.0; + 3 324.498 390.0 390.0 -390.0 1.1 100.0 1 520.0 0.0; + 4 0.0 -10.802 150.0 -150.0 1.06414 100.0 1 200.0 0.0; + 10 470.694 -165.039 450.0 -450.0 1.06907 100.0 1 600.0 0.0; +]; + +%% generator cost data +% 2 startup shutdown n c(n-1) ... c0 +mpc.gencost = [ + 2 0.0 0.0 3 0.000000 14.000000 0.000000 2.000000; + 2 0.0 0.0 3 0.000000 15.000000 0.000000 2.000000; + 2 0.0 0.0 3 0.000000 30.000000 0.000000 2.000000; + 2 0.0 0.0 3 0.000000 40.000000 0.000000 2.000000; + 2 0.0 0.0 3 0.000000 10.000000 0.000000 2.000000; +]; + +%% branch data +% fbus tbus r x b rateA rateB rateC ratio angle status angmin angmax +mpc.branch = [ + 1 2 0.00281 0.0281 0.00712 400.0 400.0 400.0 0.0 0.0 1 -30.0 30.0; + 1 4 0.00304 0.0304 0.00658 426 426 426 0.0 0.0 1 -30.0 30.0; + 1 10 0.00064 0.0064 0.03126 426 426 426 0.0 0.0 1 -30.0 30.0; + 2 3 0.00108 0.0108 0.01852 426 426 426 0.0 0.0 1 -30.0 30.0; + 3 4 0.00297 0.0297 0.00674 426 426 426 1.05 1.0 1 -30.0 30.0; + 4 10 0.00297 0.0297 0.00674 240.0 240.0 240.0 0.0 0.0 1 -30.0 30.0; + 2 5 0.00297 0.0297 0.00674 426 426 426 0.0 0.0 1 -30.0 30.0; +]; + +% hours +mpc.time_elapsed = 1.0 + +%% storage data +% storage_bus ps qs energy energy_rating charge_rating discharge_rating charge_efficiency discharge_efficiency thermal_rating qmin qmax r x p_loss q_loss status +mpc.storage = [ + 3 0.0 0.0 20.0 100.0 50.0 70.0 0.8 0.9 100.0 -50.0 70.0 0.1 0.0 0.0 0.0 1; + 10 0.0 0.0 30.0 100.0 50.0 70.0 0.9 0.8 100.0 -50.0 70.0 0.1 0.0 0.0 0.0 1; +]; diff --git a/test/opfitd_pass.jl b/test/opfitd_pass.jl deleted file mode 100644 index e6f6996..0000000 --- a/test/opfitd_pass.jl +++ /dev/null @@ -1,102 +0,0 @@ -@info "running ITD eng2math_passthrough tests" - -@testset "test/opfitd_pass.jl" begin - - @testset "Check that eng2math_passthrough value is being added to the instantiated model: " begin - pm_file = joinpath(dirname(trans_path), "case5_withload.m") - pmd_file = joinpath(dirname(dist_path), "case3_balanced_withBattery.dss") - pmitd_file = joinpath(dirname(bound_path), "case5_case3_bal_battery.json") - pmitd_type = NLPowerModelITD{ACRPowerModel, ACRUPowerModel} - pmitd_data = parse_files(pm_file, pmd_file, pmitd_file) - - # add cost to storages in PMD - for (st_name, st_data) in pmitd_data["it"]["pmd"]["storage"] - st_data["cost"] = 0.25 - end - - # instantiate model with eng2math_passthrough - eng2math_passthrough = Dict("storage"=>["cost"]) - pmitd_inst_model = instantiate_model(pmitd_data, pmitd_type, build_opfitd; eng2math_passthrough=eng2math_passthrough) - - # get storage reference - storage_ref = _IM.ref(pmitd_inst_model, _PMD.pmd_it_sym, nw=nw_id_default, :storage) - - # test that the "cost" value in storage exists. - @test storage_ref[1]["cost"] == 0.25 - end - - @testset "Check that eng2math_passthrough value is being added to the instantiated model (Multinetwork): " begin - pm_file = joinpath(dirname(trans_path), "case5_withload.m") - pmd_file = joinpath(dirname(dist_path), "case3_balanced_withBattery_mn.dss") - pmitd_file = joinpath(dirname(bound_path), "case5_case3_bal_battery_mn.json") - pmitd_type = NLPowerModelITD{ACRPowerModel, ACRUPowerModel} - pmitd_data = parse_files(pm_file, pmd_file, pmitd_file; multinetwork=true) - - # add cost to storages in PMD - for (nw_id, nw_data) in pmitd_data["it"]["pmd"]["nw"] - for (st_name, st_data) in nw_data["storage"] - st_data["cost"] = 0.25 - end - end - - # instantiate model with eng2math_passthrough - eng2math_passthrough = Dict("storage"=>["cost"]) - pmitd_inst_model = instantiate_model(pmitd_data, pmitd_type, build_mn_opfitd; multinetwork=true, eng2math_passthrough=eng2math_passthrough) - - # get storage reference from nw=4 - storage_ref_nw4 = _IM.ref(pmitd_inst_model, _PMD.pmd_it_sym, nw=4, :storage) - - # test that the "cost" value in storage nw=4 exists. - @test storage_ref_nw4[1]["cost"] == 0.25 - end - - @testset "solve_model (build_opfitd_storage): Balanced case5-case3 With Battery ACP-ACPU " begin - pm_file = joinpath(dirname(trans_path), "case5_withload.m") - pmd_file = joinpath(dirname(dist_path), "case3_balanced_withBattery.dss") - pmitd_file = joinpath(dirname(bound_path), "case5_case3_bal_battery.json") - pmitd_type = NLPowerModelITD{ACPPowerModel, ACPUPowerModel} - pmitd_data = parse_files(pm_file, pmd_file, pmitd_file) - - # cost to assing to energy storage - # Units $/pu, to convert from your wanted $/MWh just multiply by MVABase (e.g., 2.5 $/MWh x 100 MWh/1pu = 250 $/pu) - strg_cost = 250 - - # add cost to storages in PMD - for (st_name, st_data) in pmitd_data["it"]["pmd"]["storage"] - st_data["cost"] = strg_cost - end - - # eng2math_passthrough - eng2math_passthrough = Dict("storage"=>["cost"]) - - # with storage cost problem - pmitd_result_strg = solve_model(pmitd_data, pmitd_type, ipopt, build_opfitd_storage; eng2math_passthrough=eng2math_passthrough) - @test isapprox(pmitd_result_strg["objective"], 17977.4682739707+(pmitd_result_strg["solution"]["it"]["pmd"]["storage"]["3bus_bal_battery.s1"]["sd"]*strg_cost)/100000; atol = 1e-4) - - end - - @testset "solve_model (build_mn_opfitd_storage): Multinetwork Balanced case5-case3 With Battery ACP-ACPU " begin - pm_file = joinpath(dirname(trans_path), "case5_withload.m") - pmd_file = joinpath(dirname(dist_path), "case3_balanced_withBattery_mn.dss") - pmitd_file = joinpath(dirname(bound_path), "case5_case3_bal_battery_mn.json") - pmitd_type = NLPowerModelITD{ACPPowerModel, ACPUPowerModel} - pmitd_data = parse_files(pm_file, pmd_file, pmitd_file; multinetwork=true) - - # cost to assing to energy storage - # Units $/pu, to convert from your wanted $/MWh just multiply by MVABase (e.g., 2.5 $/MWh x 100 MWh/1pu = 250 $/pu) - strg_cost = 250 - - # add cost to storages in PMD - for (nw_id, nw_data) in pmitd_data["it"]["pmd"]["nw"] - for (st_name, st_data) in nw_data["storage"] - st_data["cost"] = strg_cost - end - end - - # instantiate model with eng2math_passthrough - eng2math_passthrough = Dict("storage"=>["cost"]) - pmitd_result_strg = solve_model(pmitd_data, pmitd_type, ipopt, build_mn_opfitd_storage; multinetwork=true, eng2math_passthrough=eng2math_passthrough) - @test isapprox(pmitd_result_strg["objective"], 71226.94293293+0.12500248519; atol = 1e-4) - end - -end diff --git a/test/opfitd_storage.jl b/test/opfitd_storage.jl new file mode 100644 index 0000000..bbb7cc3 --- /dev/null +++ b/test/opfitd_storage.jl @@ -0,0 +1,140 @@ +@info "running ITD storage tests" + +@testset "test/opfitd_pass.jl" begin + + + # @testset "Check that eng2math_passthrough value is being added to the instantiated model: " begin + # pm_file = joinpath(dirname(trans_path), "case5_withload_strg.m") + # # pm_file = joinpath(dirname(trans_path), "case5_withload.m") + # pmd_file = joinpath(dirname(dist_path), "case3_balanced_withBattery.dss") + # pmitd_file = joinpath(dirname(bound_path), "case5_case3_bal_battery.json") + # pmitd_type = NLPowerModelITD{ACRPowerModel, ACRUPowerModel} + # # pmitd_data = parse_files(pm_file, pmd_file, pmitd_file) + + # # @info "$(pmitd_data)" + + # pmitd_result_strg = solve_opfitd_storage(pm_file, pmd_file, pmitd_file, pmitd_type, ipopt) + + + # end + + + # @testset "solve_model (build_mn_opfitd_storage): Multinetwork Balanced case5-case3 With Battery ACP-ACPU " begin + # # pm_file = joinpath(dirname(trans_path), "case5_withload_strg.m") + # pm_file = joinpath(dirname(trans_path), "case5_withload.m") + # pmd_file = joinpath(dirname(dist_path), "case3_balanced_withBattery_mn.dss") + # pmitd_file = joinpath(dirname(bound_path), "case5_case3_bal_battery_mn.json") + # pmitd_type = NLPowerModelITD{ACPPowerModel, ACPUPowerModel} + # pmitd_data = parse_files(pm_file, pmd_file, pmitd_file; multinetwork=true) + + # # @info "$(pmitd_data)" + + # pmitd_result_strg = solve_mn_opfitd_storage(pm_file, pmd_file, pmitd_file, pmitd_type, ipopt) + + # end + + + #--------------------------------------------------------------------------------------------------------------- + + + # @testset "Check that eng2math_passthrough value is being added to the instantiated model: " begin + # pm_file = joinpath(dirname(trans_path), "case5_withload.m") + # pmd_file = joinpath(dirname(dist_path), "case3_balanced_withBattery.dss") + # pmitd_file = joinpath(dirname(bound_path), "case5_case3_bal_battery.json") + # pmitd_type = NLPowerModelITD{ACRPowerModel, ACRUPowerModel} + # pmitd_data = parse_files(pm_file, pmd_file, pmitd_file) + + # # add cost to storages in PMD + # for (st_name, st_data) in pmitd_data["it"]["pmd"]["storage"] + # st_data["cost"] = 0.25 + # end + + # # instantiate model with eng2math_passthrough + # eng2math_passthrough = Dict("storage"=>["cost"]) + # pmitd_inst_model = instantiate_model(pmitd_data, pmitd_type, build_opfitd; eng2math_passthrough=eng2math_passthrough) + + # # get storage reference + # storage_ref = _IM.ref(pmitd_inst_model, _PMD.pmd_it_sym, nw=nw_id_default, :storage) + + # # test that the "cost" value in storage exists. + # @test storage_ref[1]["cost"] == 0.25 + # end + + # @testset "Check that eng2math_passthrough value is being added to the instantiated model (Multinetwork): " begin + # pm_file = joinpath(dirname(trans_path), "case5_withload.m") + # pmd_file = joinpath(dirname(dist_path), "case3_balanced_withBattery_mn.dss") + # pmitd_file = joinpath(dirname(bound_path), "case5_case3_bal_battery_mn.json") + # pmitd_type = NLPowerModelITD{ACRPowerModel, ACRUPowerModel} + # pmitd_data = parse_files(pm_file, pmd_file, pmitd_file; multinetwork=true) + + # # add cost to storages in PMD + # for (nw_id, nw_data) in pmitd_data["it"]["pmd"]["nw"] + # for (st_name, st_data) in nw_data["storage"] + # st_data["cost"] = 0.25 + # end + # end + + # # instantiate model with eng2math_passthrough + # eng2math_passthrough = Dict("storage"=>["cost"]) + # pmitd_inst_model = instantiate_model(pmitd_data, pmitd_type, build_mn_opfitd; multinetwork=true, eng2math_passthrough=eng2math_passthrough) + + # # get storage reference from nw=4 + # storage_ref_nw4 = _IM.ref(pmitd_inst_model, _PMD.pmd_it_sym, nw=4, :storage) + + # # test that the "cost" value in storage nw=4 exists. + # @test storage_ref_nw4[1]["cost"] == 0.25 + # end + + # @testset "solve_model (build_opfitd_storage): Balanced case5-case3 With Battery ACP-ACPU " begin + # pm_file = joinpath(dirname(trans_path), "case5_withload.m") + # pmd_file = joinpath(dirname(dist_path), "case3_balanced_withBattery.dss") + # pmitd_file = joinpath(dirname(bound_path), "case5_case3_bal_battery.json") + # pmitd_type = NLPowerModelITD{ACPPowerModel, ACPUPowerModel} + # pmitd_data = parse_files(pm_file, pmd_file, pmitd_file) + + # # cost to assign to energy storage + # # Units $/pu, to convert from your wanted $/MWh just multiply by MVABase (e.g., 2.5 $/MWh x 100 MWh/1pu = 250 $/pu) + # strg_cost = 250 + + # # add cost to storages in PMD + # for (st_name, st_data) in pmitd_data["it"]["pmd"]["storage"] + # st_data["cost"] = strg_cost + # end + + # # eng2math_passthrough + # eng2math_passthrough = Dict("storage"=>["cost"]) + + # # with storage cost problem + # pmitd_result_strg = solve_model(pmitd_data, pmitd_type, ipopt, build_opfitd_storage; eng2math_passthrough=eng2math_passthrough) + # @test isapprox(pmitd_result_strg["objective"], 17977.48172498742+(pmitd_result_strg["solution"]["it"]["pmd"]["storage"]["3bus_bal_battery.s1"]["sd"]*strg_cost)/100000; atol = 1e-3) + + # end + + # @testset "solve_model (build_mn_opfitd_storage): Multinetwork Balanced case5-case3 With Battery ACP-ACPU " begin + # pm_file = joinpath(dirname(trans_path), "case5_withload.m") + # pmd_file = joinpath(dirname(dist_path), "case3_balanced_withBattery_mn.dss") + # pmitd_file = joinpath(dirname(bound_path), "case5_case3_bal_battery_mn.json") + # pmitd_type = NLPowerModelITD{ACPPowerModel, ACPUPowerModel} + # pmitd_data = parse_files(pm_file, pmd_file, pmitd_file; multinetwork=true) + + # # cost to assign to energy storage + # # Units $/pu, to convert from your wanted $/MWh just multiply by MVABase (e.g., 2.5 $/MWh x 100 MWh/1pu = 250 $/pu) + # strg_cost = 250 + + # # add cost to storages in PMD + # for (nw_id, nw_data) in pmitd_data["it"]["pmd"]["nw"] + # for (st_name, st_data) in nw_data["storage"] + # st_data["cost"] = strg_cost + # end + # end + + # # instantiate model with eng2math_passthrough + # eng2math_passthrough = Dict("storage"=>["cost"]) + + # # with storage cost problem + # pmitd_result_strg = solve_model(pmitd_data, pmitd_type, ipopt, build_mn_opfitd_storage; multinetwork=true, eng2math_passthrough=eng2math_passthrough) + # @test isapprox(pmitd_result_strg["objective"], 71226.99645541582+0.12500248519; atol = 1e-3) + + # end + +end diff --git a/test/runtests.jl b/test/runtests.jl index cfd0f23..2f7d27e 100755 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -23,23 +23,23 @@ scs_solver = optimizer_with_attributes(SCS.Optimizer, "verbose"=>0) PowerModelsITD.silence!() @testset "PowerModelsITD.jl" begin - include("io.jl") - include("base.jl") - include("data.jl") - include("autorename.jl") - include("opfitd.jl") - include("opfitd_duals.jl") - include("pfitd.jl") - include("opfitd_ms.jl") - include("pfitd_ms.jl") - include("transformations_opfitd.jl") - include("largescale_opfitd.jl") - include("opfitd_hybrids.jl") - include("pfitd_hybrids.jl") - include("opfitd_oltc.jl") - include("opfitd_mn.jl") - include("opfitd_oltc_mn.jl") - include("opfitd_dmld.jl") - include("opfitd_solution.jl") - include("opfitd_pass.jl") + # include("io.jl") + # include("base.jl") + # include("data.jl") + # include("autorename.jl") + # include("opfitd.jl") + # include("opfitd_duals.jl") + # include("pfitd.jl") + # include("opfitd_ms.jl") + # include("pfitd_ms.jl") + # include("transformations_opfitd.jl") + # include("largescale_opfitd.jl") + # include("opfitd_hybrids.jl") + # include("pfitd_hybrids.jl") + # include("opfitd_oltc.jl") + # include("opfitd_mn.jl") + # include("opfitd_oltc_mn.jl") + # include("opfitd_dmld.jl") + # include("opfitd_solution.jl") + include("opfitd_storage.jl") end From def840cecdf1e968d8ec24231e00e06c0c0e309f Mon Sep 17 00:00:00 2001 From: Juan Ospina Date: Tue, 14 Nov 2023 08:44:55 -0700 Subject: [PATCH 08/16] ADD: option to parse files and then directly run the solve_opfitd_storage function. --- CHANGELOG.md | 1 + src/prob/opfitd_storage.jl | 58 ++++++++++++++++++++++++++++++++++++++ test/opfitd_storage.jl | 36 ++++++++++++----------- 3 files changed, 79 insertions(+), 16 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 05c754d..71577c8 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ - Added the required variables and constraints for Transmission system-related storage devices to all problem specifications in `opfitd_storage.jl`. - Fixed storage variables and constraints being used in `opfitd` and `opfitd_oltc.jl` where the versions used were the `_mi` mixed integer versions. - Modified all objectives in `objective_storage.jl` to take into account the cost of discharging the storage devices in the transmission system. Also, added in-situ conversion for the PMD cost that converts the $/kWh -> $/pu cost, so that the user can provide the $/kWh in the `ENG` model, avoiding confusions. +- Added the option to parse files and use the parsed structure solve directly the optimization problem using `solve_opfitd_storage(..)` and `solve_mn_opfitd_storage(..)`. This allows users to parse the data, modify costs (or other parameters) and then run the solve functions without using the `solve_model(..)` function which requires users to explicitly use the `eng2math_passthrough = Dict("storage"=>["cost"])`. ## staged diff --git a/src/prob/opfitd_storage.jl b/src/prob/opfitd_storage.jl index 906d1ef..68cb74b 100755 --- a/src/prob/opfitd_storage.jl +++ b/src/prob/opfitd_storage.jl @@ -31,6 +31,35 @@ function solve_opfitd_storage(pm_file, pmd_file, pmitd_file, pmitd_type, optimiz end +""" + function solve_opfitd_storage( + pmitd_data::Dict{String,<:Any}, + pmitd_type, + optimizer; + solution_processors::Vector{<:Function}=Function[], + pmitd_ref_extensions::Vector{<:Function}=Vector{Function}([]), + eng2math_passthrough::Dict{String,Vector{String}}=Dict{String,Vector{String}}(), + make_si::Bool=true, + auto_rename::Bool=false, + solution_model::String="eng", + kwargs... + ) + +Solve Integrated T&D Optimal Power Flow with Storage OPF Dispatch. +""" +function solve_opfitd_storage(pmitd_data::Dict{String,<:Any}, pmitd_type, optimizer; solution_processors::Vector{<:Function}=Function[], pmitd_ref_extensions::Vector{<:Function}=Vector{Function}([]), eng2math_passthrough::Dict{String,Vector{String}}=Dict{String,Vector{String}}(), make_si::Bool=true, solution_model::String="eng", kwargs...) + + if isempty(eng2math_passthrough) + eng2math_passthrough = Dict("storage"=>["cost"]) # by default, pass the eng2math passthrough + else + eng2math_pass_strg = "storage"=>["cost"] + push!(eng2math_passthrough, eng2math_pass_strg) + end + + return solve_model(pmitd_data, pmitd_type, optimizer, build_opfitd_storage; solution_processors=solution_processors, pmitd_ref_extensions=pmitd_ref_extensions, eng2math_passthrough=eng2math_passthrough, make_si=make_si, solution_model=solution_model, kwargs...) +end + + """ function solve_mn_opfitd_storage( pm_file, @@ -62,6 +91,35 @@ function solve_mn_opfitd_storage(pm_file, pmd_file, pmitd_file, pmitd_type, opti end +""" + function solve_mn_opfitd_storage( + pmitd_data::Dict{String,<:Any} + pmitd_type, + optimizer; + solution_processors::Vector{<:Function}=Function[], + pmitd_ref_extensions::Vector{<:Function}=Vector{Function}([]), + eng2math_passthrough::Dict{String,Vector{String}}=Dict{String,Vector{String}}(), + make_si::Bool=true, + auto_rename::Bool=false, + solution_model::String="eng", + kwargs... + ) + +Solve Multinetwork Integrated T&D Optimal Power Flow with Storage OPF Dispatch. +""" +function solve_mn_opfitd_storage(pmitd_data::Dict{String,<:Any}, pmitd_type, optimizer; solution_processors::Vector{<:Function}=Function[], pmitd_ref_extensions::Vector{<:Function}=Vector{Function}([]), eng2math_passthrough::Dict{String,Vector{String}}=Dict{String,Vector{String}}(), make_si::Bool=true, solution_model::String="eng", kwargs...) + + if isempty(eng2math_passthrough) + eng2math_passthrough = Dict("storage"=>["cost"]) # by default, pass the eng2math passthrough + else + eng2math_pass_strg = "storage"=>["cost"] + push!(eng2math_passthrough, eng2math_pass_strg) + end + + return solve_model(pmitd_data, pmitd_type, optimizer, build_mn_opfitd_storage; multinetwork=true, solution_processors=solution_processors, pmitd_ref_extensions=pmitd_ref_extensions, eng2math_passthrough=eng2math_passthrough, make_si=make_si, solution_model=solution_model, kwargs...) +end + + """ function build_opfitd_storage( pmitd::AbstractPowerModelITD diff --git a/test/opfitd_storage.jl b/test/opfitd_storage.jl index bbb7cc3..bc5b2e1 100644 --- a/test/opfitd_storage.jl +++ b/test/opfitd_storage.jl @@ -4,34 +4,38 @@ # @testset "Check that eng2math_passthrough value is being added to the instantiated model: " begin - # pm_file = joinpath(dirname(trans_path), "case5_withload_strg.m") - # # pm_file = joinpath(dirname(trans_path), "case5_withload.m") + # # pm_file = joinpath(dirname(trans_path), "case5_withload_strg.m") + # pm_file = joinpath(dirname(trans_path), "case5_withload.m") # pmd_file = joinpath(dirname(dist_path), "case3_balanced_withBattery.dss") # pmitd_file = joinpath(dirname(bound_path), "case5_case3_bal_battery.json") # pmitd_type = NLPowerModelITD{ACRPowerModel, ACRUPowerModel} - # # pmitd_data = parse_files(pm_file, pmd_file, pmitd_file) - # # @info "$(pmitd_data)" - - # pmitd_result_strg = solve_opfitd_storage(pm_file, pmd_file, pmitd_file, pmitd_type, ipopt) + # # One way of doing + # # pmitd_result_strg = solve_opfitd_storage(pm_file, pmd_file, pmitd_file, pmitd_type, ipopt) + # # Other way of doing it + # pmitd_data = parse_files(pm_file, pmd_file, pmitd_file) + # pmitd_result_strg = solve_opfitd_storage(pmitd_data, pmitd_type, ipopt) # end - # @testset "solve_model (build_mn_opfitd_storage): Multinetwork Balanced case5-case3 With Battery ACP-ACPU " begin - # # pm_file = joinpath(dirname(trans_path), "case5_withload_strg.m") - # pm_file = joinpath(dirname(trans_path), "case5_withload.m") - # pmd_file = joinpath(dirname(dist_path), "case3_balanced_withBattery_mn.dss") - # pmitd_file = joinpath(dirname(bound_path), "case5_case3_bal_battery_mn.json") - # pmitd_type = NLPowerModelITD{ACPPowerModel, ACPUPowerModel} - # pmitd_data = parse_files(pm_file, pmd_file, pmitd_file; multinetwork=true) + @testset "solve_model (build_mn_opfitd_storage): Multinetwork Balanced case5-case3 With Battery ACP-ACPU " begin + # pm_file = joinpath(dirname(trans_path), "case5_withload_strg.m") + pm_file = joinpath(dirname(trans_path), "case5_withload.m") + pmd_file = joinpath(dirname(dist_path), "case3_balanced_withBattery_mn.dss") + pmitd_file = joinpath(dirname(bound_path), "case5_case3_bal_battery_mn.json") + pmitd_type = NLPowerModelITD{ACPPowerModel, ACPUPowerModel} - # # @info "$(pmitd_data)" + # One way + # pmitd_result_strg = solve_mn_opfitd_storage(pm_file, pmd_file, pmitd_file, pmitd_type, ipopt) - # pmitd_result_strg = solve_mn_opfitd_storage(pm_file, pmd_file, pmitd_file, pmitd_type, ipopt) + # Other way + pmitd_data = parse_files(pm_file, pmd_file, pmitd_file; multinetwork=true) + pmitd_result_strg = solve_mn_opfitd_storage(pmitd_data, pmitd_type, ipopt) - # end + + end #--------------------------------------------------------------------------------------------------------------- From 5ca564e2be87c831e8a121ba05b79f36dfe81076 Mon Sep 17 00:00:00 2001 From: Juan Ospina Date: Tue, 14 Nov 2023 10:26:20 -0700 Subject: [PATCH 09/16] ADD: option to parse files and use the parsed structure directly into any solve_x functions. --- CHANGELOG.md | 1 + README.md | 1 + src/prob/opfitd.jl | 40 ++++++++++++++++++ src/prob/opfitd_dmld.jl | 40 ++++++++++++++++++ src/prob/opfitd_oltc.jl | 40 ++++++++++++++++++ src/prob/opfitd_storage.jl | 2 - src/prob/pfitd.jl | 20 +++++++++ test/opfitd_storage.jl | 31 ++++++++------ test/runtests.jl | 3 +- test/solve_x.jl | 86 ++++++++++++++++++++++++++++++++++++++ 10 files changed, 248 insertions(+), 16 deletions(-) create mode 100644 test/solve_x.jl diff --git a/CHANGELOG.md b/CHANGELOG.md index 71577c8..3660e63 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ - Fixed storage variables and constraints being used in `opfitd` and `opfitd_oltc.jl` where the versions used were the `_mi` mixed integer versions. - Modified all objectives in `objective_storage.jl` to take into account the cost of discharging the storage devices in the transmission system. Also, added in-situ conversion for the PMD cost that converts the $/kWh -> $/pu cost, so that the user can provide the $/kWh in the `ENG` model, avoiding confusions. - Added the option to parse files and use the parsed structure solve directly the optimization problem using `solve_opfitd_storage(..)` and `solve_mn_opfitd_storage(..)`. This allows users to parse the data, modify costs (or other parameters) and then run the solve functions without using the `solve_model(..)` function which requires users to explicitly use the `eng2math_passthrough = Dict("storage"=>["cost"])`. +- Added option to parse files and use the parsed structure directly into any `solve_x(..)` functions. ## staged diff --git a/README.md b/README.md index bb474c3..ce261a7 100755 --- a/README.md +++ b/README.md @@ -10,6 +10,7 @@ - Integrated T&D Power Flow (pfitd) - Integrated T&D Optimal Power Flow (opfitd) +- Integrated T&D Optimal Power Flow with storage costs (opfitd_storage) - Integrated T&D Optimal Power Flow with on-load tap-changer (opfitd_oltc) - Integrated T&D Optimal power flow at transmission and minimum load delta at distribution system (opfitd_dmld) diff --git a/src/prob/opfitd.jl b/src/prob/opfitd.jl index 1950aa3..bfac232 100755 --- a/src/prob/opfitd.jl +++ b/src/prob/opfitd.jl @@ -23,6 +23,26 @@ function solve_opfitd(pm_file, pmd_file, pmitd_file, pmitd_type, optimizer; solu end +""" + function solve_opfitd( + pmitd_data::Dict{String,<:Any}, + pmitd_type, + optimizer; + solution_processors::Vector{<:Function}=Function[], + pmitd_ref_extensions::Vector{<:Function}=Vector{Function}([]), + eng2math_passthrough::Dict{String,Vector{String}}=Dict{String,Vector{String}}(), + make_si::Bool=true, + solution_model::String="eng", + kwargs... + ) + +Solve Integrated T&D Optimal Power Flow. +""" +function solve_opfitd(pmitd_data::Dict{String,<:Any}, pmitd_type, optimizer; solution_processors::Vector{<:Function}=Function[], pmitd_ref_extensions::Vector{<:Function}=Vector{Function}([]), eng2math_passthrough::Dict{String,Vector{String}}=Dict{String,Vector{String}}(), make_si::Bool=true, solution_model::String="eng", kwargs...) + return solve_model(pmitd_data, pmitd_type, optimizer, build_opfitd; solution_processors=solution_processors, pmitd_ref_extensions=pmitd_ref_extensions, eng2math_passthrough=eng2math_passthrough, make_si=make_si, solution_model=solution_model, kwargs...) +end + + """ function solve_mn_opfitd( pm_file, @@ -46,6 +66,26 @@ function solve_mn_opfitd(pm_file, pmd_file, pmitd_file, pmitd_type, optimizer; s end +""" + function solve_mn_opfitd( + pmitd_data::Dict{String,<:Any} + pmitd_type, + optimizer; + solution_processors::Vector{<:Function}=Function[], + pmitd_ref_extensions::Vector{<:Function}=Vector{Function}([]), + eng2math_passthrough::Dict{String,Vector{String}}=Dict{String,Vector{String}}(), + make_si::Bool=true, + solution_model::String="eng", + kwargs... + ) + +Solve Multinetwork Integrated T&D Optimal Power Flow. +""" +function solve_mn_opfitd(pmitd_data::Dict{String,<:Any}, pmitd_type, optimizer; solution_processors::Vector{<:Function}=Function[], pmitd_ref_extensions::Vector{<:Function}=Vector{Function}([]), eng2math_passthrough::Dict{String,Vector{String}}=Dict{String,Vector{String}}(), make_si::Bool=true, solution_model::String="eng", kwargs...) + return solve_model(pmitd_data, pmitd_type, optimizer, build_mn_opfitd; multinetwork=true, solution_processors=solution_processors, pmitd_ref_extensions=pmitd_ref_extensions, eng2math_passthrough=eng2math_passthrough, make_si=make_si, solution_model=solution_model, kwargs...) +end + + """ function build_opfitd( pmitd::AbstractPowerModelITD diff --git a/src/prob/opfitd_dmld.jl b/src/prob/opfitd_dmld.jl index 41106c0..794a103 100755 --- a/src/prob/opfitd_dmld.jl +++ b/src/prob/opfitd_dmld.jl @@ -23,6 +23,26 @@ function solve_dmld_opfitd(pm_file, pmd_file, pmitd_file, pmitd_type, optimizer; end +""" + function solve_dmld_opfitd( + pmitd_data::Dict{String,<:Any}, + pmitd_type, + optimizer; + solution_processors::Vector{<:Function}=Function[], + pmitd_ref_extensions::Vector{<:Function}=Vector{Function}([]), + eng2math_passthrough::Dict{String,Vector{String}}=Dict{String,Vector{String}}(), + make_si::Bool=true, + solution_model::String="eng", + kwargs... + ) + +Solve Integrated T&D Optimal Power Flow with minimum load delta (dmld). +""" +function solve_dmld_opfitd(pmitd_data::Dict{String,<:Any}, pmitd_type, optimizer; solution_processors::Vector{<:Function}=Function[], pmitd_ref_extensions::Vector{<:Function}=Vector{Function}([]), eng2math_passthrough::Dict{String,Vector{String}}=Dict{String,Vector{String}}(), make_si::Bool=true, solution_model::String="eng", kwargs...) + return solve_model(pmitd_data, pmitd_type, optimizer, build_dmld_opfitd; solution_processors=solution_processors, pmitd_ref_extensions=pmitd_ref_extensions, eng2math_passthrough=eng2math_passthrough, make_si=make_si, solution_model=solution_model, kwargs...) +end + + """ function solve_mn_dmld_opfitd_simple( pm_file, @@ -46,6 +66,26 @@ function solve_mn_dmld_opfitd_simple(pm_file, pmd_file, pmitd_file, pmitd_type, end +""" + function solve_mn_dmld_opfitd_simple( + pmitd_data::Dict{String,<:Any}, + pmitd_type, + optimizer; + solution_processors::Vector{<:Function}=Function[], + pmitd_ref_extensions::Vector{<:Function}=Vector{Function}([]), + eng2math_passthrough::Dict{String,Vector{String}}=Dict{String,Vector{String}}(), + make_si::Bool=true, + solution_model::String="eng", + kwargs... + ) + +Solve Multinetwork Integrated T&D Optimal Power Flow with minimum load delta (dmld continuous). +""" +function solve_mn_dmld_opfitd_simple(pmitd_data::Dict{String,<:Any}, pmitd_type, optimizer; solution_processors::Vector{<:Function}=Function[], pmitd_ref_extensions::Vector{<:Function}=Vector{Function}([]), eng2math_passthrough::Dict{String,Vector{String}}=Dict{String,Vector{String}}(), make_si::Bool=true, solution_model::String="eng", kwargs...) + return solve_model(pmitd_data, pmitd_type, optimizer, build_mn_dmld_opfitd_simple; multinetwork=true, solution_processors=solution_processors, pmitd_ref_extensions=pmitd_ref_extensions, eng2math_passthrough=eng2math_passthrough, make_si=make_si, solution_model=solution_model, kwargs...) +end + + """ function build_dmld_opfitd( pmitd::AbstractPowerModelITD diff --git a/src/prob/opfitd_oltc.jl b/src/prob/opfitd_oltc.jl index fb26d55..2999560 100644 --- a/src/prob/opfitd_oltc.jl +++ b/src/prob/opfitd_oltc.jl @@ -23,6 +23,26 @@ function solve_opfitd_oltc(pm_file, pmd_file, pmitd_file, pmitd_type, optimizer; end +""" + function solve_opfitd_oltc( + pmitd_data::Dict{String,<:Any}, + pmitd_type, + optimizer; + solution_processors::Vector{<:Function}=Function[], + pmitd_ref_extensions::Vector{<:Function}=Vector{Function}([]), + eng2math_passthrough::Dict{String,Vector{String}}=Dict{String,Vector{String}}(), + make_si::Bool=true, + solution_model::String="eng", + kwargs... + ) + +Solve Integrated T&D OLTC Optimal Power Flow. +""" +function solve_opfitd_oltc(pmitd_data::Dict{String,<:Any}, pmitd_type, optimizer; solution_processors::Vector{<:Function}=Function[], pmitd_ref_extensions::Vector{<:Function}=Vector{Function}([]), eng2math_passthrough::Dict{String,Vector{String}}=Dict{String,Vector{String}}(), make_si::Bool=true, solution_model::String="eng", kwargs...) + return solve_model(pmitd_data, pmitd_type, optimizer, build_opfitd_oltc; solution_processors=solution_processors, pmitd_ref_extensions=pmitd_ref_extensions, eng2math_passthrough=eng2math_passthrough, make_si=make_si, solution_model=solution_model, kwargs...) +end + + """ function solve_mn_opfitd_oltc( pm_file, @@ -46,6 +66,26 @@ function solve_mn_opfitd_oltc(pm_file, pmd_file, pmitd_file, pmitd_type, optimiz end +""" + function solve_mn_opfitd_oltc( + pmitd_data::Dict{String,<:Any}, + pmitd_type, + optimizer; + solution_processors::Vector{<:Function}=Function[], + pmitd_ref_extensions::Vector{<:Function}=Vector{Function}([]), + eng2math_passthrough::Dict{String,Vector{String}}=Dict{String,Vector{String}}(), + make_si::Bool=true, + solution_model::String="eng", + kwargs... + ) + +Solve Multinetwork Integrated T&D OLTC Optimal Power Flow. +""" +function solve_mn_opfitd_oltc(pmitd_data::Dict{String,<:Any}, pmitd_type, optimizer; solution_processors::Vector{<:Function}=Function[], pmitd_ref_extensions::Vector{<:Function}=Vector{Function}([]), eng2math_passthrough::Dict{String,Vector{String}}=Dict{String,Vector{String}}(), make_si::Bool=true, solution_model::String="eng", kwargs...) + return solve_model(pmitd_data, pmitd_type, optimizer, build_mn_opfitd_oltc; multinetwork=true, solution_processors=solution_processors, pmitd_ref_extensions=pmitd_ref_extensions, eng2math_passthrough=eng2math_passthrough, make_si=make_si, solution_model=solution_model, kwargs...) +end + + """ function build_opfitd_oltc( pmitd::AbstractPowerModelITD diff --git a/src/prob/opfitd_storage.jl b/src/prob/opfitd_storage.jl index 68cb74b..7b1f865 100755 --- a/src/prob/opfitd_storage.jl +++ b/src/prob/opfitd_storage.jl @@ -40,7 +40,6 @@ end pmitd_ref_extensions::Vector{<:Function}=Vector{Function}([]), eng2math_passthrough::Dict{String,Vector{String}}=Dict{String,Vector{String}}(), make_si::Bool=true, - auto_rename::Bool=false, solution_model::String="eng", kwargs... ) @@ -100,7 +99,6 @@ end pmitd_ref_extensions::Vector{<:Function}=Vector{Function}([]), eng2math_passthrough::Dict{String,Vector{String}}=Dict{String,Vector{String}}(), make_si::Bool=true, - auto_rename::Bool=false, solution_model::String="eng", kwargs... ) diff --git a/src/prob/pfitd.jl b/src/prob/pfitd.jl index d1d13ec..4c4b8fb 100755 --- a/src/prob/pfitd.jl +++ b/src/prob/pfitd.jl @@ -23,6 +23,26 @@ function solve_pfitd(pm_file, pmd_file, pmitd_file, pmitd_type, optimizer; solut end +""" + function solve_pfitd( + pmitd_data::Dict{String,<:Any} + pmitd_type, + optimizer; + solution_processors::Vector{<:Function}=Function[], + pmitd_ref_extensions::Vector{<:Function}=Vector{Function}([]), + eng2math_passthrough::Dict{String,Vector{String}}=Dict{String,Vector{String}}(), + make_si::Bool=true, + solution_model::String="eng", + kwargs... + ) + +Solve Integrated T&D Power Flow +""" +function solve_pfitd(pmitd_data::Dict{String,<:Any}, pmitd_type, optimizer; solution_processors::Vector{<:Function}=Function[], pmitd_ref_extensions::Vector{<:Function}=Vector{Function}([]), eng2math_passthrough::Dict{String,Vector{String}}=Dict{String,Vector{String}}(), make_si::Bool=true, solution_model::String="eng", kwargs...) + return solve_model(pmitd_data, pmitd_type, optimizer, build_pfitd; solution_processors=solution_processors, pmitd_ref_extensions=pmitd_ref_extensions, eng2math_passthrough=eng2math_passthrough, make_si=make_si, solution_model=solution_model, kwargs...) +end + + """ function build_pfitd( pmitd::AbstractPowerModelITD diff --git a/test/opfitd_storage.jl b/test/opfitd_storage.jl index bc5b2e1..80dcaea 100644 --- a/test/opfitd_storage.jl +++ b/test/opfitd_storage.jl @@ -1,6 +1,11 @@ @info "running ITD storage tests" -@testset "test/opfitd_pass.jl" begin +@testset "test/opfitd_storage.jl" begin + + + #--------------------------------------------------------------------------------------------------------------- + + # @testset "Check that eng2math_passthrough value is being added to the instantiated model: " begin @@ -20,22 +25,22 @@ # end - @testset "solve_model (build_mn_opfitd_storage): Multinetwork Balanced case5-case3 With Battery ACP-ACPU " begin - # pm_file = joinpath(dirname(trans_path), "case5_withload_strg.m") - pm_file = joinpath(dirname(trans_path), "case5_withload.m") - pmd_file = joinpath(dirname(dist_path), "case3_balanced_withBattery_mn.dss") - pmitd_file = joinpath(dirname(bound_path), "case5_case3_bal_battery_mn.json") - pmitd_type = NLPowerModelITD{ACPPowerModel, ACPUPowerModel} + # @testset "solve_model (build_mn_opfitd_storage): Multinetwork Balanced case5-case3 With Battery ACP-ACPU " begin + # # pm_file = joinpath(dirname(trans_path), "case5_withload_strg.m") + # pm_file = joinpath(dirname(trans_path), "case5_withload.m") + # pmd_file = joinpath(dirname(dist_path), "case3_balanced_withBattery_mn.dss") + # pmitd_file = joinpath(dirname(bound_path), "case5_case3_bal_battery_mn.json") + # pmitd_type = NLPowerModelITD{ACPPowerModel, ACPUPowerModel} - # One way - # pmitd_result_strg = solve_mn_opfitd_storage(pm_file, pmd_file, pmitd_file, pmitd_type, ipopt) + # # One way + # # pmitd_result_strg = solve_mn_opfitd_storage(pm_file, pmd_file, pmitd_file, pmitd_type, ipopt) - # Other way - pmitd_data = parse_files(pm_file, pmd_file, pmitd_file; multinetwork=true) - pmitd_result_strg = solve_mn_opfitd_storage(pmitd_data, pmitd_type, ipopt) + # # Other way + # pmitd_data = parse_files(pm_file, pmd_file, pmitd_file; multinetwork=true) + # pmitd_result_strg = solve_mn_opfitd_storage(pmitd_data, pmitd_type, ipopt) - end + # end #--------------------------------------------------------------------------------------------------------------- diff --git a/test/runtests.jl b/test/runtests.jl index 2f7d27e..bd61df9 100755 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -41,5 +41,6 @@ PowerModelsITD.silence!() # include("opfitd_oltc_mn.jl") # include("opfitd_dmld.jl") # include("opfitd_solution.jl") - include("opfitd_storage.jl") + # include("opfitd_storage.jl") + include("solve_x.jl") end diff --git a/test/solve_x.jl b/test/solve_x.jl new file mode 100644 index 0000000..b6a2538 --- /dev/null +++ b/test/solve_x.jl @@ -0,0 +1,86 @@ +@info "running ITD solve x - parsing first tests" + +@testset "test/solve_x.jl" begin + + @testset "solve pfitd" begin + pm_file = joinpath(dirname(trans_path), "case5_withload.m") + pmd_file = joinpath(dirname(dist_path), "case3_unbalanced.dss") + pmitd_file = joinpath(dirname(bound_path), "case5_case3_unbal.json") + pmitd_type = NLPowerModelITD{ACRPowerModel, ACRUPowerModel} + pmitd_data = parse_files(pm_file, pmd_file, pmitd_file) + result = solve_pfitd(pmitd_data, pmitd_type, ipopt) + @test result["termination_status"] == LOCALLY_SOLVED + end + + @testset "solve opfitd" begin + pm_file = joinpath(dirname(trans_path), "case5_withload.m") + pmd_file = joinpath(dirname(dist_path), "case3_unbalanced.dss") + pmitd_file = joinpath(dirname(bound_path), "case5_case3_unbal.json") + pmitd_type = NLPowerModelITD{ACRPowerModel, ACRUPowerModel} + pmitd_data = parse_files(pm_file, pmd_file, pmitd_file) + result = solve_opfitd(pmitd_data, pmitd_type, ipopt) + @test result["termination_status"] == LOCALLY_SOLVED + end + + @testset "solve opfitd oltc" begin + pm_file = joinpath(dirname(trans_path), "case5_withload.m") + pmd_file = joinpath(dirname(dist_path), "case3_balanced_oltc.dss") + pmitd_file = joinpath(dirname(bound_path), "case5_case3_bal_oltc.json") + pmitd_type = NLPowerModelITD{ACRPowerModel, ACRUPowerModel} + pmitd_data = parse_files(pm_file, pmd_file, pmitd_file) + result = solve_opfitd_oltc(pmitd_data, pmitd_type, ipopt) + @test result["termination_status"] == LOCALLY_SOLVED + end + + @testset "solve opfitd dmld" begin + pm_file = joinpath(dirname(trans_path), "case5_withload.m") + pmd_file = joinpath(dirname(dist_path), "ut_trans_2w_yy_138kv_nosubs.dss") + pmitd_file = joinpath(dirname(bound_path), "case5_caseut_trans_2w.json") + pmitd_type = NLPowerModelITD{ACPPowerModel, ACPUPowerModel} + pmitd_data = parse_files(pm_file, pmd_file, pmitd_file) + result = solve_dmld_opfitd(pmitd_data, pmitd_type, ipopt) + @test result["termination_status"] == LOCALLY_SOLVED + end + + @testset "solve opfitd multinetwork" begin + pm_file = joinpath(dirname(trans_path), "case5_withload.m") + pmd_file = joinpath(dirname(dist_path), "case3_unbalanced_withoutgen_mn.dss") + pmitd_file = joinpath(dirname(bound_path), "case5_case3_unbal_nogen_mn.json") + pmitd_type = NLPowerModelITD{ACPPowerModel, ACPUPowerModel} + pmitd_data = parse_files(pm_file, pmd_file, pmitd_file; multinetwork=true) + result = solve_mn_opfitd(pmitd_data, pmitd_type, ipopt) + @test result["termination_status"] == LOCALLY_SOLVED + end + + @testset "solve opfitd oltc multinetwork" begin + pm_file = joinpath(dirname(trans_path), "case5_withload.m") + pmd_file = joinpath(dirname(dist_path), "case3_balanced_oltc_mn.dss") + pmitd_file = joinpath(dirname(bound_path), "case5_case3_bal_oltc_mn.json") + pmitd_type = NLPowerModelITD{ACPPowerModel, ACPUPowerModel} + pmitd_data = parse_files(pm_file, pmd_file, pmitd_file; multinetwork=true) + result = solve_mn_opfitd(pmitd_data, pmitd_type, ipopt) + @test result["termination_status"] == LOCALLY_SOLVED + end + + @testset "solve opfitd dmld multinetwork" begin + pm_file = joinpath(dirname(trans_path), "case5_withload.m") + pmd_file = joinpath(dirname(dist_path), "ut_trans_2w_yy_138kv_nosubs_mn.dss") + pmitd_file = joinpath(dirname(bound_path), "case5_caseut_trans_2w_mn.json") + pmitd_type = NLPowerModelITD{ACPPowerModel, ACPUPowerModel} + pmitd_data = parse_files(pm_file, pmd_file, pmitd_file; multinetwork=true) + result = solve_mn_dmld_opfitd_simple(pmitd_data, pmitd_type, ipopt; make_si=true) + @test result["termination_status"] == LOCALLY_SOLVED + end + + @testset "solve opfitd multinetwork multisystem" begin + pm_file = joinpath(dirname(trans_path), "case5_with2loads.m") + pmd_file = joinpath(dirname(dist_path), "case3_unbalanced_withoutgen_mn.dss") + pmitd_file = joinpath(dirname(bound_path), "case5_case3_unbal_nogen_mn.json") + pmd_files = [pmd_file, pmd_file] + pmitd_type = NLPowerModelITD{ACPPowerModel, ACPUPowerModel} + pmitd_data = parse_files(pm_file, pmd_files, pmitd_file; auto_rename=true, multinetwork=true) + result = solve_mn_opfitd(pmitd_data, pmitd_type, ipopt) + @test result["termination_status"] == LOCALLY_SOLVED + end + +end From 7636c3f3959d195c2b7b404d7d3ace30967b2753 Mon Sep 17 00:00:00 2001 From: Juan Ospina Date: Tue, 14 Nov 2023 14:03:34 -0700 Subject: [PATCH 10/16] ADD: transmission system storage unit test case. --- Project.toml | 3 - .../case3_balanced_withBattery_mn_diff.dss | 50 +++ .../case3_unbalanced_withoutgen_mn_diff2.dss | 43 ++ test/data/distribution/load_profile_diff2.csv | 12 + test/data/transmission/case5_withload_strg.m | 3 +- test/opfitd_storage.jl | 386 ++++++++++++------ test/runtests.jl | 38 +- 7 files changed, 383 insertions(+), 152 deletions(-) create mode 100755 test/data/distribution/case3_balanced_withBattery_mn_diff.dss create mode 100755 test/data/distribution/case3_unbalanced_withoutgen_mn_diff2.dss create mode 100644 test/data/distribution/load_profile_diff2.csv diff --git a/Project.toml b/Project.toml index b55201b..de90ea6 100755 --- a/Project.toml +++ b/Project.toml @@ -6,14 +6,11 @@ version = "0.7.9" [deps] InfrastructureModels = "2030c09a-7f63-5d83-885d-db604e0e9cc0" -Ipopt = "b6b21f68-93f8-5de0-b562-5493be1d77c9" JSON = "682c06a0-de6a-54ab-a142-c8b1cf79cde6" JuMP = "4076af6c-e467-56ae-b986-b466b2749572" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" PowerModels = "c36e90e8-916a-50a6-bd94-075b64ef4655" PowerModelsDistribution = "d7431456-977f-11e9-2de3-97ff7677985e" -SCS = "c946c3f1-0d1f-5ce8-9dea-7daa1f7e2d13" -Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" [compat] InfrastructureModels = "0.7.8" diff --git a/test/data/distribution/case3_balanced_withBattery_mn_diff.dss b/test/data/distribution/case3_balanced_withBattery_mn_diff.dss new file mode 100755 index 0000000..e44ec23 --- /dev/null +++ b/test/data/distribution/case3_balanced_withBattery_mn_diff.dss @@ -0,0 +1,50 @@ +!Clear +New Circuit.3bus_bal_battery_mn +! define a really stiff source +~ basekv=230 pu=1.00 MVAsc3=200000 MVAsc1=210000 + +! Substation Transformer +New Transformer.SubXF Phases=3 Windings=2 Xhl=0.01 +~ wdg=1 bus=sourcebus conn=wye kv=230 kva=25000 %r=0.0005 +~ wdg=2 bus=Substation conn=wye kv=13.8 kva=25000 %r=0.0005 + + +!Define Linecodes + +New linecode.556MCM nphases=3 basefreq=60 ! ohms per 5 mile +~ rmatrix = ( 0.1000 | 0.0400 0.1000 | 0.0400 0.0400 0.1000) +~ xmatrix = ( 0.0583 | 0.0233 0.0583 | 0.0233 0.0233 0.0583) +~ cmatrix = (50.92958178940651 | -0 50.92958178940651 | -0 -0 50.92958178940651 ) ! small capacitance + + +New linecode.4/0QUAD nphases=3 basefreq=60 ! ohms per 100ft +~ rmatrix = ( 0.1167 | 0.0467 0.1167 | 0.0467 0.0467 0.1167) +~ xmatrix = (0.0667 | 0.0267 0.0667 | 0.0267 0.0267 0.0667 ) +~ cmatrix = (50.92958178940651 | -0 50.92958178940651 | -0 -0 50.92958178940651 ) ! small capacitance + + +!Define lines + +New Line.OHLine bus1=Substation.1.2.3 Primary.1.2.3 linecode = 556MCM length=1 ! 5 mile line +New Line.Quad Bus1=Primary.1.2.3 loadbus.1.2.3 linecode = 4/0QUAD length=1 ! 100 ft + + +!Loads - single phase +New Loadshape.ls1 pmult=(file=load_profile_diff2.csv) + +New Load.L1 phases=1 loadbus.1.0 ( 13.8 3 sqrt / ) kW=3000 kvar=1500 model=1 daily=ls1 +New Load.L2 phases=1 loadbus.2.0 ( 13.8 3 sqrt / ) kW=3000 kvar=1500 model=1 daily=ls1 +New Load.L3 phases=1 loadbus.3.0 ( 13.8 3 sqrt / ) kW=3000 kvar=1500 model=1 daily=ls1 + + +!Battery System DEFINITIONS +New Storage.S1 phases=3 bus1=primary.1.2.3 kv=( 13.8 3 sqrt / ) kwhstored=0 kwhrated=500 kva=500 kvar=100 +~ %charge=100 %discharge=100 %effcharge=100 %effdischarge=100 %idlingkw=1 %r=0 %x=50 + + +Set VoltageBases = "230,13.8" +Set tolerance=0.000001 +set defaultbasefreq=60 +!Calcvoltagebases +!Solve + diff --git a/test/data/distribution/case3_unbalanced_withoutgen_mn_diff2.dss b/test/data/distribution/case3_unbalanced_withoutgen_mn_diff2.dss new file mode 100755 index 0000000..378d2af --- /dev/null +++ b/test/data/distribution/case3_unbalanced_withoutgen_mn_diff2.dss @@ -0,0 +1,43 @@ +!Clear +New Circuit.3bus_unbal_nogen_mn_diff +! define a really stiff source +~ basekv=230 pu=1.00 MVAsc3=200000 MVAsc1=210000 + +! Substation Transformer +New Transformer.SubXF Phases=3 Windings=2 Xhl=0.01 +~ wdg=1 bus=sourcebus conn=wye kv=230 kva=25000 %r=0.0005 +~ wdg=2 bus=Substation conn=wye kv=13.8 kva=25000 %r=0.0005 + + +!Define Linecodes + +New linecode.556MCM nphases=3 basefreq=60 ! ohms per 5 mile +~ rmatrix = ( 0.1000 | 0.0400 0.1000 | 0.0400 0.0400 0.1000) +~ xmatrix = ( 0.0583 | 0.0233 0.0583 | 0.0233 0.0233 0.0583) +~ cmatrix = (50.92958178940651 | -0 50.92958178940651 | -0 -0 50.92958178940651 ) ! small capacitance + + +New linecode.4/0QUAD nphases=3 basefreq=60 ! ohms per 100ft +~ rmatrix = ( 0.1167 | 0.0467 0.1167 | 0.0467 0.0467 0.1167) +~ xmatrix = (0.0667 | 0.0267 0.0667 | 0.0267 0.0267 0.0667 ) +~ cmatrix = (50.92958178940651 | -0 50.92958178940651 | -0 -0 50.92958178940651 ) ! small capacitance + + +!Define lines + +New Line.OHLine bus1=Substation.1.2.3 Primary.1.2.3 linecode = 556MCM length=1 normamps=6000 emergamps=6000 ! 5 mile line +New Line.Quad Bus1=Primary.1.2.3 loadbus.1.2.3 linecode = 4/0QUAD length=1 normamps=6000 emergamps=6000 ! 100 ft + + +!Loads - single phase +New Loadshape.ls1 pmult=(file=load_profile_diff2.csv) + +New Load.L1 phases=1 loadbus.1.0 ( 13.8 3 sqrt / ) kW=4000 kvar=1333.33 model=1 daily=ls1 +New Load.L2 phases=1 loadbus.2.0 ( 13.8 3 sqrt / ) kW=3000 kvar=1500 model=1 daily=ls1 +New Load.L3 phases=1 loadbus.3.0 ( 13.8 3 sqrt / ) kW=3000 kvar=1500 model=1 daily=ls1 + +Set VoltageBases = "230,13.8" +Set tolerance=0.000001 +set defaultbasefreq=60 +!Calcvoltagebases +!Solve diff --git a/test/data/distribution/load_profile_diff2.csv b/test/data/distribution/load_profile_diff2.csv new file mode 100644 index 0000000..1a52ce4 --- /dev/null +++ b/test/data/distribution/load_profile_diff2.csv @@ -0,0 +1,12 @@ +0.1 +0.05 +0.05 +0.05 +0.3 +0.4 +0.5 +0.7 +1.1 +1.6 +0.7 +0.8 diff --git a/test/data/transmission/case5_withload_strg.m b/test/data/transmission/case5_withload_strg.m index debc219..5bffd65 100755 --- a/test/data/transmission/case5_withload_strg.m +++ b/test/data/transmission/case5_withload_strg.m @@ -59,6 +59,5 @@ %% storage data % storage_bus ps qs energy energy_rating charge_rating discharge_rating charge_efficiency discharge_efficiency thermal_rating qmin qmax r x p_loss q_loss status mpc.storage = [ - 3 0.0 0.0 20.0 100.0 50.0 70.0 0.8 0.9 100.0 -50.0 70.0 0.1 0.0 0.0 0.0 1; - 10 0.0 0.0 30.0 100.0 50.0 70.0 0.9 0.8 100.0 -50.0 70.0 0.1 0.0 0.0 0.0 1; + 3 0.0 0.0 0.0 200.0 100.0 100.0 0.95 0.95 100.0 -50.0 70.0 0.1 0.0 0.0 0.0 1; ]; diff --git a/test/opfitd_storage.jl b/test/opfitd_storage.jl index 80dcaea..9fcf3bf 100644 --- a/test/opfitd_storage.jl +++ b/test/opfitd_storage.jl @@ -2,133 +2,149 @@ @testset "test/opfitd_storage.jl" begin - - #--------------------------------------------------------------------------------------------------------------- - - - - - # @testset "Check that eng2math_passthrough value is being added to the instantiated model: " begin - # # pm_file = joinpath(dirname(trans_path), "case5_withload_strg.m") - # pm_file = joinpath(dirname(trans_path), "case5_withload.m") - # pmd_file = joinpath(dirname(dist_path), "case3_balanced_withBattery.dss") - # pmitd_file = joinpath(dirname(bound_path), "case5_case3_bal_battery.json") - # pmitd_type = NLPowerModelITD{ACRPowerModel, ACRUPowerModel} - - # # One way of doing - # # pmitd_result_strg = solve_opfitd_storage(pm_file, pmd_file, pmitd_file, pmitd_type, ipopt) - - # # Other way of doing it - # pmitd_data = parse_files(pm_file, pmd_file, pmitd_file) - # pmitd_result_strg = solve_opfitd_storage(pmitd_data, pmitd_type, ipopt) - - # end - - - # @testset "solve_model (build_mn_opfitd_storage): Multinetwork Balanced case5-case3 With Battery ACP-ACPU " begin - # # pm_file = joinpath(dirname(trans_path), "case5_withload_strg.m") - # pm_file = joinpath(dirname(trans_path), "case5_withload.m") - # pmd_file = joinpath(dirname(dist_path), "case3_balanced_withBattery_mn.dss") - # pmitd_file = joinpath(dirname(bound_path), "case5_case3_bal_battery_mn.json") - # pmitd_type = NLPowerModelITD{ACPPowerModel, ACPUPowerModel} - - # # One way - # # pmitd_result_strg = solve_mn_opfitd_storage(pm_file, pmd_file, pmitd_file, pmitd_type, ipopt) - - # # Other way - # pmitd_data = parse_files(pm_file, pmd_file, pmitd_file; multinetwork=true) - # pmitd_result_strg = solve_mn_opfitd_storage(pmitd_data, pmitd_type, ipopt) - - - # end - - - #--------------------------------------------------------------------------------------------------------------- - - - # @testset "Check that eng2math_passthrough value is being added to the instantiated model: " begin - # pm_file = joinpath(dirname(trans_path), "case5_withload.m") - # pmd_file = joinpath(dirname(dist_path), "case3_balanced_withBattery.dss") - # pmitd_file = joinpath(dirname(bound_path), "case5_case3_bal_battery.json") - # pmitd_type = NLPowerModelITD{ACRPowerModel, ACRUPowerModel} - # pmitd_data = parse_files(pm_file, pmd_file, pmitd_file) - - # # add cost to storages in PMD - # for (st_name, st_data) in pmitd_data["it"]["pmd"]["storage"] - # st_data["cost"] = 0.25 - # end - - # # instantiate model with eng2math_passthrough - # eng2math_passthrough = Dict("storage"=>["cost"]) - # pmitd_inst_model = instantiate_model(pmitd_data, pmitd_type, build_opfitd; eng2math_passthrough=eng2math_passthrough) - - # # get storage reference - # storage_ref = _IM.ref(pmitd_inst_model, _PMD.pmd_it_sym, nw=nw_id_default, :storage) - - # # test that the "cost" value in storage exists. - # @test storage_ref[1]["cost"] == 0.25 - # end - - # @testset "Check that eng2math_passthrough value is being added to the instantiated model (Multinetwork): " begin + @testset "Check that eng2math_passthrough value is being added to the instantiated model: " begin + pm_file = joinpath(dirname(trans_path), "case5_withload.m") + pmd_file = joinpath(dirname(dist_path), "case3_balanced_withBattery.dss") + pmitd_file = joinpath(dirname(bound_path), "case5_case3_bal_battery.json") + pmitd_type = NLPowerModelITD{ACRPowerModel, ACRUPowerModel} + pmitd_data = parse_files(pm_file, pmd_file, pmitd_file) + + # add cost to storages in PMD + for (st_name, st_data) in pmitd_data["it"]["pmd"]["storage"] + st_data["cost"] = 0.25 + end + + # instantiate model with eng2math_passthrough + eng2math_passthrough = Dict("storage"=>["cost"]) + pmitd_inst_model = instantiate_model(pmitd_data, pmitd_type, build_opfitd; eng2math_passthrough=eng2math_passthrough) + + # get storage reference + storage_ref = _IM.ref(pmitd_inst_model, _PMD.pmd_it_sym, nw=nw_id_default, :storage) + + # test that the "cost" value in storage exists. + @test storage_ref[1]["cost"] == 0.25 + end + + @testset "Check that eng2math_passthrough value is being added to the instantiated model (Multinetwork): " begin + pm_file = joinpath(dirname(trans_path), "case5_withload.m") + pmd_file = joinpath(dirname(dist_path), "case3_balanced_withBattery_mn.dss") + pmitd_file = joinpath(dirname(bound_path), "case5_case3_bal_battery_mn.json") + pmitd_type = NLPowerModelITD{ACRPowerModel, ACRUPowerModel} + pmitd_data = parse_files(pm_file, pmd_file, pmitd_file; multinetwork=true) + + # add cost to storages in PMD + for (nw_id, nw_data) in pmitd_data["it"]["pmd"]["nw"] + for (st_name, st_data) in nw_data["storage"] + st_data["cost"] = 0.25 + end + end + + # instantiate model with eng2math_passthrough + eng2math_passthrough = Dict("storage"=>["cost"]) + pmitd_inst_model = instantiate_model(pmitd_data, pmitd_type, build_mn_opfitd; multinetwork=true, eng2math_passthrough=eng2math_passthrough) + + # get storage reference from nw=4 + storage_ref_nw4 = _IM.ref(pmitd_inst_model, _PMD.pmd_it_sym, nw=4, :storage) + + # test that the "cost" value in storage nw=4 exists. + @test storage_ref_nw4[1]["cost"] == 0.25 + end + + @testset "solve_model (build_opfitd_storage): Balanced case5-case3 With Battery ACP-ACPU " begin + pm_file = joinpath(dirname(trans_path), "case5_withload.m") + pmd_file = joinpath(dirname(dist_path), "case3_balanced_withBattery.dss") + pmitd_file = joinpath(dirname(bound_path), "case5_case3_bal_battery.json") + pmitd_type = NLPowerModelITD{ACPPowerModel, ACPUPowerModel} + pmitd_data = parse_files(pm_file, pmd_file, pmitd_file) + + # cost to assign to energy storage + # Units $/kWh + strg_cost = 0.025 + + # add cost to storages in PMD + for (st_name, st_data) in pmitd_data["it"]["pmd"]["storage"] + st_data["cost"] = strg_cost + end + + # eng2math_passthrough + eng2math_passthrough = Dict("storage"=>["cost"]) + + # with storage cost problem + pmitd_result_strg = solve_model(pmitd_data, pmitd_type, ipopt, build_opfitd_storage; eng2math_passthrough=eng2math_passthrough) + @test isapprox(pmitd_result_strg["objective"], 17977.48172498742+(pmitd_result_strg["solution"]["it"]["pmd"]["storage"]["3bus_bal_battery.s1"]["sd"]*strg_cost); atol = 1e-3) + + end + + @testset "solve_model (build_mn_opfitd_storage): Multinetwork Balanced case5-case3 With Battery ACP-ACPU " begin + pm_file = joinpath(dirname(trans_path), "case5_withload.m") + pmd_file = joinpath(dirname(dist_path), "case3_balanced_withBattery_mn.dss") + pmitd_file = joinpath(dirname(bound_path), "case5_case3_bal_battery_mn.json") + pmitd_type = NLPowerModelITD{ACPPowerModel, ACPUPowerModel} + pmitd_data = parse_files(pm_file, pmd_file, pmitd_file; multinetwork=true) + + # cost to assign to energy storage + # Units $/kWh + strg_cost = 0.025 + + # add cost to storages in PMD + for (nw_id, nw_data) in pmitd_data["it"]["pmd"]["nw"] + for (st_name, st_data) in nw_data["storage"] + st_data["cost"] = strg_cost + end + end + + # instantiate model with eng2math_passthrough + eng2math_passthrough = Dict("storage"=>["cost"]) + + # with storage cost problem + pmitd_result_strg = solve_model(pmitd_data, pmitd_type, ipopt, build_mn_opfitd_storage; multinetwork=true, eng2math_passthrough=eng2math_passthrough) + + # add solution cost of the batteries + sln_batt_cost = 0 + for (nw_id, nw_data) in pmitd_result_strg["solution"]["it"]["pmd"]["nw"] + for (st_name, st_data) in nw_data["storage"] + sln_batt_cost += st_data["sd"]*strg_cost + end + end + + @test isapprox(pmitd_result_strg["objective"], 71226.99645541582+(sln_batt_cost); atol = 1e-3) + + end + + @testset "solve_mn_opfitd_storage: Multinetwork Balanced case5-case3 With Battery ACP-ACPU " begin + pm_file = joinpath(dirname(trans_path), "case5_withload.m") + pmd_file = joinpath(dirname(dist_path), "case3_balanced_withBattery_mn_diff.dss") + pmitd_file = joinpath(dirname(bound_path), "case5_case3_bal_battery_mn.json") + pmitd_type = NLPowerModelITD{ACPPowerModel, ACPUPowerModel} + pmitd_data = parse_files(pm_file, pmd_file, pmitd_file; multinetwork=true) + + # cost to assign to energy storage + # Units $/kWh + strg_cost = 0.0025 + + # add cost to storages in PMD + for (nw_id, nw_data) in pmitd_data["it"]["pmd"]["nw"] + for (st_name, st_data) in nw_data["storage"] + st_data["cost"] = strg_cost + end + end + + pmitd_result_strg = solve_mn_opfitd_storage(pmitd_data, pmitd_type, ipopt) + @test isapprox(pmitd_result_strg["solution"]["it"]["pmd"]["nw"]["10"]["storage"]["3bus_bal_battery_mn.s1"]["sd"], 500.0; atol = 1e-1) + + end + + ## IVRU is missing critical current variables (real currentr) for solving opfs with storage in PMD + # @testset "solve_mn_opfitd_storage: Multinetwork Balanced case5-case3 With Battery IVR-IVRU " begin # pm_file = joinpath(dirname(trans_path), "case5_withload.m") - # pmd_file = joinpath(dirname(dist_path), "case3_balanced_withBattery_mn.dss") + # pmd_file = joinpath(dirname(dist_path), "case3_balanced_withBattery_mn_diff.dss") # pmitd_file = joinpath(dirname(bound_path), "case5_case3_bal_battery_mn.json") - # pmitd_type = NLPowerModelITD{ACRPowerModel, ACRUPowerModel} + # pmitd_type = IVRPowerModelITD{IVRPowerModel, IVRUPowerModel} # pmitd_data = parse_files(pm_file, pmd_file, pmitd_file; multinetwork=true) - # # add cost to storages in PMD - # for (nw_id, nw_data) in pmitd_data["it"]["pmd"]["nw"] - # for (st_name, st_data) in nw_data["storage"] - # st_data["cost"] = 0.25 - # end - # end - - # # instantiate model with eng2math_passthrough - # eng2math_passthrough = Dict("storage"=>["cost"]) - # pmitd_inst_model = instantiate_model(pmitd_data, pmitd_type, build_mn_opfitd; multinetwork=true, eng2math_passthrough=eng2math_passthrough) - - # # get storage reference from nw=4 - # storage_ref_nw4 = _IM.ref(pmitd_inst_model, _PMD.pmd_it_sym, nw=4, :storage) - - # # test that the "cost" value in storage nw=4 exists. - # @test storage_ref_nw4[1]["cost"] == 0.25 - # end - - # @testset "solve_model (build_opfitd_storage): Balanced case5-case3 With Battery ACP-ACPU " begin - # pm_file = joinpath(dirname(trans_path), "case5_withload.m") - # pmd_file = joinpath(dirname(dist_path), "case3_balanced_withBattery.dss") - # pmitd_file = joinpath(dirname(bound_path), "case5_case3_bal_battery.json") - # pmitd_type = NLPowerModelITD{ACPPowerModel, ACPUPowerModel} - # pmitd_data = parse_files(pm_file, pmd_file, pmitd_file) - # # cost to assign to energy storage - # # Units $/pu, to convert from your wanted $/MWh just multiply by MVABase (e.g., 2.5 $/MWh x 100 MWh/1pu = 250 $/pu) - # strg_cost = 250 - - # # add cost to storages in PMD - # for (st_name, st_data) in pmitd_data["it"]["pmd"]["storage"] - # st_data["cost"] = strg_cost - # end - - # # eng2math_passthrough - # eng2math_passthrough = Dict("storage"=>["cost"]) - - # # with storage cost problem - # pmitd_result_strg = solve_model(pmitd_data, pmitd_type, ipopt, build_opfitd_storage; eng2math_passthrough=eng2math_passthrough) - # @test isapprox(pmitd_result_strg["objective"], 17977.48172498742+(pmitd_result_strg["solution"]["it"]["pmd"]["storage"]["3bus_bal_battery.s1"]["sd"]*strg_cost)/100000; atol = 1e-3) - - # end - - # @testset "solve_model (build_mn_opfitd_storage): Multinetwork Balanced case5-case3 With Battery ACP-ACPU " begin - # pm_file = joinpath(dirname(trans_path), "case5_withload.m") - # pmd_file = joinpath(dirname(dist_path), "case3_balanced_withBattery_mn.dss") - # pmitd_file = joinpath(dirname(bound_path), "case5_case3_bal_battery_mn.json") - # pmitd_type = NLPowerModelITD{ACPPowerModel, ACPUPowerModel} - # pmitd_data = parse_files(pm_file, pmd_file, pmitd_file; multinetwork=true) - - # # cost to assign to energy storage - # # Units $/pu, to convert from your wanted $/MWh just multiply by MVABase (e.g., 2.5 $/MWh x 100 MWh/1pu = 250 $/pu) - # strg_cost = 250 + # # Units $/kWh + # strg_cost = 0.0025 # # add cost to storages in PMD # for (nw_id, nw_data) in pmitd_data["it"]["pmd"]["nw"] @@ -137,13 +153,127 @@ # end # end - # # instantiate model with eng2math_passthrough - # eng2math_passthrough = Dict("storage"=>["cost"]) - - # # with storage cost problem - # pmitd_result_strg = solve_model(pmitd_data, pmitd_type, ipopt, build_mn_opfitd_storage; multinetwork=true, eng2math_passthrough=eng2math_passthrough) - # @test isapprox(pmitd_result_strg["objective"], 71226.99645541582+0.12500248519; atol = 1e-3) + # pmitd_result_strg = solve_mn_opfitd_storage(pmitd_data, pmitd_type, ipopt) + # @test isapprox(pmitd_result_strg["solution"]["it"]["pmd"]["nw"]["10"]["storage"]["3bus_bal_battery_mn.s1"]["sd"], 500.0; atol = 1e-1) # end + @testset "solve_mn_opfitd_storage: Multinetwork Balanced case5-case3 With Battery ACR-FBSUBF " begin + pm_file = joinpath(dirname(trans_path), "case5_withload.m") + pmd_file = joinpath(dirname(dist_path), "case3_balanced_withBattery_mn_diff.dss") + pmitd_file = joinpath(dirname(bound_path), "case5_case3_bal_battery_mn.json") + pmitd_type = NLBFPowerModelITD{ACRPowerModel, FBSUBFPowerModel} + pmitd_data = parse_files(pm_file, pmd_file, pmitd_file; multinetwork=true) + + # cost to assign to energy storage + # Units $/kWh + strg_cost = 0.0025 + + # add cost to storages in PMD + for (nw_id, nw_data) in pmitd_data["it"]["pmd"]["nw"] + for (st_name, st_data) in nw_data["storage"] + st_data["cost"] = strg_cost + end + end + + pmitd_result_strg = solve_mn_opfitd_storage(pmitd_data, pmitd_type, ipopt) + @test isapprox(pmitd_result_strg["solution"]["it"]["pmd"]["nw"]["10"]["storage"]["3bus_bal_battery_mn.s1"]["sd"], 500.0; atol = 1e-1) + + end + + @testset "solve_mn_opfitd_storage: Multinetwork Balanced case5-case3 With Battery ACR-FOTRU " begin + pm_file = joinpath(dirname(trans_path), "case5_withload.m") + pmd_file = joinpath(dirname(dist_path), "case3_balanced_withBattery_mn_diff.dss") + pmitd_file = joinpath(dirname(bound_path), "case5_case3_bal_battery_mn.json") + pmitd_type = NLFOTPowerModelITD{ACRPowerModel, FOTRUPowerModel} + pmitd_data = parse_files(pm_file, pmd_file, pmitd_file; multinetwork=true) + + # cost to assign to energy storage + # Units $/kWh + strg_cost = 0.0025 + + # add cost to storages in PMD + for (nw_id, nw_data) in pmitd_data["it"]["pmd"]["nw"] + for (st_name, st_data) in nw_data["storage"] + st_data["cost"] = strg_cost + end + end + + pmitd_result_strg = solve_mn_opfitd_storage(pmitd_data, pmitd_type, ipopt) + @test isapprox(pmitd_result_strg["solution"]["it"]["pmd"]["nw"]["10"]["storage"]["3bus_bal_battery_mn.s1"]["sd"], 500.0; atol = 1e-1) + + end + + @testset "solve_mn_opfitd_storage: Multinetwork Balanced case5-case3 With Battery BFA-LinDist3Flow " begin + pm_file = joinpath(dirname(trans_path), "case5_withload.m") + pmd_file = joinpath(dirname(dist_path), "case3_balanced_withBattery_mn_diff.dss") + pmitd_file = joinpath(dirname(bound_path), "case5_case3_bal_battery_mn.json") + pmitd_type = BFPowerModelITD{BFAPowerModel, LinDist3FlowPowerModel} + pmitd_data = parse_files(pm_file, pmd_file, pmitd_file; multinetwork=true) + + # cost to assign to energy storage + # Units $/kWh + strg_cost = 0.0025 + + # add cost to storages in PMD + for (nw_id, nw_data) in pmitd_data["it"]["pmd"]["nw"] + for (st_name, st_data) in nw_data["storage"] + st_data["cost"] = strg_cost + end + end + + pmitd_result_strg = solve_mn_opfitd_storage(pmitd_data, pmitd_type, ipopt) + # LinDist3Flow seems to have problems with Energy Storage control. + # @test isapprox(pmitd_result_strg["solution"]["it"]["pmd"]["nw"]["10"]["storage"]["3bus_bal_battery_mn.s1"]["sd"], 500.0; atol = 1e-1) + + end + + @testset "solve_mn_opfitd_storage: Multinetwork Balanced case5-case3 With Battery in Transmission ACP-ACPU " begin + pm_file = joinpath(dirname(trans_path), "case5_withload_strg.m") + pmd_file = joinpath(dirname(dist_path), "case3_unbalanced_withoutgen_mn_diff2.dss") + pmitd_file = joinpath(dirname(bound_path), "case5_case3_unbal_nogen_mn_diff.json") + pmitd_type = NLPowerModelITD{ACPPowerModel, ACPUPowerModel} + pmitd_data = parse_files(pm_file, pmd_file, pmitd_file; multinetwork=true) + + # cost to assign to energy storage + # Units $/pu + strg_cost = 0.0025 + + # add cost to storages in PMD + for (nw_id, nw_data) in pmitd_data["it"]["pm"]["nw"] + for (st_name, st_data) in nw_data["storage"] + st_data["cost"] = strg_cost + end + end + + # varying load + pm_residential_prof_p = [0.293391082 0.139045672 0.469951253 0.432787275 0.392755291 0.548138118 0.466415402 0.862109886 0.794416422 0.643809168 0.709608241 0.704126411 0.963765953 0.575280879 0.611590304 0.488662676 0.430361821 0.87041275 1.306424968 1.307958207 0.928544462 0.572729047 0.552861937 0.346550725] + pm_residential_prof_q = [0.247949186 0.171635443 0.183848151 0.227990471 0.192748178 0.319480381 0.180830891 0.415389708 0.224002485 0.322713139 0.241254927 0.300147051 0.298347195 0.186199635 0.22251068 0.303541769 0.185154905 0.432356924 0.425774291 0.50190239 0.294529233 0.320796731 0.14920804 0.136169507] + + pm_community_prof_p = [0.284287776 0.268623055 0.227327982 0.397346384 0.235818756 0.20823994 0.435025687 0.569052345 0.837116674 0.895062747 0.966074521 1.01396914 0.87299946 0.900887326 0.92727811 0.839239282 1.068426425 1.066425748 1.392200763 1.268324779 1.400386576 1.218758216 1.001009647 0.538582071] + pm_community_prof_q = [0.124401652 0.149265062 0.112556201 0.226246144 0.112703236 0.145539091 0.24876825 0.304559383 0.412502283 0.360553174 0.361034872 0.481935002 0.515125962 0.507287921 0.398147224 0.463007061 0.507275554 0.534884845 0.508290756 0.531864342 0.549012302 0.519189403 0.452179964 0.218622959] + + pm_commercial_prof_p = [0.375187827 0.286661472 0.289096564 0.22256313 0.271830914 0.292079786 0.236396231 0.93119696 1.056699043 1.049660067 1.009438919 1.082846718 1.059419722 1.039691666 1.025920791 1.032047161 1.085377745 1.04104745 1.022642982 0.924980776 0.319601874 0.347188983 0.289086358 0.276695314] + pm_commercial_prof_q = [0.167332967 0.299127427 0.279260644 0.207282394 0.159990914 0.219440511 0.178642786 0.559090945 0.514151587 0.540382353 0.531009283 0.518096858 0.514654825 0.591549766 0.525647471 0.549013224 0.520947523 0.557888204 0.553033554 0.511838587 0.161558414 0.257372041 0.20559509 0.295150131] + + for (nw, nw_data) in pmitd_data["it"]["pm"]["nw"] + for (load, load_data) in nw_data["load"] + if (load == "1") + load_data["pd"] = load_data["pd"]*pm_commercial_prof_p[parse(Int64,nw)] + load_data["qd"] = load_data["qd"]*pm_commercial_prof_q[parse(Int64,nw)] + elseif (load == "2") + load_data["pd"] = load_data["pd"]*pm_community_prof_p[parse(Int64,nw)] + load_data["qd"] = load_data["qd"]*pm_community_prof_q[parse(Int64,nw)] + elseif (load == "3") + load_data["pd"] = load_data["pd"]*pm_residential_prof_p[parse(Int64,nw)] + load_data["qd"] = load_data["qd"]*pm_residential_prof_q[parse(Int64,nw)] + end + end + end + + pmitd_result_strg = solve_mn_opfitd_storage(pmitd_data, pmitd_type, ipopt) + @test isapprox(pmitd_result_strg["solution"]["it"]["pm"]["nw"]["10"]["storage"]["1"]["sd"]*100, 37.8596; atol = 1e-1) + + end + end diff --git a/test/runtests.jl b/test/runtests.jl index bd61df9..c6b2e37 100755 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -23,24 +23,24 @@ scs_solver = optimizer_with_attributes(SCS.Optimizer, "verbose"=>0) PowerModelsITD.silence!() @testset "PowerModelsITD.jl" begin - # include("io.jl") - # include("base.jl") - # include("data.jl") - # include("autorename.jl") - # include("opfitd.jl") - # include("opfitd_duals.jl") - # include("pfitd.jl") - # include("opfitd_ms.jl") - # include("pfitd_ms.jl") - # include("transformations_opfitd.jl") - # include("largescale_opfitd.jl") - # include("opfitd_hybrids.jl") - # include("pfitd_hybrids.jl") - # include("opfitd_oltc.jl") - # include("opfitd_mn.jl") - # include("opfitd_oltc_mn.jl") - # include("opfitd_dmld.jl") - # include("opfitd_solution.jl") - # include("opfitd_storage.jl") + include("io.jl") + include("base.jl") + include("data.jl") + include("autorename.jl") + include("opfitd.jl") + include("opfitd_duals.jl") + include("pfitd.jl") + include("opfitd_ms.jl") + include("pfitd_ms.jl") + include("transformations_opfitd.jl") + include("largescale_opfitd.jl") + include("opfitd_hybrids.jl") + include("pfitd_hybrids.jl") + include("opfitd_oltc.jl") + include("opfitd_mn.jl") + include("opfitd_oltc_mn.jl") + include("opfitd_dmld.jl") + include("opfitd_solution.jl") + include("opfitd_storage.jl") include("solve_x.jl") end From 0cb6892eb0d926e64501265d68ce2172e0d4bd96 Mon Sep 17 00:00:00 2001 From: Juan Ospina Date: Wed, 15 Nov 2023 11:34:29 -0700 Subject: [PATCH 11/16] DOC: added documentation of new storage problem formulation and updated BeginnersGuide and dependencies. --- CHANGELOG.md | 3 + docs/Project.toml | 4 +- docs/make.jl | 14 +- docs/src/manual/storage.md | 143 ++++ docs/src/tutorials/Beginners Guide.md | 3 - docs/src/tutorials/BeginnersGuide.md | 3 + .../{Beginners Guide.jl => BeginnersGuide.jl} | 749 ++++++++++-------- src/PowerModelsITD.jl | 2 +- 8 files changed, 581 insertions(+), 340 deletions(-) create mode 100644 docs/src/manual/storage.md delete mode 100644 docs/src/tutorials/Beginners Guide.md create mode 100644 docs/src/tutorials/BeginnersGuide.md rename examples/{Beginners Guide.jl => BeginnersGuide.jl} (77%) mode change 100644 => 100755 diff --git a/CHANGELOG.md b/CHANGELOG.md index 3660e63..c6175b0 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,9 @@ - Modified all objectives in `objective_storage.jl` to take into account the cost of discharging the storage devices in the transmission system. Also, added in-situ conversion for the PMD cost that converts the $/kWh -> $/pu cost, so that the user can provide the $/kWh in the `ENG` model, avoiding confusions. - Added the option to parse files and use the parsed structure solve directly the optimization problem using `solve_opfitd_storage(..)` and `solve_mn_opfitd_storage(..)`. This allows users to parse the data, modify costs (or other parameters) and then run the solve functions without using the `solve_model(..)` function which requires users to explicitly use the `eng2math_passthrough = Dict("storage"=>["cost"])`. - Added option to parse files and use the parsed structure directly into any `solve_x(..)` functions. +- Added documentation of the new problem formulation that considers storage costs. +- Updated BeginnersGuide.jl Pluto Notebook to latest versions. +- Updated DOCs dependencies and fixed minor issues. ## staged diff --git a/docs/Project.toml b/docs/Project.toml index 37dd472..39d1117 100644 --- a/docs/Project.toml +++ b/docs/Project.toml @@ -4,6 +4,6 @@ Gumbo = "708ec375-b3d6-5a57-a7ce-8257bf98657a" Pluto = "c3e4b0f8-55cb-11ea-2926-15256bba5781" [compat] -Documenter = "~0.27.7" +Documenter = "~1.1.1" Gumbo = "~0.8.0" -Pluto = "~0.15.1" +Pluto = "~0.19.1" diff --git a/docs/make.jl b/docs/make.jl index 254cdab..2b972ff 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -15,7 +15,6 @@ makedocs( prettyurls=false, collapselevel=1, ), - strict=false, sitename = "PowerModelsITD.jl", authors = "Juan Ospina, David M Fobes, and contributors", pages = [ @@ -25,9 +24,10 @@ makedocs( "Getting Started" => "manual/quickguide.md", "File Formats" => "manual/fileformat.md", "Formulations" => "manual/formulations.md", + "Storage" => "manual/storage.md", ], "Tutorials" => [ - "Beginners Guide" => "tutorials/Beginners Guide.md", + "Beginners Guide" => "tutorials/BeginnersGuide.md", ], "API Reference" => [ "Base" => "reference/base.md", @@ -60,12 +60,15 @@ if !_FAST Pluto.update_run!(ss, nb, nb.cells) html = Pluto.generate_html(nb) - fileout = "docs/build/tutorials/$(basename(file)).html" + base_name = basename(file) + base_name_splitted = split(base_name, '.')[1] + fileout = "docs/build/tutorials/$(base_name_splitted).html" + open(fileout, "w") do io write(io, html) end - doc = open("docs/build/tutorials/$(replace(basename(file), ".jl" => ".html"))", "r") do io + doc = open("docs/build/tutorials/$(base_name_splitted).html", "r") do io Gumbo.parsehtml(read(io, String)) end @@ -82,9 +85,10 @@ if !_FAST # edit existing html to replace :article with :iframe doc.root[2][1][2][2] = iframe + # doc.root[2][1][1] = iframe # Overwrite HTML - open("docs/build/tutorials/$(replace(basename(file), ".jl" => ".html"))", "w") do io + open("docs/build/tutorials/$(base_name_splitted).html", "w") do io Gumbo.prettyprint(io, doc) end end diff --git a/docs/src/manual/storage.md b/docs/src/manual/storage.md new file mode 100644 index 0000000..ecc2243 --- /dev/null +++ b/docs/src/manual/storage.md @@ -0,0 +1,143 @@ +# Quick Guide on Storage (with Costs) Problem Specification + +In this guide, we will discuss the differences between the two problem specifications currently available in `PowermodelsITD` that consider `storage devices` when performing OPF. + +## TL;DR + +- `solve_opfitd(...)`: considers the operation (charge and discharge) of storage devices at both transmission and distribution system(s), but, no cost is added to the objective cost function. In other words, cycling/using the storage devices is a `free` operation. + +- `solve_opfitd_storage(...)`: considers the operation (charge and discharge) of storage devices at both transmission and distribution system(s), and cost term(s) are added to the objective cost function. In other words, cycling/using the storage devices is `not free` and the specific cost of using the storage device is added to the cost function as $sd \times cost$, where `sd` is the amount of power discharged and `cost` is a scalar value in units \$/kWh or \$/pu. + +## Default Costs and Units + +By default, the costs of the storage devices are calculated based on the references shown below. However, users are *encouraged* to assign their own costs after parsing files. + +- Transmission cost must be in \$/pu units. +(e.g., transformation from \$/MWh -> \$/pu: 200 \$/MWh x 100 MVA base/1 pu = 20,000 \$/pu) +- Distribution cost must be in \$/kWh units. +- Default costs of storage devices (in \$/kWh) are computed based on Eq. (23) from this [publication](https://ieeexplore.ieee.org/document/8805394). +- The total cost of the storage system, $C_{total}^{ES}$, is estimated based on NREL data obtained from this [resource](https://atb.nrel.gov/electricity/2021/residential_battery_storage). + +```math +\begin{align} +% +cost = c_{\epsilon, \varepsilon} = \dfrac{C_{total}^{ES}}{Cyc\cdot {E_{_{ES}}^{max}}\cdot DoD\cdot \eta_{r}}, +% +\end{align} +``` + +## Running Integrated Transmission-Distribution Optimal Power Flow with Storage Costs + +The snippet below shows how to run a steady-state Integrated Transmission-Distribution (ITD) AC Optimal Power Flow with storage costs. +All of these files can be found in `test` folder of the repository. + +```julia +using PowerModelsITD +using Ipopt + +pm_file = "case5_withload.m" +pmd_file = "case3_balanced_withBattery.dss" +pmitd_file = "case5_case3_bal_battery.json" +pmitd_type = NLPowerModelITD{ACPPowerModel, ACPUPowerModel} +pmitd_data = parse_files(pm_file, pmd_file, pmitd_file) + +# cost to assign to energy storage +# Units $/kWh +strg_cost = 0.025 + +# add cost to storages in PMD +for (st_name, st_data) in pmitd_data["it"]["pmd"]["storage"] + st_data["cost"] = strg_cost +end + +# solve optimization with storage cost problem +pmitd_result_strg = solve_opfitd_storage(pmitd_data, pmitd_type, Ipopt.Optimizer) + +``` + +## Running Integrated Transmission-Distribution Optimal Power Flow with Storage Costs Multinetwork + +Running a multinetwork (i.e., multi-timestep) is also very simple. Only slight changes are required (assuming multinetwork data exists in the distribution files) as seen in the snippet shown below. + +```julia +using PowerModelsITD +using Ipopt + +pm_file = "case5_withload.m" +pmd_file = "case3_balanced_withBattery_mn_diff.dss" +pmitd_file = "case5_case3_bal_battery_mn.json" +pmitd_type = NLPowerModelITD{ACPPowerModel, ACPUPowerModel} +pmitd_data = parse_files(pm_file, pmd_file, pmitd_file; multinetwork=true) + +# cost to assign to energy storage +# Units $/kWh +strg_cost = 0.0025 + +# add cost to storages in PMD +for (nw_id, nw_data) in pmitd_data["it"]["pmd"]["nw"] + for (st_name, st_data) in nw_data["storage"] + st_data["cost"] = strg_cost + end +end + +pmitd_result_strg = solve_mn_opfitd_storage(pmitd_data, pmitd_type, Ipopt.Optimizer) + +``` + +## How are the Storage Devices Modeled? + +The storage mathematical model (for both transmission and distribution system(s)) are based on the model shown [here](https://lanl-ansi.github.io/PowerModels.jl/stable/storage/). +Given the storage data model and two sequential time points $s$ and $t$, the storage component's mathematical model is given by, + +```math +\begin{align} +% +\mbox{data: } & \nonumber \\ +& e^u \mbox{ - energy rating} \nonumber \\ +& sc^u \mbox{ - charge rating} \nonumber \\ +& sd^u \mbox{ - discharge rating} \nonumber \\ +& \eta^c \mbox{ - charge efficiency} \nonumber \\ +& \eta^d \mbox{ - discharge efficiency} \nonumber \\ +& te \mbox{ - time elapsed} \nonumber \\ +& S^l \mbox{ - power losses} \nonumber \\ +& Z \mbox{ - injection impedance} \nonumber \\ +& q^l, q^u \mbox{ - reactive power injection limits} \nonumber \\ +& s^u \mbox{ - thermal injection limit} \nonumber \\ +& i^u \mbox{ - current injection limit} \nonumber \\ +% +\mbox{variables: } & \nonumber \\ +& e_i \in (0, e^u) \mbox{ - storage energy at time $i$} \label{var_strg_energy} \\ +& sc_i \in (0, sc^u) \mbox{ - charge amount at time $i$} \label{var_strg_charge} \\ +& sd_i \in (0, sd^u) \mbox{ - discharge amount at time $i$} \label{var_strg_discharge} \\ +& sqc_i \mbox{ - reactive power slack at time $i$} \label{var_strg_qslack} \\ +& S_i \mbox{ - complex bus power injection at time $i$} \label{var_strg_power} \\ +& I_i \mbox{ - complex bus current injection at time $i$} \label{var_strg_current} \\ +% +\mbox{subject to: } & \nonumber \\ +& e_t - e_s = te \left(\eta^c sc_t - \frac{sd_t}{\eta^d} \right) \label{eq_strg_energy} \\ +& sc_t \cdot sd_t = 0 \label{eq_strg_compl} \\ +& S_t + (sd_t - sc_t) = j \cdot sqc_t + S^l + Z |I_t|^2 \label{eq_strg_loss} \\ +& q^l \leq \Im(S_t) \leq q^u \label{eq_strg_q_limit} \\ +& |S_t| \leq s^u \label{eq_strg_thermal_limit} \\ +& |I_t| \leq i^u \label{eq_strg_current_limit} +\end{align} +``` + +## What is the Cost (Objective) Function? + +The cost (objective) function of the `solve_opfitd_storage(...)` and `solve_mn_opfitd_storage(...)` is based on the mathematical model: + +```math +\begin{align} +\begin{split} + \text{min} &\bigg(\sum_{k \in G^{^\mathcal{T}}} c_{2k}(P_{g,k}^{\mathcal{T}})^2 + c_{1k}(P_{g,k}^{\mathcal{T}}) + c_{0k} \bigg) +\\ + &\bigg(\sum_{\epsilon \in E^{^\mathcal{T}}} c_{\epsilon}(sd_{\epsilon}^{\mathcal{T}})\bigg) +\\ + &\bigg(\sum_{m \in G^{^\mathcal{D}}} c_{2m}(\sum_{\varphi \in \Phi} P_{g,m}^{\mathcal{D},\varphi})^2 + c_{1m}(\sum_{\varphi \in \Phi} P_{g,m}^{\mathcal{D},\varphi}) + c_{0m} \bigg) +\\ + &\bigg(\sum_{\varepsilon \in E^{^\mathcal{D}}} c_{\varepsilon}(sd_{\varepsilon}^{\mathcal{D}})\bigg) +\end{split} +\end{align} +``` + +As observed, the costs of discharging the storage devices for both Transmission and Distribution system(s) are added the cost function of the OPFITD. This means that storage devices will only discharge power to the grid when the cost of `charging` + `discharging` energy is less than the cost of *not* using (cycling) the storage device. + +This cost function only cosiders cost as a factor for determining when to use the storage devices, however, it is important to note that users are encouraged to create their own cost functions that consider other factors (e.g., voltage deviations) that will make the storage devices operate more often, as needed. diff --git a/docs/src/tutorials/Beginners Guide.md b/docs/src/tutorials/Beginners Guide.md deleted file mode 100644 index d647887..0000000 --- a/docs/src/tutorials/Beginners Guide.md +++ /dev/null @@ -1,3 +0,0 @@ -# Introduction to PowerModelsITD - -Stub for Beginners Guide.jl Pluto Notebook in the examples/ folder. The Pluto Notebook will get rendered and inserted as an iframe at documentation build time. diff --git a/docs/src/tutorials/BeginnersGuide.md b/docs/src/tutorials/BeginnersGuide.md new file mode 100644 index 0000000..50a284e --- /dev/null +++ b/docs/src/tutorials/BeginnersGuide.md @@ -0,0 +1,3 @@ +# Introduction to PowerModelsITD + +Stub for BeginnersGuide.jl Pluto Notebook in the examples/ folder. The Pluto Notebook will get rendered and inserted as an iframe at documentation build time. diff --git a/examples/Beginners Guide.jl b/examples/BeginnersGuide.jl old mode 100644 new mode 100755 similarity index 77% rename from examples/Beginners Guide.jl rename to examples/BeginnersGuide.jl index d9699e7..91fbcaa --- a/examples/Beginners Guide.jl +++ b/examples/BeginnersGuide.jl @@ -1,5 +1,5 @@ ### A Pluto.jl notebook ### -# v0.15.1 +# v0.19.32 using Markdown using InteractiveUtils @@ -7,18 +7,22 @@ using InteractiveUtils # This Pluto notebook uses @bind for interactivity. When running this notebook outside of Pluto, the following 'mock version' of @bind gives bound variables a default value (instead of an error). macro bind(def, element) quote + local iv = try Base.loaded_modules[Base.PkgId(Base.UUID("6e696c72-6542-2067-7265-42206c756150"), "AbstractPlutoDingetjes")].Bonds.initial_value catch; b -> missing; end local el = $(esc(element)) - global $(esc(def)) = Core.applicable(Base.get, el) ? Base.get(el) : missing + global $(esc(def)) = Core.applicable(Base.get, el) ? Base.get(el) : iv(el) el end end # ╔═╡ 4b293648-92aa-4ebb-9d33-a6707773f0b1 -using CodeTracking, Revise, PlutoUI +using PlutoUI # ╔═╡ 7592c3de-481f-4877-a362-56e0eaaf56b0 using PowerModelsITD +# ╔═╡ e5c578f1-0c16-4b6c-83fb-045f8f18a8c4 +using Ipopt # Optimizer + # ╔═╡ ea6bd476-ba83-4b1b-ad70-825b25d15135 md""" # Introduction to PowerModelsITD @@ -26,9 +30,9 @@ md""" This Notebook was designed for the following versions: - `julia = "~1.6"` -- `PowerModelsITD = "~0.7.1"` -- `PowerModelsDistribution = "~0.14.4"` -- `PowerModels = "~0.19.5"` +- `PowerModelsITD = "~0.7.9"` +- `PowerModelsDistribution = "~0.15.1"` +- `PowerModels = "~0.19.9"` This notebook is a beginner's introduction to [`PowerModelsITD.jl`](https://github.com/lanl-ansi/PowerModelsITD.jl), an optimization-focused Julia library for steady state integrated power transmission-distribution modeling, based on `PowerModels.jl`, `PowerModelsDistribution.jl`, `JuMP.jl`, and part of the larger [`InfrastructureModels.jl`](https://github.com/lanl-ansi/InfrastructureModels.jl) ecosystem, which notably includes: @@ -59,16 +63,6 @@ md""" Let's define the optimizer to be used for the rest of this notebook (**IPOPT**). """ -# ╔═╡ e5c578f1-0c16-4b6c-83fb-045f8f18a8c4 -begin - import JuMP - import Ipopt - ipopt = JuMP.optimizer_with_attributes(Ipopt.Optimizer, - "acceptable_tol"=>1.0e-8, - "print_level"=>0, "sb"=>"yes") -end - - # ╔═╡ 39d43eb3-ff43-4ef6-a30d-bd6ef8a632e0 md""" ## Run OPFITD and PFITD using `PowerModelsITD` @@ -228,7 +222,7 @@ md""" """ # ╔═╡ f25f1f56-a60e-40aa-85f2-8bb73ad41ed7 - result = solve_opfitd(pm_file, pmd_file, pmitd_file, pmitd_type, ipopt) + result = solve_opfitd(pm_file, pmd_file, pmitd_file, pmitd_type, Ipopt.Optimizer) # ╔═╡ 816424e4-5ec4-4ac2-a94a-41e7e2e7e305 md""" @@ -284,7 +278,7 @@ md""" """ # ╔═╡ 16f7f7d2-054c-409f-88fc-280b8023ade6 - result_pfitd = solve_pfitd(pm_file, pmd_file, pmitd_file, pmitd_type, ipopt) + result_pfitd = solve_pfitd(pm_file, pmd_file, pmitd_file, pmitd_type, Ipopt.Optimizer) # ╔═╡ 57657b07-7690-4ff9-86cf-90512d4b7091 md""" @@ -410,7 +404,7 @@ result_soc = solve_opfitd(pm_file_other, pmd_file_soc, pmitd_file_soc, pmitd_type_soc, - ipopt) + Ipopt.Optimizer) # ╔═╡ ac80d9b0-4cde-4b04-a4f0-1346194f6b55 result_soc["solution"]["it"]["pmitd"] @@ -573,7 +567,7 @@ md""" # ╔═╡ 5793e46b-c702-4f38-b1a8-c3fcb7778ec0 result_multcase = solve_model(pmitd_data_multcase, pmitd_type_multcase, - ipopt, + Ipopt.Optimizer, build_opfitd) # ╔═╡ 8a3156df-f4a8-4872-9eae-8453f128b642 @@ -628,7 +622,7 @@ begin # solve multinetwork opfitd. result_mn = solve_mn_opfitd(pm_file_mn, pmd_files_mn, pmitd_file_mn, - pmitd_type_mn, ipopt; auto_rename=true) + pmitd_type_mn, Ipopt.Optimizer; auto_rename=true) end @@ -706,7 +700,7 @@ begin # run opftid result_opfitd_b = solve_model(pmitd_data_vbounds, pmitd_type_multcase, - ipopt, + Ipopt.Optimizer, build_opfitd) end @@ -730,7 +724,7 @@ begin # run opftid result_rab = solve_model(pmitd_data_rab, pmitd_type_multcase, - ipopt, + Ipopt.Optimizer, build_opfitd) end @@ -755,7 +749,7 @@ begin # run opftid result_kron = solve_model(pmitd_data_kron, pmitd_type_multcase, - ipopt, + Ipopt.Optimizer, build_opfitd) end @@ -791,7 +785,7 @@ begin pmd_file_sol, boundary_file_sol, pmitd_type_sol, - ipopt; + Ipopt.Optimizer; make_si=false, solution_processors=[sol_data_model!]) @@ -852,7 +846,7 @@ begin pmd_file_math, pmitd_file_math, pmitd_type_math, - ipopt; + Ipopt.Optimizer; make_si=false, solution_model="math") end @@ -880,7 +874,7 @@ begin pmd_file_eng, pmitd_file_eng, pmitd_type_eng, - ipopt; + Ipopt.Optimizer; make_si=false, solution_model="eng") end @@ -902,658 +896,755 @@ md""" # ╔═╡ 00000000-0000-0000-0000-000000000001 PLUTO_PROJECT_TOML_CONTENTS = """ [deps] -CodeTracking = "da1fd8a2-8d9e-5ec2-8556-3022fb5608a2" Ipopt = "b6b21f68-93f8-5de0-b562-5493be1d77c9" -JuMP = "4076af6c-e467-56ae-b986-b466b2749572" PlutoUI = "7f904dfe-b85e-4ff6-b463-dae2292396a8" PowerModelsITD = "615c3f80-b0cb-4ecd-88fe-27bee056c380" -Revise = "295af30f-e4ad-537b-8983-00126c2a3abe" [compat] -CodeTracking = "~1.0.9" Ipopt = "~1.0.2" -JuMP = "~1.0.0" PlutoUI = "~0.7.39" PowerModelsITD = "~0.7.1" -Revise = "~3.3.3" """ # ╔═╡ 00000000-0000-0000-0000-000000000002 PLUTO_MANIFEST_TOML_CONTENTS = """ # This file is machine-generated - editing it directly is not advised -[[ASL_jll]] +julia_version = "1.9.1" +manifest_format = "2.0" +project_hash = "01fded79dcdc95cb80b44cd3c643000d0454af75" + +[[deps.ASL_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] git-tree-sha1 = "6252039f98492252f9e47c312c8ffda0e3b9e78d" uuid = "ae81ac8f-d209-56e5-92de-9978fef736f9" version = "0.1.3+0" -[[AbstractPlutoDingetjes]] +[[deps.AbstractPlutoDingetjes]] deps = ["Pkg"] -git-tree-sha1 = "8eaf9f1b4921132a4cff3f36a1d9ba923b14a481" +git-tree-sha1 = "91bd53c39b9cbfb5ef4b015e8b582d344532bd0a" uuid = "6e696c72-6542-2067-7265-42206c756150" -version = "1.1.4" +version = "1.2.0" -[[ArgTools]] -uuid = "0dad84c5-d112-42e6-8d28-ef12dabb789f" +[[deps.Adapt]] +deps = ["LinearAlgebra", "Requires"] +git-tree-sha1 = "02f731463748db57cc2ebfbd9fbc9ce8280d3433" +uuid = "79e6a3ab-5dfb-504d-930d-738a2a938a0e" +version = "3.7.1" + + [deps.Adapt.extensions] + AdaptStaticArraysExt = "StaticArrays" -[[ArrayInterfaceCore]] -deps = ["LinearAlgebra", "SparseArrays", "SuiteSparse"] -git-tree-sha1 = "8a9c02f9d323d4dd8a47245abb106355bf7b45e6" -uuid = "30b0a656-2188-435a-8636-2ec0e6a096e2" -version = "0.1.2" + [deps.Adapt.weakdeps] + StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" -[[Artifacts]] +[[deps.ArgTools]] +uuid = "0dad84c5-d112-42e6-8d28-ef12dabb789f" +version = "1.1.1" + +[[deps.ArrayInterface]] +deps = ["Adapt", "LinearAlgebra", "Requires", "SparseArrays", "SuiteSparse"] +git-tree-sha1 = "16267cf279190ca7c1b30d020758ced95db89cd0" +uuid = "4fba245c-0d91-5ea0-9b3e-6abc04ee57a9" +version = "7.5.1" + + [deps.ArrayInterface.extensions] + ArrayInterfaceBandedMatricesExt = "BandedMatrices" + ArrayInterfaceBlockBandedMatricesExt = "BlockBandedMatrices" + ArrayInterfaceCUDAExt = "CUDA" + ArrayInterfaceGPUArraysCoreExt = "GPUArraysCore" + ArrayInterfaceStaticArraysCoreExt = "StaticArraysCore" + ArrayInterfaceTrackerExt = "Tracker" + + [deps.ArrayInterface.weakdeps] + BandedMatrices = "aae01518-5342-5314-be14-df237901396f" + BlockBandedMatrices = "ffab5731-97b5-5995-9138-79e8c1846df0" + CUDA = "052768ef-5323-5732-b1bb-66c8b64840ba" + GPUArraysCore = "46192b85-c4d5-4398-a991-12ede77f4527" + StaticArraysCore = "1e83bf80-4336-4d27-bf5d-d5a4f845583c" + Tracker = "9f7883ad-71c0-57eb-9f7f-b5c9e6d3789c" + +[[deps.Artifacts]] uuid = "56f22d72-fd6d-98f1-02f0-08ddc0907c33" -[[Base64]] +[[deps.Base64]] uuid = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f" -[[BenchmarkTools]] +[[deps.BenchmarkTools]] deps = ["JSON", "Logging", "Printf", "Profile", "Statistics", "UUIDs"] -git-tree-sha1 = "4c10eee4af024676200bc7752e536f858c6b8f93" +git-tree-sha1 = "d9a9701b899b30332bbcb3e1679c41cce81fb0e8" uuid = "6e4b80f9-dd63-53aa-95a3-0cdb28fa8baf" -version = "1.3.1" +version = "1.3.2" -[[Bzip2_jll]] +[[deps.Bzip2_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] git-tree-sha1 = "19a35467a82e236ff51bc17a3a44b69ef35185a2" uuid = "6e34b625-4abd-537c-b88f-471c36dfa7a0" version = "1.0.8+0" -[[CSV]] -deps = ["CodecZlib", "Dates", "FilePathsBase", "InlineStrings", "Mmap", "Parsers", "PooledArrays", "SentinelArrays", "Tables", "Unicode", "WeakRefStrings"] -git-tree-sha1 = "873fb188a4b9d76549b81465b1f75c82aaf59238" +[[deps.CSV]] +deps = ["CodecZlib", "Dates", "FilePathsBase", "InlineStrings", "Mmap", "Parsers", "PooledArrays", "PrecompileTools", "SentinelArrays", "Tables", "Unicode", "WeakRefStrings", "WorkerUtilities"] +git-tree-sha1 = "44dbf560808d49041989b8a96cae4cffbeb7966a" uuid = "336ed68f-0bac-5ca0-87d4-7b16caf5d00b" -version = "0.10.4" +version = "0.10.11" -[[Calculus]] +[[deps.Calculus]] deps = ["LinearAlgebra"] git-tree-sha1 = "f641eb0a4f00c343bbc32346e1217b86f3ce9dad" uuid = "49dc2e85-a5d0-5ad3-a950-438e2897f1b9" version = "0.5.1" -[[ChainRulesCore]] -deps = ["Compat", "LinearAlgebra", "SparseArrays"] -git-tree-sha1 = "9489214b993cd42d17f44c36e359bf6a7c919abf" -uuid = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" -version = "1.15.0" - -[[ChangesOfVariables]] -deps = ["ChainRulesCore", "LinearAlgebra", "Test"] -git-tree-sha1 = "1e315e3f4b0b7ce40feded39c73049692126cf53" -uuid = "9e997f8a-9a97-42d5-a9f1-ce6bfc15e2c0" -version = "0.1.3" - -[[CodeTracking]] -deps = ["InteractiveUtils", "UUIDs"] -git-tree-sha1 = "6d4fa04343a7fc9f9cb9cff9558929f3d2752717" -uuid = "da1fd8a2-8d9e-5ec2-8556-3022fb5608a2" -version = "1.0.9" - -[[CodecBzip2]] +[[deps.CodecBzip2]] deps = ["Bzip2_jll", "Libdl", "TranscodingStreams"] -git-tree-sha1 = "2e62a725210ce3c3c2e1a3080190e7ca491f18d7" +git-tree-sha1 = "c0ae2a86b162fb5d7acc65269b469ff5b8a73594" uuid = "523fee87-0ab8-5b00-afb7-3ecf72e48cfd" -version = "0.7.2" +version = "0.8.1" -[[CodecZlib]] +[[deps.CodecZlib]] deps = ["TranscodingStreams", "Zlib_jll"] -git-tree-sha1 = "ded953804d019afa9a3f98981d99b33e3db7b6da" +git-tree-sha1 = "cd67fc487743b2f0fd4380d4cbd3a24660d0eec8" uuid = "944b1d66-785c-5afd-91f1-9de20f533193" -version = "0.7.0" +version = "0.7.3" -[[ColorTypes]] +[[deps.ColorTypes]] deps = ["FixedPointNumbers", "Random"] -git-tree-sha1 = "a985dc37e357a3b22b260a5def99f3530fb415d3" +git-tree-sha1 = "eb7f0f8307f71fac7c606984ea5fb2817275d6e4" uuid = "3da002f7-5984-5a60-b8a6-cbb66c0b333f" -version = "0.11.2" +version = "0.11.4" -[[CommonSubexpressions]] +[[deps.CommonSubexpressions]] deps = ["MacroTools", "Test"] git-tree-sha1 = "7b8a93dba8af7e3b42fecabf646260105ac373f7" uuid = "bbf7d656-a473-5ed7-a52c-81e309532950" version = "0.3.0" -[[Compat]] -deps = ["Base64", "Dates", "DelimitedFiles", "Distributed", "InteractiveUtils", "LibGit2", "Libdl", "LinearAlgebra", "Markdown", "Mmap", "Pkg", "Printf", "REPL", "Random", "SHA", "Serialization", "SharedArrays", "Sockets", "SparseArrays", "Statistics", "Test", "UUIDs", "Unicode"] -git-tree-sha1 = "b153278a25dd42c65abbf4e62344f9d22e59191b" +[[deps.Compat]] +deps = ["UUIDs"] +git-tree-sha1 = "8a62af3e248a8c4bad6b32cbbe663ae02275e32c" uuid = "34da2185-b29b-5c13-b0c7-acf172513d20" -version = "3.43.0" +version = "4.10.0" +weakdeps = ["Dates", "LinearAlgebra"] + + [deps.Compat.extensions] + CompatLinearAlgebraExt = "LinearAlgebra" -[[CompilerSupportLibraries_jll]] +[[deps.CompilerSupportLibraries_jll]] deps = ["Artifacts", "Libdl"] uuid = "e66e0078-7015-5450-92f7-15fbd957f2ae" +version = "1.0.2+0" + +[[deps.ConstructionBase]] +deps = ["LinearAlgebra"] +git-tree-sha1 = "c53fc348ca4d40d7b371e71fd52251839080cbc9" +uuid = "187b0558-2788-49d3-abe0-74a17ed4e7c9" +version = "1.5.4" -[[DataAPI]] -git-tree-sha1 = "fb5f5316dd3fd4c5e7c30a24d50643b73e37cd40" + [deps.ConstructionBase.extensions] + ConstructionBaseIntervalSetsExt = "IntervalSets" + ConstructionBaseStaticArraysExt = "StaticArrays" + + [deps.ConstructionBase.weakdeps] + IntervalSets = "8197267c-284f-5f27-9208-e0e47529a953" + StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" + +[[deps.DataAPI]] +git-tree-sha1 = "8da84edb865b0b5b0100c0666a9bc9a0b71c553c" uuid = "9a962f9c-6df0-11e9-0e5d-c546b8b5ee8a" -version = "1.10.0" +version = "1.15.0" -[[DataStructures]] +[[deps.DataStructures]] deps = ["Compat", "InteractiveUtils", "OrderedCollections"] -git-tree-sha1 = "cc1a8e22627f33c789ab60b36a9132ac050bbf75" +git-tree-sha1 = "3dbd312d370723b6bb43ba9d02fc36abade4518d" uuid = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8" -version = "0.18.12" +version = "0.18.15" -[[DataValueInterfaces]] +[[deps.DataValueInterfaces]] git-tree-sha1 = "bfc1187b79289637fa0ef6d4436ebdfe6905cbd6" uuid = "e2d170a0-9d28-54be-80f0-106bbe20a464" version = "1.0.0" -[[Dates]] +[[deps.Dates]] deps = ["Printf"] uuid = "ade2ca70-3891-5945-98fb-dc099432e06a" -[[DelimitedFiles]] -deps = ["Mmap"] -uuid = "8bb1440f-4735-579b-a4ab-409b98df4dab" - -[[DiffResults]] -deps = ["StaticArrays"] -git-tree-sha1 = "c18e98cba888c6c25d1c3b048e4b3380ca956805" +[[deps.DiffResults]] +deps = ["StaticArraysCore"] +git-tree-sha1 = "782dd5f4561f5d267313f23853baaaa4c52ea621" uuid = "163ba53b-c6d8-5494-b064-1a9d43ac40c5" -version = "1.0.3" +version = "1.1.0" -[[DiffRules]] +[[deps.DiffRules]] deps = ["IrrationalConstants", "LogExpFunctions", "NaNMath", "Random", "SpecialFunctions"] -git-tree-sha1 = "28d605d9a0ac17118fe2c5e9ce0fbb76c3ceb120" +git-tree-sha1 = "23163d55f885173722d1e4cf0f6110cdbaf7e272" uuid = "b552c78f-8df3-52c6-915a-8e097449b14b" -version = "1.11.0" +version = "1.15.1" -[[Distances]] -deps = ["LinearAlgebra", "SparseArrays", "Statistics", "StatsAPI"] -git-tree-sha1 = "3258d0659f812acde79e8a74b11f17ac06d0ca04" +[[deps.Distances]] +deps = ["LinearAlgebra", "Statistics", "StatsAPI"] +git-tree-sha1 = "5225c965635d8c21168e32a12954675e7bea1151" uuid = "b4f34e82-e78d-54a5-968a-f98e89d6e8f7" -version = "0.10.7" +version = "0.10.10" -[[Distributed]] + [deps.Distances.extensions] + DistancesChainRulesCoreExt = "ChainRulesCore" + DistancesSparseArraysExt = "SparseArrays" + + [deps.Distances.weakdeps] + ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" + SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" + +[[deps.Distributed]] deps = ["Random", "Serialization", "Sockets"] uuid = "8ba89e20-285c-5b6f-9357-94700520ee1b" -[[DocStringExtensions]] +[[deps.DocStringExtensions]] deps = ["LibGit2"] -git-tree-sha1 = "b19534d1895d702889b219c382a6e18010797f0b" +git-tree-sha1 = "2fb1e02f2b635d0845df5d7c167fec4dd739b00d" uuid = "ffbed154-4ef7-542d-bbb7-c09d3a79fcae" -version = "0.8.6" +version = "0.9.3" -[[Downloads]] -deps = ["ArgTools", "LibCURL", "NetworkOptions"] +[[deps.Downloads]] +deps = ["ArgTools", "FileWatching", "LibCURL", "NetworkOptions"] uuid = "f43a241f-c20a-4ad4-852c-f6b1247861c6" +version = "1.6.0" -[[FilePaths]] +[[deps.FilePaths]] deps = ["FilePathsBase", "MacroTools", "Reexport", "Requires"] git-tree-sha1 = "919d9412dbf53a2e6fe74af62a73ceed0bce0629" uuid = "8fc22ac5-c921-52a6-82fd-178b2807b824" version = "0.8.3" -[[FilePathsBase]] +[[deps.FilePathsBase]] deps = ["Compat", "Dates", "Mmap", "Printf", "Test", "UUIDs"] -git-tree-sha1 = "129b104185df66e408edd6625d480b7f9e9823a0" +git-tree-sha1 = "9f00e42f8d99fdde64d40c8ea5d14269a2e2c1aa" uuid = "48062228-2e41-5def-b9a4-89aafe57970f" -version = "0.9.18" +version = "0.9.21" -[[FileWatching]] +[[deps.FileWatching]] uuid = "7b1f6079-737a-58dc-b8bc-7a2ca5c1b5ee" -[[FiniteDiff]] -deps = ["ArrayInterfaceCore", "LinearAlgebra", "Requires", "SparseArrays", "StaticArrays"] -git-tree-sha1 = "4fc79c0f63ddfdcdc623a8ce36623346a7ce9ae4" +[[deps.FiniteDiff]] +deps = ["ArrayInterface", "LinearAlgebra", "Requires", "Setfield", "SparseArrays"] +git-tree-sha1 = "c6e4a1fbe73b31a3dea94b1da449503b8830c306" uuid = "6a86dc24-6348-571c-b903-95158fe2bd41" -version = "2.12.0" +version = "2.21.1" + + [deps.FiniteDiff.extensions] + FiniteDiffBandedMatricesExt = "BandedMatrices" + FiniteDiffBlockBandedMatricesExt = "BlockBandedMatrices" + FiniteDiffStaticArraysExt = "StaticArrays" -[[FixedPointNumbers]] + [deps.FiniteDiff.weakdeps] + BandedMatrices = "aae01518-5342-5314-be14-df237901396f" + BlockBandedMatrices = "ffab5731-97b5-5995-9138-79e8c1846df0" + StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" + +[[deps.FixedPointNumbers]] deps = ["Statistics"] git-tree-sha1 = "335bfdceacc84c5cdf16aadc768aa5ddfc5383cc" uuid = "53c48c17-4a7d-5ca2-90c5-79b7896eea93" version = "0.8.4" -[[ForwardDiff]] -deps = ["CommonSubexpressions", "DiffResults", "DiffRules", "LinearAlgebra", "LogExpFunctions", "NaNMath", "Preferences", "Printf", "Random", "SpecialFunctions", "StaticArrays"] -git-tree-sha1 = "2f18915445b248731ec5db4e4a17e451020bf21e" +[[deps.ForwardDiff]] +deps = ["CommonSubexpressions", "DiffResults", "DiffRules", "LinearAlgebra", "LogExpFunctions", "NaNMath", "Preferences", "Printf", "Random", "SpecialFunctions"] +git-tree-sha1 = "cf0fe81336da9fb90944683b8c41984b08793dad" uuid = "f6369f11-7733-5829-9624-2563aa707210" -version = "0.10.30" +version = "0.10.36" + + [deps.ForwardDiff.extensions] + ForwardDiffStaticArraysExt = "StaticArrays" -[[Future]] + [deps.ForwardDiff.weakdeps] + StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" + +[[deps.Future]] deps = ["Random"] uuid = "9fa8497b-333b-5362-9e8d-4d0656e87820" -[[Glob]] -git-tree-sha1 = "4df9f7e06108728ebf00a0a11edee4b29a482bb2" +[[deps.Glob]] +git-tree-sha1 = "97285bbd5230dd766e9ef6749b80fc617126d496" uuid = "c27321d9-0574-5035-807b-f59d2c89b15c" -version = "1.3.0" +version = "1.3.1" -[[Hyperscript]] +[[deps.Hyperscript]] deps = ["Test"] git-tree-sha1 = "8d511d5b81240fc8e6802386302675bdf47737b9" uuid = "47d2ed2b-36de-50cf-bf87-49c2cf4b8b91" version = "0.0.4" -[[HypertextLiteral]] +[[deps.HypertextLiteral]] deps = ["Tricks"] -git-tree-sha1 = "c47c5fa4c5308f27ccaac35504858d8914e102f9" +git-tree-sha1 = "7134810b1afce04bbc1045ca1985fbe81ce17653" uuid = "ac1192a8-f4b3-4bfe-ba22-af5b92cd3ab2" -version = "0.9.4" +version = "0.9.5" -[[IOCapture]] +[[deps.IOCapture]] deps = ["Logging", "Random"] -git-tree-sha1 = "f7be53659ab06ddc986428d3a9dcc95f6fa6705a" +git-tree-sha1 = "d75853a0bdbfb1ac815478bacd89cd27b550ace6" uuid = "b5f81e59-6552-4d32-b1f0-c071b021bf89" -version = "0.2.2" +version = "0.2.3" -[[InfrastructureModels]] +[[deps.InfrastructureModels]] deps = ["JuMP", "Memento"] -git-tree-sha1 = "0c9ec48199cb90a6d1b5c6bdc0f9a15ce8a108b2" +git-tree-sha1 = "f9c1f6bdac8ad3fca6fc24fcf68256958ad84c28" uuid = "2030c09a-7f63-5d83-885d-db604e0e9cc0" -version = "0.7.4" +version = "0.7.8" -[[InlineStrings]] +[[deps.InlineStrings]] deps = ["Parsers"] -git-tree-sha1 = "61feba885fac3a407465726d0c330b3055df897f" +git-tree-sha1 = "9cc2baf75c6d09f9da536ddf58eb2f29dedaf461" uuid = "842dd82b-1e85-43dc-bf29-5d0ee9dffc48" -version = "1.1.2" +version = "1.4.0" -[[InteractiveUtils]] +[[deps.InteractiveUtils]] deps = ["Markdown"] uuid = "b77e0a4c-d291-57a0-90e8-8db25a27a240" -[[InverseFunctions]] -deps = ["Test"] -git-tree-sha1 = "336cc738f03e069ef2cac55a104eb823455dca75" -uuid = "3587e190-3f89-42d0-90ee-14403ec27112" -version = "0.1.4" - -[[Ipopt]] +[[deps.Ipopt]] deps = ["Ipopt_jll", "MathOptInterface"] -git-tree-sha1 = "8b7b5fdbc71d8f88171865faa11d1c6669e96e32" +git-tree-sha1 = "6d4c0cec91619b7c44ed9d4f9d021ce053019e15" uuid = "b6b21f68-93f8-5de0-b562-5493be1d77c9" -version = "1.0.2" +version = "1.0.4" -[[Ipopt_jll]] +[[deps.Ipopt_jll]] deps = ["ASL_jll", "Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "Libdl", "MUMPS_seq_jll", "OpenBLAS32_jll", "Pkg"] git-tree-sha1 = "e3e202237d93f18856b6ff1016166b0f172a49a8" uuid = "9cc047cb-c261-5740-88fc-0cf96f7bdcc7" version = "300.1400.400+0" -[[IrrationalConstants]] -git-tree-sha1 = "7fd44fd4ff43fc60815f8e764c0f352b83c49151" +[[deps.IrrationalConstants]] +git-tree-sha1 = "630b497eafcc20001bba38a4651b327dcfc491d2" uuid = "92d709cd-6900-40b7-9082-c6be49f344b6" -version = "0.1.1" +version = "0.2.2" -[[IteratorInterfaceExtensions]] +[[deps.IteratorInterfaceExtensions]] git-tree-sha1 = "a3f24677c21f5bbe9d2a714f95dcd58337fb2856" uuid = "82899510-4779-5014-852e-03e436cf321d" version = "1.0.0" -[[JLLWrappers]] -deps = ["Preferences"] -git-tree-sha1 = "abc9885a7ca2052a736a600f7fa66209f96506e1" +[[deps.JLLWrappers]] +deps = ["Artifacts", "Preferences"] +git-tree-sha1 = "7e5d6779a1e09a36db2a7b6cff50942a0a7d0fca" uuid = "692b3bcd-3c85-4b1f-b108-f13ce0eb3210" -version = "1.4.1" +version = "1.5.0" -[[JSON]] +[[deps.JSON]] deps = ["Dates", "Mmap", "Parsers", "Unicode"] -git-tree-sha1 = "3c837543ddb02250ef42f4738347454f95079d4e" +git-tree-sha1 = "31e996f0a15c7b280ba9f76636b3ff9e2ae58c9a" uuid = "682c06a0-de6a-54ab-a142-c8b1cf79cde6" -version = "0.21.3" +version = "0.21.4" -[[JuMP]] +[[deps.JuMP]] deps = ["Calculus", "DataStructures", "ForwardDiff", "LinearAlgebra", "MathOptInterface", "MutableArithmetics", "NaNMath", "OrderedCollections", "Printf", "SparseArrays", "SpecialFunctions"] git-tree-sha1 = "936e7ebf6c84f0c0202b83bb22461f4ebc5c9969" uuid = "4076af6c-e467-56ae-b986-b466b2749572" version = "1.0.0" -[[JuliaInterpreter]] -deps = ["CodeTracking", "InteractiveUtils", "Random", "UUIDs"] -git-tree-sha1 = "52617c41d2761cc05ed81fe779804d3b7f14fff7" -uuid = "aa1ae85d-cabe-5617-a682-6adf51b2e16a" -version = "0.9.13" - -[[LibCURL]] +[[deps.LibCURL]] deps = ["LibCURL_jll", "MozillaCACerts_jll"] uuid = "b27032c2-a3e7-50c8-80cd-2d36dbcbfd21" +version = "0.6.3" -[[LibCURL_jll]] +[[deps.LibCURL_jll]] deps = ["Artifacts", "LibSSH2_jll", "Libdl", "MbedTLS_jll", "Zlib_jll", "nghttp2_jll"] uuid = "deac9b47-8bc7-5906-a0fe-35ac56dc84c0" +version = "7.84.0+0" -[[LibGit2]] +[[deps.LibGit2]] deps = ["Base64", "NetworkOptions", "Printf", "SHA"] uuid = "76f85450-5226-5b5a-8eaa-529ad045b433" -[[LibSSH2_jll]] +[[deps.LibSSH2_jll]] deps = ["Artifacts", "Libdl", "MbedTLS_jll"] uuid = "29816b5a-b9ab-546f-933c-edad1886dfa8" +version = "1.10.2+0" -[[Libdl]] +[[deps.Libdl]] uuid = "8f399da3-3557-5675-b5ff-fb832c97cbdb" -[[LineSearches]] +[[deps.LineSearches]] deps = ["LinearAlgebra", "NLSolversBase", "NaNMath", "Parameters", "Printf"] -git-tree-sha1 = "f27132e551e959b3667d8c93eae90973225032dd" +git-tree-sha1 = "7bbea35cec17305fc70a0e5b4641477dc0789d9d" uuid = "d3d80556-e9d4-5f37-9878-2ab0fcc64255" -version = "7.1.1" +version = "7.2.0" -[[LinearAlgebra]] -deps = ["Libdl"] +[[deps.LinearAlgebra]] +deps = ["Libdl", "OpenBLAS_jll", "libblastrampoline_jll"] uuid = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" -[[LogExpFunctions]] -deps = ["ChainRulesCore", "ChangesOfVariables", "DocStringExtensions", "InverseFunctions", "IrrationalConstants", "LinearAlgebra"] -git-tree-sha1 = "09e4b894ce6a976c354a69041a04748180d43637" +[[deps.LogExpFunctions]] +deps = ["DocStringExtensions", "IrrationalConstants", "LinearAlgebra"] +git-tree-sha1 = "7d6dd4e9212aebaeed356de34ccf262a3cd415aa" uuid = "2ab3a3ac-af41-5b50-aa03-7779005ae688" -version = "0.3.15" +version = "0.3.26" -[[Logging]] + [deps.LogExpFunctions.extensions] + LogExpFunctionsChainRulesCoreExt = "ChainRulesCore" + LogExpFunctionsChangesOfVariablesExt = "ChangesOfVariables" + LogExpFunctionsInverseFunctionsExt = "InverseFunctions" + + [deps.LogExpFunctions.weakdeps] + ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" + ChangesOfVariables = "9e997f8a-9a97-42d5-a9f1-ce6bfc15e2c0" + InverseFunctions = "3587e190-3f89-42d0-90ee-14403ec27112" + +[[deps.Logging]] uuid = "56ddb016-857b-54e1-b83d-db4d58db5568" -[[LoggingExtras]] +[[deps.LoggingExtras]] deps = ["Dates", "Logging"] -git-tree-sha1 = "e9437ef53c3b29a838f4635e748bb38d29d11384" +git-tree-sha1 = "5d4d2d9904227b8bd66386c1138cf4d5ffa826bf" uuid = "e6f89c97-d47a-5376-807f-9c37f3926c36" -version = "0.4.8" - -[[LoweredCodeUtils]] -deps = ["JuliaInterpreter"] -git-tree-sha1 = "dedbebe234e06e1ddad435f5c6f4b85cd8ce55f7" -uuid = "6f1432cf-f94c-5a45-995e-cdbf5db27b0b" -version = "2.2.2" +version = "0.4.9" -[[METIS_jll]] +[[deps.METIS_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "1d31872bb9c5e7ec1f618e8c4a56c8b0d9bddc7e" +git-tree-sha1 = "1fd0a97409e418b78c53fac671cf4622efdf0f21" uuid = "d00139f3-1899-568f-a2f0-47f597d42d70" -version = "5.1.1+0" +version = "5.1.2+0" -[[MUMPS_seq_jll]] +[[deps.MIMEs]] +git-tree-sha1 = "65f28ad4b594aebe22157d6fac869786a255b7eb" +uuid = "6c6e2e6c-3030-632d-7369-2d6c69616d65" +version = "0.1.4" + +[[deps.MUMPS_seq_jll]] deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "Libdl", "METIS_jll", "OpenBLAS32_jll", "Pkg"] git-tree-sha1 = "29de2841fa5aefe615dea179fcde48bb87b58f57" uuid = "d7ed1dd3-d0ae-5e8e-bfb4-87a502085b8d" version = "5.4.1+0" -[[MacroTools]] +[[deps.MacroTools]] deps = ["Markdown", "Random"] -git-tree-sha1 = "3d3e902b31198a27340d0bf00d6ac452866021cf" +git-tree-sha1 = "9ee1618cbf5240e6d4e0371d6f24065083f60c48" uuid = "1914dd2f-81c6-5fcd-8719-6d5c9610ff09" -version = "0.5.9" +version = "0.5.11" -[[Markdown]] +[[deps.Markdown]] deps = ["Base64"] uuid = "d6f4376e-aef5-505a-96c1-9c027394607a" -[[MathOptInterface]] -deps = ["BenchmarkTools", "CodecBzip2", "CodecZlib", "JSON", "LinearAlgebra", "MutableArithmetics", "OrderedCollections", "Printf", "SparseArrays", "Test", "Unicode"] -git-tree-sha1 = "23c99cadd752cc0b70d4c74c969a679948b1bb6a" +[[deps.MathOptInterface]] +deps = ["BenchmarkTools", "CodecBzip2", "CodecZlib", "DataStructures", "ForwardDiff", "JSON", "LinearAlgebra", "MutableArithmetics", "NaNMath", "OrderedCollections", "PrecompileTools", "Printf", "SparseArrays", "SpecialFunctions", "Test", "Unicode"] +git-tree-sha1 = "70ea2892b8bfffecc0387ba1a6a21192814f120c" uuid = "b8f27783-ece8-5eb3-8dc8-9495eed66fee" -version = "1.2.0" +version = "1.22.0" -[[MbedTLS_jll]] +[[deps.MbedTLS_jll]] deps = ["Artifacts", "Libdl"] uuid = "c8ffd9c3-330d-5841-b78e-0817d7145fa1" +version = "2.28.2+0" -[[Memento]] +[[deps.Memento]] deps = ["Dates", "Distributed", "Requires", "Serialization", "Sockets", "Test", "UUIDs"] -git-tree-sha1 = "aa3dfe57792d69afc1191efb62fe513bf5d0c0c2" +git-tree-sha1 = "bb2e8f4d9f400f6e90d57b34860f6abdc51398e5" uuid = "f28f55f0-a522-5efc-85c2-fe41dfb9b2d9" -version = "1.3.1" +version = "1.4.1" -[[Mmap]] +[[deps.Mmap]] uuid = "a63ad114-7e13-5084-954f-fe012c677804" -[[MozillaCACerts_jll]] +[[deps.MozillaCACerts_jll]] uuid = "14a3606d-f60d-562e-9121-12d972cd8159" +version = "2022.10.11" -[[MutableArithmetics]] +[[deps.MutableArithmetics]] deps = ["LinearAlgebra", "SparseArrays", "Test"] -git-tree-sha1 = "4050cd02756970414dab13b55d55ae1826b19008" +git-tree-sha1 = "6985021d02ab8c509c841bb8b2becd3145a7b490" uuid = "d8a4904e-b15c-11e9-3269-09a3773c0cb0" -version = "1.0.2" +version = "1.3.3" -[[NLSolversBase]] +[[deps.NLSolversBase]] deps = ["DiffResults", "Distributed", "FiniteDiff", "ForwardDiff"] -git-tree-sha1 = "50310f934e55e5ca3912fb941dec199b49ca9b68" +git-tree-sha1 = "a0b464d183da839699f4c79e7606d9d186ec172c" uuid = "d41bc354-129a-5804-8e4c-c37616107c6c" -version = "7.8.2" +version = "7.8.3" -[[NLsolve]] +[[deps.NLsolve]] deps = ["Distances", "LineSearches", "LinearAlgebra", "NLSolversBase", "Printf", "Reexport"] git-tree-sha1 = "019f12e9a1a7880459d0173c182e6a99365d7ac1" uuid = "2774e3e8-f4cf-5e23-947b-6d7e65073b56" version = "4.5.1" -[[NaNMath]] -git-tree-sha1 = "b086b7ea07f8e38cf122f5016af580881ac914fe" +[[deps.NaNMath]] +deps = ["OpenLibm_jll"] +git-tree-sha1 = "0877504529a3e5c3343c6f8b4c0381e57e4387e4" uuid = "77ba4419-2d1f-58cd-9bb1-8ffee604a2e3" -version = "0.3.7" +version = "1.0.2" -[[NetworkOptions]] +[[deps.NetworkOptions]] uuid = "ca575930-c2e3-43a9-ace4-1e988b2c1908" +version = "1.2.0" -[[OpenBLAS32_jll]] +[[deps.OpenBLAS32_jll]] deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "ba4a8f683303c9082e84afba96f25af3c7fb2436" +git-tree-sha1 = "2fb9ee2dc14d555a6df2a714b86b7125178344c2" uuid = "656ef2d0-ae68-5445-9ca0-591084a874a2" -version = "0.3.12+1" +version = "0.3.21+0" -[[OpenLibm_jll]] +[[deps.OpenBLAS_jll]] +deps = ["Artifacts", "CompilerSupportLibraries_jll", "Libdl"] +uuid = "4536629a-c528-5b80-bd46-f80d51c5b363" +version = "0.3.21+4" + +[[deps.OpenLibm_jll]] deps = ["Artifacts", "Libdl"] uuid = "05823500-19ac-5b8b-9628-191a04bc5112" +version = "0.8.1+0" -[[OpenSpecFun_jll]] +[[deps.OpenSpecFun_jll]] deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "Libdl", "Pkg"] git-tree-sha1 = "13652491f6856acfd2db29360e1bbcd4565d04f1" uuid = "efe28fd5-8261-553b-a9e1-b2916fc3738e" version = "0.5.5+0" -[[OrderedCollections]] -git-tree-sha1 = "85f8e6578bf1f9ee0d11e7bb1b1456435479d47c" +[[deps.OrderedCollections]] +git-tree-sha1 = "2e73fe17cac3c62ad1aebe70d44c963c3cfdc3e3" uuid = "bac558e1-5e72-5ebc-8fee-abe8a469f55d" -version = "1.4.1" +version = "1.6.2" -[[Parameters]] +[[deps.Parameters]] deps = ["OrderedCollections", "UnPack"] git-tree-sha1 = "34c0e9ad262e5f7fc75b10a9952ca7692cfc5fbe" uuid = "d96e819e-fc66-5662-9728-84c9c7592b0a" version = "0.12.3" -[[Parsers]] -deps = ["Dates"] -git-tree-sha1 = "1285416549ccfcdf0c50d4997a94331e88d68413" +[[deps.Parsers]] +deps = ["Dates", "PrecompileTools", "UUIDs"] +git-tree-sha1 = "a935806434c9d4c506ba941871b327b96d41f2bf" uuid = "69de0a69-1ddd-5017-9359-2bf0b02dc9f0" -version = "2.3.1" +version = "2.8.0" -[[Pkg]] -deps = ["Artifacts", "Dates", "Downloads", "LibGit2", "Libdl", "Logging", "Markdown", "Printf", "REPL", "Random", "SHA", "Serialization", "TOML", "Tar", "UUIDs", "p7zip_jll"] +[[deps.Pkg]] +deps = ["Artifacts", "Dates", "Downloads", "FileWatching", "LibGit2", "Libdl", "Logging", "Markdown", "Printf", "REPL", "Random", "SHA", "Serialization", "TOML", "Tar", "UUIDs", "p7zip_jll"] uuid = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" +version = "1.9.0" -[[PlutoUI]] -deps = ["AbstractPlutoDingetjes", "Base64", "ColorTypes", "Dates", "Hyperscript", "HypertextLiteral", "IOCapture", "InteractiveUtils", "JSON", "Logging", "Markdown", "Random", "Reexport", "UUIDs"] -git-tree-sha1 = "8d1f54886b9037091edf146b517989fc4a09efec" +[[deps.PlutoUI]] +deps = ["AbstractPlutoDingetjes", "Base64", "ColorTypes", "Dates", "FixedPointNumbers", "Hyperscript", "HypertextLiteral", "IOCapture", "InteractiveUtils", "JSON", "Logging", "MIMEs", "Markdown", "Random", "Reexport", "URIs", "UUIDs"] +git-tree-sha1 = "db8ec28846dbf846228a32de5a6912c63e2052e3" uuid = "7f904dfe-b85e-4ff6-b463-dae2292396a8" -version = "0.7.39" +version = "0.7.53" -[[PolyhedralRelaxations]] +[[deps.PolyhedralRelaxations]] deps = ["DataStructures", "ForwardDiff", "JuMP", "Logging", "LoggingExtras"] -git-tree-sha1 = "fc2d9132d1c7df35ae7ac00afedf6524288fd98d" +git-tree-sha1 = "05f2adc696ae9a99be3de99dd8970d00a4dccefe" uuid = "2e741578-48fa-11ea-2d62-b52c946f73a0" -version = "0.3.4" +version = "0.3.5" -[[PooledArrays]] +[[deps.PooledArrays]] deps = ["DataAPI", "Future"] -git-tree-sha1 = "a6062fe4063cdafe78f4a0a81cfffb89721b30e7" +git-tree-sha1 = "36d8b4b899628fb92c2749eb488d884a926614d3" uuid = "2dfb63ee-cc39-5dd5-95bd-886bf059d720" -version = "1.4.2" +version = "1.4.3" -[[PowerModels]] +[[deps.PowerModels]] deps = ["InfrastructureModels", "JSON", "JuMP", "LinearAlgebra", "Memento", "NLsolve", "SparseArrays"] -git-tree-sha1 = "c680b66275025a7f3265ee356c95a9b644483150" +git-tree-sha1 = "1e4bd5e4dfe3d9677ff17c42dd77bf17da269cc0" uuid = "c36e90e8-916a-50a6-bd94-075b64ef4655" -version = "0.19.5" +version = "0.19.9" -[[PowerModelsDistribution]] -deps = ["CSV", "Dates", "FilePaths", "Glob", "InfrastructureModels", "JSON", "JuMP", "LinearAlgebra", "Logging", "LoggingExtras", "PolyhedralRelaxations", "Statistics"] -git-tree-sha1 = "ae1eb6eed3e17fce8893e77fa02965b1622a0fb4" +[[deps.PowerModelsDistribution]] +deps = ["CSV", "Dates", "FilePaths", "Glob", "InfrastructureModels", "JSON", "JuMP", "LinearAlgebra", "Logging", "LoggingExtras", "PolyhedralRelaxations", "SparseArrays", "SpecialFunctions", "Statistics"] +git-tree-sha1 = "c474857308af49d155d67352a42b523203ee7d9c" uuid = "d7431456-977f-11e9-2de3-97ff7677985e" -version = "0.14.4" +version = "0.15.1" -[[PowerModelsITD]] +[[deps.PowerModelsITD]] deps = ["InfrastructureModels", "JSON", "JuMP", "LinearAlgebra", "PowerModels", "PowerModelsDistribution"] -git-tree-sha1 = "8e470177468f725f37745b2ce594b80d9bcf9a48" +git-tree-sha1 = "2119baa316e80d23c87413c30e1e6ce745985da2" uuid = "615c3f80-b0cb-4ecd-88fe-27bee056c380" -version = "0.7.1" +version = "0.7.9" + +[[deps.PrecompileTools]] +deps = ["Preferences"] +git-tree-sha1 = "03b4c25b43cb84cee5c90aa9b5ea0a78fd848d2f" +uuid = "aea7be01-6a6a-4083-8856-8a6e6704d82a" +version = "1.2.0" -[[Preferences]] +[[deps.Preferences]] deps = ["TOML"] -git-tree-sha1 = "47e5f437cc0e7ef2ce8406ce1e7e24d44915f88d" +git-tree-sha1 = "00805cd429dcb4870060ff49ef443486c262e38e" uuid = "21216c6a-2e73-6563-6e65-726566657250" -version = "1.3.0" +version = "1.4.1" -[[Printf]] +[[deps.Printf]] deps = ["Unicode"] uuid = "de0858da-6303-5e67-8744-51eddeeeb8d7" -[[Profile]] +[[deps.Profile]] deps = ["Printf"] uuid = "9abbd945-dff8-562f-b5e8-e1ebf5ef1b79" -[[REPL]] +[[deps.REPL]] deps = ["InteractiveUtils", "Markdown", "Sockets", "Unicode"] uuid = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb" -[[Random]] -deps = ["Serialization"] +[[deps.Random]] +deps = ["SHA", "Serialization"] uuid = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" -[[Reexport]] +[[deps.Reexport]] git-tree-sha1 = "45e428421666073eab6f2da5c9d310d99bb12f9b" uuid = "189a3867-3050-52da-a836-e630ba90ab69" version = "1.2.2" -[[Requires]] +[[deps.Requires]] deps = ["UUIDs"] git-tree-sha1 = "838a3a4188e2ded87a4f9f184b4b0d78a1e91cb7" uuid = "ae029012-a4dd-5104-9daa-d747884805df" version = "1.3.0" -[[Revise]] -deps = ["CodeTracking", "Distributed", "FileWatching", "JuliaInterpreter", "LibGit2", "LoweredCodeUtils", "OrderedCollections", "Pkg", "REPL", "Requires", "UUIDs", "Unicode"] -git-tree-sha1 = "4d4239e93531ac3e7ca7e339f15978d0b5149d03" -uuid = "295af30f-e4ad-537b-8983-00126c2a3abe" -version = "3.3.3" - -[[SHA]] +[[deps.SHA]] uuid = "ea8e919c-243c-51af-8825-aaa63cd721ce" +version = "0.7.0" -[[SentinelArrays]] +[[deps.SentinelArrays]] deps = ["Dates", "Random"] -git-tree-sha1 = "6a2f7d70512d205ca8c7ee31bfa9f142fe74310c" +git-tree-sha1 = "0e7508ff27ba32f26cd459474ca2ede1bc10991f" uuid = "91c51154-3ec4-41a3-a24f-3f23e20d615c" -version = "1.3.12" +version = "1.4.1" -[[Serialization]] +[[deps.Serialization]] uuid = "9e88b42a-f829-5b0c-bbe9-9e923198166b" -[[SharedArrays]] -deps = ["Distributed", "Mmap", "Random", "Serialization"] -uuid = "1a1011a3-84de-559e-8e89-a11a2f7dc383" +[[deps.Setfield]] +deps = ["ConstructionBase", "Future", "MacroTools", "StaticArraysCore"] +git-tree-sha1 = "e2cc6d8c88613c05e1defb55170bf5ff211fbeac" +uuid = "efcf1570-3423-57d1-acb7-fd33fddbac46" +version = "1.1.1" -[[Sockets]] +[[deps.Sockets]] uuid = "6462fe0b-24de-5631-8697-dd941f90decc" -[[SparseArrays]] -deps = ["LinearAlgebra", "Random"] +[[deps.SparseArrays]] +deps = ["Libdl", "LinearAlgebra", "Random", "Serialization", "SuiteSparse_jll"] uuid = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" -[[SpecialFunctions]] -deps = ["ChainRulesCore", "IrrationalConstants", "LogExpFunctions", "OpenLibm_jll", "OpenSpecFun_jll"] -git-tree-sha1 = "bc40f042cfcc56230f781d92db71f0e21496dffd" +[[deps.SpecialFunctions]] +deps = ["IrrationalConstants", "LogExpFunctions", "OpenLibm_jll", "OpenSpecFun_jll"] +git-tree-sha1 = "e2cfc4012a19088254b3950b85c3c1d8882d864d" uuid = "276daf66-3868-5448-9aa4-cd146d93841b" -version = "2.1.5" +version = "2.3.1" + + [deps.SpecialFunctions.extensions] + SpecialFunctionsChainRulesCoreExt = "ChainRulesCore" -[[StaticArrays]] -deps = ["LinearAlgebra", "Random", "Statistics"] -git-tree-sha1 = "cd56bf18ed715e8b09f06ef8c6b781e6cdc49911" -uuid = "90137ffa-7385-5640-81b9-e52037218182" -version = "1.4.4" + [deps.SpecialFunctions.weakdeps] + ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" -[[Statistics]] +[[deps.StaticArraysCore]] +git-tree-sha1 = "36b3d696ce6366023a0ea192b4cd442268995a0d" +uuid = "1e83bf80-4336-4d27-bf5d-d5a4f845583c" +version = "1.4.2" + +[[deps.Statistics]] deps = ["LinearAlgebra", "SparseArrays"] uuid = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" +version = "1.9.0" -[[StatsAPI]] +[[deps.StatsAPI]] deps = ["LinearAlgebra"] -git-tree-sha1 = "c82aaa13b44ea00134f8c9c89819477bd3986ecd" +git-tree-sha1 = "1ff449ad350c9c4cbc756624d6f8a8c3ef56d3ed" uuid = "82ae8749-77ed-4fe6-ae5f-f523153014b0" -version = "1.3.0" +version = "1.7.0" -[[SuiteSparse]] +[[deps.SuiteSparse]] deps = ["Libdl", "LinearAlgebra", "Serialization", "SparseArrays"] uuid = "4607b0f0-06f3-5cda-b6b1-a6196a1729e9" -[[TOML]] +[[deps.SuiteSparse_jll]] +deps = ["Artifacts", "Libdl", "Pkg", "libblastrampoline_jll"] +uuid = "bea87d4a-7f5b-5778-9afe-8cc45184846c" +version = "5.10.1+6" + +[[deps.TOML]] deps = ["Dates"] uuid = "fa267f1f-6049-4f14-aa54-33bafae1ed76" +version = "1.0.3" -[[TableTraits]] +[[deps.TableTraits]] deps = ["IteratorInterfaceExtensions"] git-tree-sha1 = "c06b2f539df1c6efa794486abfb6ed2022561a39" uuid = "3783bdb8-4a98-5b6b-af9a-565f29a5fe9c" version = "1.0.1" -[[Tables]] -deps = ["DataAPI", "DataValueInterfaces", "IteratorInterfaceExtensions", "LinearAlgebra", "OrderedCollections", "TableTraits", "Test"] -git-tree-sha1 = "5ce79ce186cc678bbb5c5681ca3379d1ddae11a1" +[[deps.Tables]] +deps = ["DataAPI", "DataValueInterfaces", "IteratorInterfaceExtensions", "LinearAlgebra", "OrderedCollections", "TableTraits"] +git-tree-sha1 = "cb76cf677714c095e535e3501ac7954732aeea2d" uuid = "bd369af6-aec1-5ad0-b16a-f7cc5008161c" -version = "1.7.0" +version = "1.11.1" -[[Tar]] +[[deps.Tar]] deps = ["ArgTools", "SHA"] uuid = "a4e569a6-e804-4fa4-b0f3-eef7a1d5b13e" +version = "1.10.0" -[[Test]] +[[deps.Test]] deps = ["InteractiveUtils", "Logging", "Random", "Serialization"] uuid = "8dfed614-e22c-5e08-85e1-65c5234f0b40" -[[TranscodingStreams]] -deps = ["Random", "Test"] -git-tree-sha1 = "216b95ea110b5972db65aa90f88d8d89dcb8851c" +[[deps.TranscodingStreams]] +git-tree-sha1 = "1fbeaaca45801b4ba17c251dd8603ef24801dd84" uuid = "3bb67fe8-82b1-5028-8e26-92a6c54297fa" -version = "0.9.6" +version = "0.10.2" +weakdeps = ["Random", "Test"] -[[Tricks]] -git-tree-sha1 = "6bac775f2d42a611cdfcd1fb217ee719630c4175" + [deps.TranscodingStreams.extensions] + TestExt = ["Test", "Random"] + +[[deps.Tricks]] +git-tree-sha1 = "eae1bb484cd63b36999ee58be2de6c178105112f" uuid = "410a4b4d-49e4-4fbc-ab6d-cb71b17b3775" -version = "0.1.6" +version = "0.1.8" + +[[deps.URIs]] +git-tree-sha1 = "67db6cc7b3821e19ebe75791a9dd19c9b1188f2b" +uuid = "5c2747f8-b7ea-4ff2-ba2e-563bfd36b1d4" +version = "1.5.1" -[[UUIDs]] +[[deps.UUIDs]] deps = ["Random", "SHA"] uuid = "cf7118a7-6976-5b1a-9a39-7adc72f591a4" -[[UnPack]] +[[deps.UnPack]] git-tree-sha1 = "387c1f73762231e86e0c9c5443ce3b4a0a9a0c2b" uuid = "3a884ed6-31ef-47d7-9d2a-63182c4928ed" version = "1.0.2" -[[Unicode]] +[[deps.Unicode]] uuid = "4ec0a83e-493e-50e2-b9ac-8f72acf5a8f5" -[[WeakRefStrings]] +[[deps.WeakRefStrings]] deps = ["DataAPI", "InlineStrings", "Parsers"] git-tree-sha1 = "b1be2855ed9ed8eac54e5caff2afcdb442d52c23" uuid = "ea10d353-3f73-51f8-a26c-33c1cb351aa5" version = "1.4.2" -[[Zlib_jll]] +[[deps.WorkerUtilities]] +git-tree-sha1 = "cd1659ba0d57b71a464a29e64dbc67cfe83d54e7" +uuid = "76eceee3-57b5-4d4a-8e66-0e911cebbf60" +version = "1.6.1" + +[[deps.Zlib_jll]] deps = ["Libdl"] uuid = "83775a58-1f1d-513f-b197-d71354ab007a" +version = "1.2.13+0" + +[[deps.libblastrampoline_jll]] +deps = ["Artifacts", "Libdl"] +uuid = "8e850b90-86db-534c-a0d3-1478176c7d93" +version = "5.8.0+0" -[[nghttp2_jll]] +[[deps.nghttp2_jll]] deps = ["Artifacts", "Libdl"] uuid = "8e850ede-7688-5339-a07c-302acd2aaf8d" +version = "1.48.0+0" -[[p7zip_jll]] +[[deps.p7zip_jll]] deps = ["Artifacts", "Libdl"] uuid = "3f19e933-33d8-53b3-aaab-bd5110c3b7a0" +version = "17.4.0+0" """ # ╔═╡ Cell order: @@ -1561,7 +1652,7 @@ uuid = "3f19e933-33d8-53b3-aaab-bd5110c3b7a0" # ╟─36d78d29-1417-4c05-adf3-4560fbc0304e # ╠═4b293648-92aa-4ebb-9d33-a6707773f0b1 # ╟─6eec60ab-d093-4230-8374-01f6d5799e8d -# ╠═7592c3de-481f-4877-a362-56e0eaaf56b0 +# ╟─7592c3de-481f-4877-a362-56e0eaaf56b0 # ╟─cc24ea4a-656f-46aa-93db-2bc27d0e796e # ╠═e5c578f1-0c16-4b6c-83fb-045f8f18a8c4 # ╟─39d43eb3-ff43-4ef6-a30d-bd6ef8a632e0 diff --git a/src/PowerModelsITD.jl b/src/PowerModelsITD.jl index 53614de..ea83351 100755 --- a/src/PowerModelsITD.jl +++ b/src/PowerModelsITD.jl @@ -65,6 +65,6 @@ module PowerModelsITD include("prob/opfitd_dmld.jl") include("prob/opfitd_storage.jl") - # This must come last to support automated export. + # This must come last to support automated export. include("core/export.jl") end From 3feccb25e47fb1b5dca69a83f81be48478b2683e Mon Sep 17 00:00:00 2001 From: Juan Ospina Date: Wed, 15 Nov 2023 12:02:38 -0700 Subject: [PATCH 12/16] Prep for pull request to main repo. --- CHANGELOG.md | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c6175b0..661fa70 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # PowerModelsITD.jl Change Log -## staged Energy-Storage +## staged - Added `eng2math_passthrough` parameter to all `instantiate_model(..)` and `solve_X(...)` functions. - Added unit tests to `opftid_storage.jl` that test the correct operation of the `eng2math_passthrough` both in single network and multinetwork applications. @@ -20,10 +20,6 @@ - Updated BeginnersGuide.jl Pluto Notebook to latest versions. - Updated DOCs dependencies and fixed minor issues. -## staged - -- none. - ## v0.7.9 - Refactor initialization of boundary variables to round them up to 4 digits (decimal). From fcc1d11c8af028f89830bda1b680b1c21a0b2c5a Mon Sep 17 00:00:00 2001 From: Juan Ospina Date: Wed, 15 Nov 2023 13:24:34 -0700 Subject: [PATCH 13/16] DOC: fix doc error related to Vector Gumbo.HTMLNode --- docs/make.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/make.jl b/docs/make.jl index 2b972ff..c99c914 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -84,8 +84,8 @@ if !_FAST ) # edit existing html to replace :article with :iframe - doc.root[2][1][2][2] = iframe - # doc.root[2][1][1] = iframe + # doc.root[2][1][2][2] = iframe + doc.root[2][1][1] = iframe # Overwrite HTML open("docs/build/tutorials/$(base_name_splitted).html", "w") do io From acdea9531ec4556d37862cb9861b7dba70d70f73 Mon Sep 17 00:00:00 2001 From: Juan Ospina Date: Tue, 21 Nov 2023 11:38:26 -0700 Subject: [PATCH 14/16] FIX: coverage for new storage functions adding more comprehensive tests. --- src/core/objective_storage.jl | 8 +- ... case3_unbalanced_withBattery_mn_diff.dss} | 6 +- ... case3_unbalanced_withoutgen_mn_diff3.dss} | 2 +- test/data/distribution/load_profile_diff2.csv | 17 +-- test/data/distribution/load_profile_diff3.csv | 12 +++ test/opfitd_storage.jl | 102 ++++++++++++------ 6 files changed, 94 insertions(+), 53 deletions(-) rename test/data/distribution/{case3_balanced_withBattery_mn_diff.dss => case3_unbalanced_withBattery_mn_diff.dss} (90%) rename test/data/distribution/{case3_unbalanced_withoutgen_mn_diff2.dss => case3_unbalanced_withoutgen_mn_diff3.dss} (94%) create mode 100644 test/data/distribution/load_profile_diff3.csv diff --git a/src/core/objective_storage.jl b/src/core/objective_storage.jl index aa8ed5f..2aa3b92 100644 --- a/src/core/objective_storage.jl +++ b/src/core/objective_storage.jl @@ -125,7 +125,7 @@ function _objective_itd_min_fuel_cost_polynomial_linquad_storage(pmitd::Abstract for (n, nw_ref) in _PMD.nws(pmd) for (i, strg) in nw_ref[:storage] dsch = _PMD.var(pmd, n, :sd, i) # get discharge power value - strg_cost_dollar_per_pu = strg["cost"][1]*nw_ref[:settings]["sbase_default"] # convert from $/kWh -> $/pu + strg_cost_dollar_per_pu = strg["cost"][1]#*nw_ref[:settings]["sbase_default"] # convert from $/kWh -> $/pu strg_cost_dollar_per_pu = round(strg_cost_dollar_per_pu, digits=4) pmd_strg_cost[(n,i)] = strg_cost_dollar_per_pu*dsch # compute discharge cost end @@ -220,7 +220,7 @@ function _objective_itd_min_fuel_cost_polynomial_linquad_storage(pmitd::Abstract for (n, nw_ref) in _PMD.nws(pmd) for (i, strg) in nw_ref[:storage] dsch = _PMD.var(pmd, n, :sd, i) # get discharge power value - strg_cost_dollar_per_pu = strg["cost"][1]*nw_ref[:settings]["sbase_default"] # convert from $/kWh -> $/pu + strg_cost_dollar_per_pu = strg["cost"][1]#*nw_ref[:settings]["sbase_default"] # convert from $/kWh -> $/pu strg_cost_dollar_per_pu = round(strg_cost_dollar_per_pu, digits=4) pmd_strg_cost[(n,i)] = strg_cost_dollar_per_pu*dsch # compute discharge cost end @@ -321,7 +321,7 @@ function _objective_itd_min_fuel_cost_polynomial_nl_storage(pmitd::AbstractIVRPo for (n, nw_ref) in _PMD.nws(pmd) for (i, strg) in nw_ref[:storage] dsch = _PMD.var(pmd, n, :sd, i) # get discharge power value - strg_cost_dollar_per_pu = strg["cost"][1]*nw_ref[:settings]["sbase_default"] # convert from $/kWh -> $/pu + strg_cost_dollar_per_pu = strg["cost"][1]#*nw_ref[:settings]["sbase_default"] # convert from $/kWh -> $/pu strg_cost_dollar_per_pu = round(strg_cost_dollar_per_pu, digits=4) pmd_strg_cost[(n,i)] = strg_cost_dollar_per_pu*dsch # compute discharge cost end @@ -424,7 +424,7 @@ function _objective_itd_min_fuel_cost_polynomial_nl_storage(pmitd::AbstractPower for (n, nw_ref) in _PMD.nws(pmd) for (i, strg) in nw_ref[:storage] dsch = _PMD.var(pmd, n, :sd, i) # get discharge power value - strg_cost_dollar_per_pu = strg["cost"][1]*nw_ref[:settings]["sbase_default"] # convert from $/kWh -> $/pu + strg_cost_dollar_per_pu = strg["cost"][1]#*nw_ref[:settings]["sbase_default"] # convert from $/kWh -> $/pu strg_cost_dollar_per_pu = round(strg_cost_dollar_per_pu, digits=4) pmd_strg_cost[(n,i)] = strg_cost_dollar_per_pu*dsch # compute discharge cost end diff --git a/test/data/distribution/case3_balanced_withBattery_mn_diff.dss b/test/data/distribution/case3_unbalanced_withBattery_mn_diff.dss similarity index 90% rename from test/data/distribution/case3_balanced_withBattery_mn_diff.dss rename to test/data/distribution/case3_unbalanced_withBattery_mn_diff.dss index e44ec23..938b7cf 100755 --- a/test/data/distribution/case3_balanced_withBattery_mn_diff.dss +++ b/test/data/distribution/case3_unbalanced_withBattery_mn_diff.dss @@ -36,11 +36,9 @@ New Load.L1 phases=1 loadbus.1.0 ( 13.8 3 sqrt / ) kW=3000 kvar=1500 mod New Load.L2 phases=1 loadbus.2.0 ( 13.8 3 sqrt / ) kW=3000 kvar=1500 model=1 daily=ls1 New Load.L3 phases=1 loadbus.3.0 ( 13.8 3 sqrt / ) kW=3000 kvar=1500 model=1 daily=ls1 - !Battery System DEFINITIONS -New Storage.S1 phases=3 bus1=primary.1.2.3 kv=( 13.8 3 sqrt / ) kwhstored=0 kwhrated=500 kva=500 kvar=100 -~ %charge=100 %discharge=100 %effcharge=100 %effdischarge=100 %idlingkw=1 %r=0 %x=50 - +New Storage.S1 phases=3 bus1=loadbus.1.2.3 kv=( 13.8 3 sqrt / ) kwhstored=0.0 kwhrated=500 kva=500 kvar=0.0 +~ %charge=100 %discharge=100 %effcharge=100 %effdischarge=100 %idlingkw=0 %r=0 %x=50 Set VoltageBases = "230,13.8" Set tolerance=0.000001 diff --git a/test/data/distribution/case3_unbalanced_withoutgen_mn_diff2.dss b/test/data/distribution/case3_unbalanced_withoutgen_mn_diff3.dss similarity index 94% rename from test/data/distribution/case3_unbalanced_withoutgen_mn_diff2.dss rename to test/data/distribution/case3_unbalanced_withoutgen_mn_diff3.dss index 378d2af..ca8c491 100755 --- a/test/data/distribution/case3_unbalanced_withoutgen_mn_diff2.dss +++ b/test/data/distribution/case3_unbalanced_withoutgen_mn_diff3.dss @@ -30,7 +30,7 @@ New Line.Quad Bus1=Primary.1.2.3 loadbus.1.2.3 linecode = 4/0QUAD length=1 !Loads - single phase -New Loadshape.ls1 pmult=(file=load_profile_diff2.csv) +New Loadshape.ls1 pmult=(file=load_profile_diff3.csv) New Load.L1 phases=1 loadbus.1.0 ( 13.8 3 sqrt / ) kW=4000 kvar=1333.33 model=1 daily=ls1 New Load.L2 phases=1 loadbus.2.0 ( 13.8 3 sqrt / ) kW=3000 kvar=1500 model=1 daily=ls1 diff --git a/test/data/distribution/load_profile_diff2.csv b/test/data/distribution/load_profile_diff2.csv index 1a52ce4..2cda803 100644 --- a/test/data/distribution/load_profile_diff2.csv +++ b/test/data/distribution/load_profile_diff2.csv @@ -1,12 +1,5 @@ -0.1 -0.05 -0.05 -0.05 -0.3 -0.4 -0.5 -0.7 -1.1 -1.6 -0.7 -0.8 +1.2 +0.01 +1.2 +1.5 +1.2 diff --git a/test/data/distribution/load_profile_diff3.csv b/test/data/distribution/load_profile_diff3.csv new file mode 100644 index 0000000..1a52ce4 --- /dev/null +++ b/test/data/distribution/load_profile_diff3.csv @@ -0,0 +1,12 @@ +0.1 +0.05 +0.05 +0.05 +0.3 +0.4 +0.5 +0.7 +1.1 +1.6 +0.7 +0.8 diff --git a/test/opfitd_storage.jl b/test/opfitd_storage.jl index 9fcf3bf..a08e8af 100644 --- a/test/opfitd_storage.jl +++ b/test/opfitd_storage.jl @@ -50,7 +50,7 @@ @test storage_ref_nw4[1]["cost"] == 0.25 end - @testset "solve_model (build_opfitd_storage): Balanced case5-case3 With Battery ACP-ACPU " begin + @testset "solve_opfitd_storage: Balanced case5-case3 With Battery ACP-ACPU " begin pm_file = joinpath(dirname(trans_path), "case5_withload.m") pmd_file = joinpath(dirname(dist_path), "case3_balanced_withBattery.dss") pmitd_file = joinpath(dirname(bound_path), "case5_case3_bal_battery.json") @@ -66,25 +66,40 @@ st_data["cost"] = strg_cost end - # eng2math_passthrough - eng2math_passthrough = Dict("storage"=>["cost"]) - # with storage cost problem - pmitd_result_strg = solve_model(pmitd_data, pmitd_type, ipopt, build_opfitd_storage; eng2math_passthrough=eng2math_passthrough) - @test isapprox(pmitd_result_strg["objective"], 17977.48172498742+(pmitd_result_strg["solution"]["it"]["pmd"]["storage"]["3bus_bal_battery.s1"]["sd"]*strg_cost); atol = 1e-3) + pmitd_result_strg = solve_opfitd_storage(pmitd_data, pmitd_type, ipopt) + @test isapprox(pmitd_result_strg["objective"], 17977.48172498742+(pmitd_result_strg["solution"]["it"]["pmd"]["storage"]["3bus_bal_battery.s1"]["sd"]*strg_cost/100000); atol = 1e-3) end - @testset "solve_model (build_mn_opfitd_storage): Multinetwork Balanced case5-case3 With Battery ACP-ACPU " begin - pm_file = joinpath(dirname(trans_path), "case5_withload.m") + @testset "solve_opfitd_storage with Transmission storage: Balanced case5-case3 With Battery ACP-ACPU " begin + pm_file = joinpath(dirname(trans_path), "case5_withload_strg.m") + pmd_file = joinpath(dirname(dist_path), "case3_balanced_withBattery.dss") + pmitd_file = joinpath(dirname(bound_path), "case5_case3_bal_battery.json") + pmitd_type = NLPowerModelITD{ACPPowerModel, ACPUPowerModel} + pmitd_result_strg = solve_opfitd_storage(pm_file, pmd_file, pmitd_file, pmitd_type, ipopt) + @test pmitd_result_strg["termination_status"] == LOCALLY_SOLVED + end + + @testset "solve_mn_opfitd_storage with Transmission storage: Multinetwork Balanced case5-case3 With Battery ACP-ACPU " begin + pm_file = joinpath(dirname(trans_path), "case5_withload_strg.m") pmd_file = joinpath(dirname(dist_path), "case3_balanced_withBattery_mn.dss") pmitd_file = joinpath(dirname(bound_path), "case5_case3_bal_battery_mn.json") pmitd_type = NLPowerModelITD{ACPPowerModel, ACPUPowerModel} + pmitd_result_strg = solve_mn_opfitd_storage(pm_file, pmd_file, pmitd_file, pmitd_type, ipopt) + @test pmitd_result_strg["termination_status"] == LOCALLY_SOLVED + end + + @testset "solve_mn_opfitd_storage: Multinetwork Balanced case5-case3 With Battery ACP-ACPU - very cheap storage cost" begin + pm_file = joinpath(dirname(trans_path), "case5_withload.m") + pmd_file = joinpath(dirname(dist_path), "case3_unbalanced_withBattery_mn_diff.dss") + pmitd_file = joinpath(dirname(bound_path), "case5_case3_bal_battery_mn.json") + pmitd_type = NLPowerModelITD{ACPPowerModel, ACPUPowerModel} pmitd_data = parse_files(pm_file, pmd_file, pmitd_file; multinetwork=true) # cost to assign to energy storage # Units $/kWh - strg_cost = 0.025 + strg_cost = 0.001 # add cost to storages in PMD for (nw_id, nw_data) in pmitd_data["it"]["pmd"]["nw"] @@ -93,34 +108,37 @@ end end - # instantiate model with eng2math_passthrough - eng2math_passthrough = Dict("storage"=>["cost"]) - - # with storage cost problem - pmitd_result_strg = solve_model(pmitd_data, pmitd_type, ipopt, build_mn_opfitd_storage; multinetwork=true, eng2math_passthrough=eng2math_passthrough) + pmitd_result_strg = solve_mn_opfitd_storage(pmitd_data, pmitd_type, ipopt) # add solution cost of the batteries - sln_batt_cost = 0 + result_soln_dsch = zeros(5) + result_soln_ch = zeros(5) + result_soln_ps = zeros(5) for (nw_id, nw_data) in pmitd_result_strg["solution"]["it"]["pmd"]["nw"] for (st_name, st_data) in nw_data["storage"] - sln_batt_cost += st_data["sd"]*strg_cost + result_soln_dsch[parse(Int64, nw_id)] = st_data["sd"] + result_soln_ch[parse(Int64, nw_id)] = st_data["sc"] + result_soln_ps[parse(Int64, nw_id)] = sum(st_data["ps"]) end end - @test isapprox(pmitd_result_strg["objective"], 71226.99645541582+(sln_batt_cost); atol = 1e-3) + @test isapprox(pmitd_result_strg["solution"]["it"]["pmd"]["nw"]["4"]["storage"]["3bus_bal_battery_mn.s1"]["sd"], 500.0; atol = 1e-1) + @test isapprox(result_soln_dsch[4], 500.0; atol = 1e-1) + @test isapprox(pmitd_result_strg["solution"]["it"]["pmd"]["nw"]["2"]["storage"]["3bus_bal_battery_mn.s1"]["sc"], 500.0; atol = 1e-1) + @test isapprox(result_soln_ch[2], 500.0; atol = 1e-1) end - @testset "solve_mn_opfitd_storage: Multinetwork Balanced case5-case3 With Battery ACP-ACPU " begin + @testset "solve_mn_opfitd_storage: Multinetwork Balanced case5-case3 With Battery ACP-ACPU - very expensive storage cost" begin pm_file = joinpath(dirname(trans_path), "case5_withload.m") - pmd_file = joinpath(dirname(dist_path), "case3_balanced_withBattery_mn_diff.dss") + pmd_file = joinpath(dirname(dist_path), "case3_unbalanced_withBattery_mn_diff.dss") pmitd_file = joinpath(dirname(bound_path), "case5_case3_bal_battery_mn.json") pmitd_type = NLPowerModelITD{ACPPowerModel, ACPUPowerModel} pmitd_data = parse_files(pm_file, pmd_file, pmitd_file; multinetwork=true) # cost to assign to energy storage # Units $/kWh - strg_cost = 0.0025 + strg_cost = 100000000 # add cost to storages in PMD for (nw_id, nw_data) in pmitd_data["it"]["pmd"]["nw"] @@ -130,14 +148,30 @@ end pmitd_result_strg = solve_mn_opfitd_storage(pmitd_data, pmitd_type, ipopt) - @test isapprox(pmitd_result_strg["solution"]["it"]["pmd"]["nw"]["10"]["storage"]["3bus_bal_battery_mn.s1"]["sd"], 500.0; atol = 1e-1) + + # add solution cost of the batteries + result_soln_dsch = zeros(5) + result_soln_ch = zeros(5) + result_soln_ps = zeros(5) + for (nw_id, nw_data) in pmitd_result_strg["solution"]["it"]["pmd"]["nw"] + for (st_name, st_data) in nw_data["storage"] + result_soln_dsch[parse(Int64, nw_id)] = st_data["sd"] + result_soln_ch[parse(Int64, nw_id)] = st_data["sc"] + result_soln_ps[parse(Int64, nw_id)] = sum(st_data["ps"]) + end + end + + @test isapprox(pmitd_result_strg["solution"]["it"]["pmd"]["nw"]["4"]["storage"]["3bus_bal_battery_mn.s1"]["sd"], 0.0; atol = 1e-1) + @test isapprox(result_soln_dsch[4], 0.0; atol = 1e-1) + @test isapprox(pmitd_result_strg["solution"]["it"]["pmd"]["nw"]["2"]["storage"]["3bus_bal_battery_mn.s1"]["sc"], 0.0; atol = 1e-1) + @test isapprox(result_soln_ch[2], 0.0; atol = 1e-1) end ## IVRU is missing critical current variables (real currentr) for solving opfs with storage in PMD # @testset "solve_mn_opfitd_storage: Multinetwork Balanced case5-case3 With Battery IVR-IVRU " begin # pm_file = joinpath(dirname(trans_path), "case5_withload.m") - # pmd_file = joinpath(dirname(dist_path), "case3_balanced_withBattery_mn_diff.dss") + # pmd_file = joinpath(dirname(dist_path), "case3_unbalanced_withBattery_mn_diff.dss") # pmitd_file = joinpath(dirname(bound_path), "case5_case3_bal_battery_mn.json") # pmitd_type = IVRPowerModelITD{IVRPowerModel, IVRUPowerModel} # pmitd_data = parse_files(pm_file, pmd_file, pmitd_file; multinetwork=true) @@ -160,14 +194,14 @@ @testset "solve_mn_opfitd_storage: Multinetwork Balanced case5-case3 With Battery ACR-FBSUBF " begin pm_file = joinpath(dirname(trans_path), "case5_withload.m") - pmd_file = joinpath(dirname(dist_path), "case3_balanced_withBattery_mn_diff.dss") + pmd_file = joinpath(dirname(dist_path), "case3_unbalanced_withBattery_mn_diff.dss") pmitd_file = joinpath(dirname(bound_path), "case5_case3_bal_battery_mn.json") pmitd_type = NLBFPowerModelITD{ACRPowerModel, FBSUBFPowerModel} pmitd_data = parse_files(pm_file, pmd_file, pmitd_file; multinetwork=true) # cost to assign to energy storage # Units $/kWh - strg_cost = 0.0025 + strg_cost = 0.001 # add cost to storages in PMD for (nw_id, nw_data) in pmitd_data["it"]["pmd"]["nw"] @@ -177,20 +211,21 @@ end pmitd_result_strg = solve_mn_opfitd_storage(pmitd_data, pmitd_type, ipopt) - @test isapprox(pmitd_result_strg["solution"]["it"]["pmd"]["nw"]["10"]["storage"]["3bus_bal_battery_mn.s1"]["sd"], 500.0; atol = 1e-1) + @test isapprox(pmitd_result_strg["solution"]["it"]["pmd"]["nw"]["4"]["storage"]["3bus_bal_battery_mn.s1"]["sd"], 500.0; atol = 1e-1) + @test isapprox(pmitd_result_strg["solution"]["it"]["pmd"]["nw"]["2"]["storage"]["3bus_bal_battery_mn.s1"]["sc"], 500.0; atol = 1e-1) end @testset "solve_mn_opfitd_storage: Multinetwork Balanced case5-case3 With Battery ACR-FOTRU " begin pm_file = joinpath(dirname(trans_path), "case5_withload.m") - pmd_file = joinpath(dirname(dist_path), "case3_balanced_withBattery_mn_diff.dss") + pmd_file = joinpath(dirname(dist_path), "case3_unbalanced_withBattery_mn_diff.dss") pmitd_file = joinpath(dirname(bound_path), "case5_case3_bal_battery_mn.json") pmitd_type = NLFOTPowerModelITD{ACRPowerModel, FOTRUPowerModel} pmitd_data = parse_files(pm_file, pmd_file, pmitd_file; multinetwork=true) # cost to assign to energy storage # Units $/kWh - strg_cost = 0.0025 + strg_cost = 0.001 # add cost to storages in PMD for (nw_id, nw_data) in pmitd_data["it"]["pmd"]["nw"] @@ -200,20 +235,21 @@ end pmitd_result_strg = solve_mn_opfitd_storage(pmitd_data, pmitd_type, ipopt) - @test isapprox(pmitd_result_strg["solution"]["it"]["pmd"]["nw"]["10"]["storage"]["3bus_bal_battery_mn.s1"]["sd"], 500.0; atol = 1e-1) + @test isapprox(pmitd_result_strg["solution"]["it"]["pmd"]["nw"]["4"]["storage"]["3bus_bal_battery_mn.s1"]["sd"], 500.0; atol = 1e-1) + @test isapprox(pmitd_result_strg["solution"]["it"]["pmd"]["nw"]["2"]["storage"]["3bus_bal_battery_mn.s1"]["sc"], 500.0; atol = 1e-1) end @testset "solve_mn_opfitd_storage: Multinetwork Balanced case5-case3 With Battery BFA-LinDist3Flow " begin pm_file = joinpath(dirname(trans_path), "case5_withload.m") - pmd_file = joinpath(dirname(dist_path), "case3_balanced_withBattery_mn_diff.dss") + pmd_file = joinpath(dirname(dist_path), "case3_unbalanced_withBattery_mn_diff.dss") pmitd_file = joinpath(dirname(bound_path), "case5_case3_bal_battery_mn.json") pmitd_type = BFPowerModelITD{BFAPowerModel, LinDist3FlowPowerModel} pmitd_data = parse_files(pm_file, pmd_file, pmitd_file; multinetwork=true) # cost to assign to energy storage # Units $/kWh - strg_cost = 0.0025 + strg_cost = 0.001 # add cost to storages in PMD for (nw_id, nw_data) in pmitd_data["it"]["pmd"]["nw"] @@ -223,14 +259,16 @@ end pmitd_result_strg = solve_mn_opfitd_storage(pmitd_data, pmitd_type, ipopt) + @test pmitd_result_strg["termination_status"] == LOCALLY_SOLVED # LinDist3Flow seems to have problems with Energy Storage control. - # @test isapprox(pmitd_result_strg["solution"]["it"]["pmd"]["nw"]["10"]["storage"]["3bus_bal_battery_mn.s1"]["sd"], 500.0; atol = 1e-1) + # @test isapprox(pmitd_result_strg["solution"]["it"]["pmd"]["nw"]["4"]["storage"]["3bus_bal_battery_mn.s1"]["sd"], 500.0; atol = 1e-1) + # @test isapprox(pmitd_result_strg["solution"]["it"]["pmd"]["nw"]["2"]["storage"]["3bus_bal_battery_mn.s1"]["sc"], 500.0; atol = 1e-1) end @testset "solve_mn_opfitd_storage: Multinetwork Balanced case5-case3 With Battery in Transmission ACP-ACPU " begin pm_file = joinpath(dirname(trans_path), "case5_withload_strg.m") - pmd_file = joinpath(dirname(dist_path), "case3_unbalanced_withoutgen_mn_diff2.dss") + pmd_file = joinpath(dirname(dist_path), "case3_unbalanced_withoutgen_mn_diff3.dss") pmitd_file = joinpath(dirname(bound_path), "case5_case3_unbal_nogen_mn_diff.json") pmitd_type = NLPowerModelITD{ACPPowerModel, ACPUPowerModel} pmitd_data = parse_files(pm_file, pmd_file, pmitd_file; multinetwork=true) From 0319ce60067749cbd48a47456f9bb7dc5c24c9f3 Mon Sep 17 00:00:00 2001 From: Juan Ospina Date: Wed, 22 Nov 2023 14:02:01 -0700 Subject: [PATCH 15/16] ADD: unit tests coverage to new storage probs and objectives. --- src/core/objective_storage.jl | 336 +++++++++++---------- src/prob/opfitd_storage.jl | 552 +++++++++++++++++----------------- test/opfitd_storage.jl | 99 +++++- 3 files changed, 544 insertions(+), 443 deletions(-) diff --git a/src/core/objective_storage.jl b/src/core/objective_storage.jl index 2aa3b92..af81c49 100644 --- a/src/core/objective_storage.jl +++ b/src/core/objective_storage.jl @@ -162,86 +162,88 @@ Fuel cost minimization objective for polynomial terms linear-quadratic (IVR form """ function _objective_itd_min_fuel_cost_polynomial_linquad_storage(pmitd::AbstractIVRPowerModelITD, pm::_PM.AbstractIVRModel, pmd::_PMD.AbstractUnbalancedIVRModel) - # PM - pm_gen_cost = Dict() - - for (n, nw_ref) in _PM.nws(pm) - for (i,gen) in nw_ref[:gen] - bus = gen["gen_bus"] - - #to avoid function calls inside of @NLconstraint: - pg = _PM.var(pm, n, :pg, i) - if length(gen["cost"]) == 1 - pm_gen_cost[(n,i)] = gen["cost"][1] - elseif length(gen["cost"]) == 2 - pm_gen_cost[(n,i)] = JuMP.@NLexpression(pm.model, gen["cost"][1]*pg + gen["cost"][2]) - elseif length(gen["cost"]) == 3 - pm_gen_cost[(n,i)] = JuMP.@NLexpression(pm.model, gen["cost"][1]*pg^2 + gen["cost"][2]*pg + gen["cost"][3]) - else - pm_gen_cost[(n,i)] = 0.0 - end - end - end - - # PM Storage - pm_strg_cost = Dict() - - for (n, nw_ref) in _PM.nws(pm) - for (i, strg) in nw_ref[:storage] - dsch = _PM.var(pm, n, :sd, i) # get discharge power value - pm_strg_cost[(n,i)] = strg["cost"][1]*dsch # compute discharge cost (no cost conversion is needed, cost must be in $/pu) - end - end - - # PMD - pmd_gen_cost = Dict() - - for (n, nw_ref) in _PMD.nws(pmd) - for (i,gen) in nw_ref[:gen] - bus = gen["gen_bus"] - - #to avoid function calls inside of @NLconstraint: - pg = _PMD.var(pmd, n, :pg, i) - if length(gen["cost"]) == 1 - pmd_gen_cost[(n,i)] = gen["cost"][1] - elseif length(gen["cost"]) == 2 - pmd_gen_cost[(n,i)] = JuMP.@NLexpression(pmd.model, gen["cost"][1]*sum(pg[c] for c in gen["connections"]) + gen["cost"][2]) - elseif length(gen["cost"]) == 3 - pmd_gen_cost[(n,i)] = JuMP.@NLexpression(pmd.model, gen["cost"][1]*sum(pg[c] for c in gen["connections"])^2 + gen["cost"][2]*sum(pg[c] for c in gen["connections"]) + gen["cost"][3]) - else - pmd_gen_cost[(n,i)] = 0.0 - end - end - end - - # PMD Storage - pmd_strg_cost = Dict() - - for (n, nw_ref) in _PMD.nws(pmd) - for (i, strg) in nw_ref[:storage] - dsch = _PMD.var(pmd, n, :sd, i) # get discharge power value - strg_cost_dollar_per_pu = strg["cost"][1]#*nw_ref[:settings]["sbase_default"] # convert from $/kWh -> $/pu - strg_cost_dollar_per_pu = round(strg_cost_dollar_per_pu, digits=4) - pmd_strg_cost[(n,i)] = strg_cost_dollar_per_pu*dsch # compute discharge cost - end - end - - - # ITD (Combined objective) - return JuMP.@NLobjective(pmitd.model, Min, - sum( - sum( pm_gen_cost[(n,i)] for (i,gen) in nw_ref[:gen] ) - for (n, nw_ref) in _PM.nws(pm)) - + sum( - sum( pm_strg_cost[(n,i)] for (i,strg) in nw_ref[:storage] ) - for (n, nw_ref) in _PM.nws(pm)) - + sum( - sum( pmd_gen_cost[(n,i)] for (i,gen) in nw_ref[:gen] ) - for (n, nw_ref) in _PMD.nws(pmd)) - + sum( - sum( pmd_strg_cost[(n,i)] for (i,strg) in nw_ref[:storage] ) - for (n, nw_ref) in _PMD.nws(pmd)) - ) + @error "IVR-IVRU formulation not yet supported for storage problems." + + # # PM + # pm_gen_cost = Dict() + + # for (n, nw_ref) in _PM.nws(pm) + # for (i,gen) in nw_ref[:gen] + # bus = gen["gen_bus"] + + # #to avoid function calls inside of @NLconstraint: + # pg = _PM.var(pm, n, :pg, i) + # if length(gen["cost"]) == 1 + # pm_gen_cost[(n,i)] = gen["cost"][1] + # elseif length(gen["cost"]) == 2 + # pm_gen_cost[(n,i)] = JuMP.@NLexpression(pm.model, gen["cost"][1]*pg + gen["cost"][2]) + # elseif length(gen["cost"]) == 3 + # pm_gen_cost[(n,i)] = JuMP.@NLexpression(pm.model, gen["cost"][1]*pg^2 + gen["cost"][2]*pg + gen["cost"][3]) + # else + # pm_gen_cost[(n,i)] = 0.0 + # end + # end + # end + + # # PM Storage + # pm_strg_cost = Dict() + + # for (n, nw_ref) in _PM.nws(pm) + # for (i, strg) in nw_ref[:storage] + # dsch = _PM.var(pm, n, :sd, i) # get discharge power value + # pm_strg_cost[(n,i)] = strg["cost"][1]*dsch # compute discharge cost (no cost conversion is needed, cost must be in $/pu) + # end + # end + + # # PMD + # pmd_gen_cost = Dict() + + # for (n, nw_ref) in _PMD.nws(pmd) + # for (i,gen) in nw_ref[:gen] + # bus = gen["gen_bus"] + + # #to avoid function calls inside of @NLconstraint: + # pg = _PMD.var(pmd, n, :pg, i) + # if length(gen["cost"]) == 1 + # pmd_gen_cost[(n,i)] = gen["cost"][1] + # elseif length(gen["cost"]) == 2 + # pmd_gen_cost[(n,i)] = JuMP.@NLexpression(pmd.model, gen["cost"][1]*sum(pg[c] for c in gen["connections"]) + gen["cost"][2]) + # elseif length(gen["cost"]) == 3 + # pmd_gen_cost[(n,i)] = JuMP.@NLexpression(pmd.model, gen["cost"][1]*sum(pg[c] for c in gen["connections"])^2 + gen["cost"][2]*sum(pg[c] for c in gen["connections"]) + gen["cost"][3]) + # else + # pmd_gen_cost[(n,i)] = 0.0 + # end + # end + # end + + # # PMD Storage + # pmd_strg_cost = Dict() + + # for (n, nw_ref) in _PMD.nws(pmd) + # for (i, strg) in nw_ref[:storage] + # dsch = _PMD.var(pmd, n, :sd, i) # get discharge power value + # strg_cost_dollar_per_pu = strg["cost"][1]#*nw_ref[:settings]["sbase_default"] # convert from $/kWh -> $/pu + # strg_cost_dollar_per_pu = round(strg_cost_dollar_per_pu, digits=4) + # pmd_strg_cost[(n,i)] = strg_cost_dollar_per_pu*dsch # compute discharge cost + # end + # end + + + # # ITD (Combined objective) + # return JuMP.@NLobjective(pmitd.model, Min, + # sum( + # sum( pm_gen_cost[(n,i)] for (i,gen) in nw_ref[:gen] ) + # for (n, nw_ref) in _PM.nws(pm)) + # + sum( + # sum( pm_strg_cost[(n,i)] for (i,strg) in nw_ref[:storage] ) + # for (n, nw_ref) in _PM.nws(pm)) + # + sum( + # sum( pmd_gen_cost[(n,i)] for (i,gen) in nw_ref[:gen] ) + # for (n, nw_ref) in _PMD.nws(pmd)) + # + sum( + # sum( pmd_strg_cost[(n,i)] for (i,strg) in nw_ref[:storage] ) + # for (n, nw_ref) in _PMD.nws(pmd)) + # ) end @@ -257,92 +259,94 @@ Fuel cost minimization objective for polynomial terms non-linear (IVR formulatio """ function _objective_itd_min_fuel_cost_polynomial_nl_storage(pmitd::AbstractIVRPowerModelITD, pm::_PM.AbstractIVRModel, pmd::_PMD.AbstractUnbalancedIVRModel) - # PM - pm_gen_cost = Dict() - for (n, nw_ref) in _PM.nws(pm) - for (i,gen) in nw_ref[:gen] - bus = gen["gen_bus"] - - #to avoid function calls inside of @NLconstraint: - pg = _PM.var(pm, n, :pg, i) - cost_rev = reverse(gen["cost"]) - if length(cost_rev) == 1 - pm_gen_cost[(n,i)] = JuMP.@NLexpression(pmitd.model, cost_rev[1]) - elseif length(cost_rev) == 2 - pm_gen_cost[(n,i)] = JuMP.@NLexpression(pmitd.model, cost_rev[1] + cost_rev[2]*pg) - elseif length(cost_rev) == 3 - pm_gen_cost[(n,i)] = JuMP.@NLexpression(pmitd.model, cost_rev[1] + cost_rev[2]*pg + cost_rev[3]*pg^2) - elseif length(cost_rev) >= 4 - cost_rev_nl = cost_rev[4:end] - pm_gen_cost[(n,i)] = JuMP.@NLexpression(pmitd.model, cost_rev[1] + cost_rev[2]*pg + cost_rev[3]*pg^2 + sum( v*pg^(d+3) for (d,v) in enumerate(cost_rev_nl)) ) - else - pm_gen_cost[(n,i)] = JuMP.@NLexpression(pmitd.model, 0.0) - end - end - end - - # PM Storage - pm_strg_cost = Dict() - - for (n, nw_ref) in _PM.nws(pm) - for (i, strg) in nw_ref[:storage] - dsch = _PM.var(pm, n, :sd, i) # get discharge power value - pm_strg_cost[(n,i)] = strg["cost"][1]*dsch # compute discharge cost (no cost conversion is needed, cost must be in $/pu) - end - end - - - # PMD - pmd_gen_cost = Dict() - for (n, nw_ref) in _PMD.nws(pmd) - for (i,gen) in nw_ref[:gen] - - pg = _PMD.var(pmd, n, :pg, i) - cost_rev = reverse(gen["cost"]) - - if length(cost_rev) == 1 - pmd_gen_cost[(n,i)] = JuMP.@NLexpression(pmitd.model, cost_rev[1]) - elseif length(cost_rev) == 2 - pmd_gen_cost[(n,i)] = JuMP.@NLexpression(pmitd.model, cost_rev[1] + cost_rev[2]*sum(pg[c] for c in gen["connections"])) - elseif length(cost_rev) == 3 - pmd_gen_cost[(n,i)] = JuMP.@NLexpression(pmitd.model, cost_rev[1] + cost_rev[2]*sum(pg[c] for c in gen["connections"]) + cost_rev[3]*sum(pg[c] for c in gen["connections"])^2) - elseif length(cost_rev) >= 4 - cost_rev_nl = cost_rev[4:end] - pmd_gen_cost[(n,i)] = JuMP.@NLexpression(pmitd.model, cost_rev[1] + cost_rev[2]*sum(pg[c] for c in gen["connections"]) + cost_rev[3]*sum(pg[c] for c in gen["connections"])^2 + sum( v*sum(pg[c] for c in gen["connections"])^(d+3) for (d,v) in enumerate(cost_rev_nl)) ) - else - pmd_gen_cost[(n,i)] = JuMP.@NLexpression(pmitd.model, 0.0) - end - end - end - - # PMD Storage - pmd_strg_cost = Dict() - - for (n, nw_ref) in _PMD.nws(pmd) - for (i, strg) in nw_ref[:storage] - dsch = _PMD.var(pmd, n, :sd, i) # get discharge power value - strg_cost_dollar_per_pu = strg["cost"][1]#*nw_ref[:settings]["sbase_default"] # convert from $/kWh -> $/pu - strg_cost_dollar_per_pu = round(strg_cost_dollar_per_pu, digits=4) - pmd_strg_cost[(n,i)] = strg_cost_dollar_per_pu*dsch # compute discharge cost - end - end - - - # ITD (Combined objective) - return JuMP.@NLobjective(pmitd.model, Min, - sum( - sum( pm_gen_cost[(n,i)] for (i,gen) in nw_ref[:gen] ) - for (n, nw_ref) in _PM.nws(pm)) - + sum( - sum( pm_strg_cost[(n,i)] for (i,strg) in nw_ref[:storage] ) - for (n, nw_ref) in _PM.nws(pm)) - + sum( - sum( pmd_gen_cost[(n,i)] for (i,gen) in nw_ref[:gen] ) - for (n, nw_ref) in _PMD.nws(pmd)) - + sum( - sum( pmd_strg_cost[(n,i)] for (i,strg) in nw_ref[:storage] ) - for (n, nw_ref) in _PMD.nws(pmd)) - ) + @error "IVR-IVRU formulation not yet supported for storage problems." + + # # PM + # pm_gen_cost = Dict() + # for (n, nw_ref) in _PM.nws(pm) + # for (i,gen) in nw_ref[:gen] + # bus = gen["gen_bus"] + + # #to avoid function calls inside of @NLconstraint: + # pg = _PM.var(pm, n, :pg, i) + # cost_rev = reverse(gen["cost"]) + # if length(cost_rev) == 1 + # pm_gen_cost[(n,i)] = JuMP.@NLexpression(pmitd.model, cost_rev[1]) + # elseif length(cost_rev) == 2 + # pm_gen_cost[(n,i)] = JuMP.@NLexpression(pmitd.model, cost_rev[1] + cost_rev[2]*pg) + # elseif length(cost_rev) == 3 + # pm_gen_cost[(n,i)] = JuMP.@NLexpression(pmitd.model, cost_rev[1] + cost_rev[2]*pg + cost_rev[3]*pg^2) + # elseif length(cost_rev) >= 4 + # cost_rev_nl = cost_rev[4:end] + # pm_gen_cost[(n,i)] = JuMP.@NLexpression(pmitd.model, cost_rev[1] + cost_rev[2]*pg + cost_rev[3]*pg^2 + sum( v*pg^(d+3) for (d,v) in enumerate(cost_rev_nl)) ) + # else + # pm_gen_cost[(n,i)] = JuMP.@NLexpression(pmitd.model, 0.0) + # end + # end + # end + + # # PM Storage + # pm_strg_cost = Dict() + + # for (n, nw_ref) in _PM.nws(pm) + # for (i, strg) in nw_ref[:storage] + # dsch = _PM.var(pm, n, :sd, i) # get discharge power value + # pm_strg_cost[(n,i)] = strg["cost"][1]*dsch # compute discharge cost (no cost conversion is needed, cost must be in $/pu) + # end + # end + + + # # PMD + # pmd_gen_cost = Dict() + # for (n, nw_ref) in _PMD.nws(pmd) + # for (i,gen) in nw_ref[:gen] + + # pg = _PMD.var(pmd, n, :pg, i) + # cost_rev = reverse(gen["cost"]) + + # if length(cost_rev) == 1 + # pmd_gen_cost[(n,i)] = JuMP.@NLexpression(pmitd.model, cost_rev[1]) + # elseif length(cost_rev) == 2 + # pmd_gen_cost[(n,i)] = JuMP.@NLexpression(pmitd.model, cost_rev[1] + cost_rev[2]*sum(pg[c] for c in gen["connections"])) + # elseif length(cost_rev) == 3 + # pmd_gen_cost[(n,i)] = JuMP.@NLexpression(pmitd.model, cost_rev[1] + cost_rev[2]*sum(pg[c] for c in gen["connections"]) + cost_rev[3]*sum(pg[c] for c in gen["connections"])^2) + # elseif length(cost_rev) >= 4 + # cost_rev_nl = cost_rev[4:end] + # pmd_gen_cost[(n,i)] = JuMP.@NLexpression(pmitd.model, cost_rev[1] + cost_rev[2]*sum(pg[c] for c in gen["connections"]) + cost_rev[3]*sum(pg[c] for c in gen["connections"])^2 + sum( v*sum(pg[c] for c in gen["connections"])^(d+3) for (d,v) in enumerate(cost_rev_nl)) ) + # else + # pmd_gen_cost[(n,i)] = JuMP.@NLexpression(pmitd.model, 0.0) + # end + # end + # end + + # # PMD Storage + # pmd_strg_cost = Dict() + + # for (n, nw_ref) in _PMD.nws(pmd) + # for (i, strg) in nw_ref[:storage] + # dsch = _PMD.var(pmd, n, :sd, i) # get discharge power value + # strg_cost_dollar_per_pu = strg["cost"][1]#*nw_ref[:settings]["sbase_default"] # convert from $/kWh -> $/pu + # strg_cost_dollar_per_pu = round(strg_cost_dollar_per_pu, digits=4) + # pmd_strg_cost[(n,i)] = strg_cost_dollar_per_pu*dsch # compute discharge cost + # end + # end + + + # # ITD (Combined objective) + # return JuMP.@NLobjective(pmitd.model, Min, + # sum( + # sum( pm_gen_cost[(n,i)] for (i,gen) in nw_ref[:gen] ) + # for (n, nw_ref) in _PM.nws(pm)) + # + sum( + # sum( pm_strg_cost[(n,i)] for (i,strg) in nw_ref[:storage] ) + # for (n, nw_ref) in _PM.nws(pm)) + # + sum( + # sum( pmd_gen_cost[(n,i)] for (i,gen) in nw_ref[:gen] ) + # for (n, nw_ref) in _PMD.nws(pmd)) + # + sum( + # sum( pmd_strg_cost[(n,i)] for (i,strg) in nw_ref[:storage] ) + # for (n, nw_ref) in _PMD.nws(pmd)) + # ) end diff --git a/src/prob/opfitd_storage.jl b/src/prob/opfitd_storage.jl index 7b1f865..11699c8 100755 --- a/src/prob/opfitd_storage.jl +++ b/src/prob/opfitd_storage.jl @@ -270,123 +270,125 @@ Constructor for Integrated T&D Optimal Power Flow in current-voltage (IV) variab """ function build_opfitd_storage(pmitd::AbstractIVRPowerModelITD) - # Get Models - pm_model = _get_powermodel_from_powermodelitd(pmitd) - pmd_model = _get_powermodeldistribution_from_powermodelitd(pmitd) - - # PM(Transmission) Variables - _PM.variable_bus_voltage(pm_model) - _PM.variable_branch_current(pm_model) - _PM.variable_gen_current(pm_model) - _PM.variable_dcline_current(pm_model) - _PM.variable_storage_power(pm_model) - - # PMD(Distribution) Variables - _PMD.variable_mc_bus_voltage(pmd_model) - _PMD.variable_mc_branch_current(pmd_model) - _PMD.variable_mc_switch_current(pmd_model) - _PMD.variable_mc_transformer_current(pmd_model) - _PMD.variable_mc_generator_current(pmd_model) - _PMD.variable_mc_load_current(pmd_model) - - # PMITD (Boundary) Current Variables - variable_boundary_current(pmitd) - - # --- PM(Transmission) Constraints --- - # reference buses (this only needs to happen for pm(transmission)) - for i in _PM.ids(pm_model, :ref_buses) - _PM.constraint_theta_ref(pm_model, i) - end - - for i in _PM.ids(pm_model, :storage) - _PM.constraint_storage_state(pm_model, i) - _PM.constraint_storage_complementarity_nl(pm_model, i) - _PM.constraint_storage_losses(pm_model, i) - _PM.constraint_storage_thermal_limit(pm_model, i) - end - - for i in _PM.ids(pm_model, :branch) - _PM.constraint_current_from(pm_model, i) - _PM.constraint_current_to(pm_model, i) - - _PM.constraint_voltage_drop(pm_model, i) - _PM.constraint_voltage_angle_difference(pm_model, i) - - _PM.constraint_thermal_limit_from(pm_model, i) - _PM.constraint_thermal_limit_to(pm_model, i) - end - - for i in _PM.ids(pm_model, :dcline) - _PM.constraint_dcline_power_losses(pm_model, i) - end - - - # --- PMD(Distribution) Constraints --- - # gens should be constrained before KCL, or Pd/Qd undefined - for id in _PMD.ids(pmd_model, :gen) - _PMD.constraint_mc_generator_power(pmd_model, id) - end - - # loads should be constrained before KCL, or Pd/Qd undefined - for id in _PMD.ids(pmd_model, :load) - _PMD.constraint_mc_load_power(pmd_model, id) - end - - - for i in _PMD.ids(pmd_model, :branch) - _PMD.constraint_mc_current_from(pmd_model, i) - _PMD.constraint_mc_current_to(pmd_model, i) - _PMD.constraint_mc_bus_voltage_drop(pmd_model, i) - _PMD.constraint_mc_voltage_angle_difference(pmd_model, i) - _PMD.constraint_mc_thermal_limit_from(pmd_model, i) - _PMD.constraint_mc_thermal_limit_to(pmd_model, i) - end - - for i in _PMD.ids(pmd_model, :switch) - _PMD.constraint_mc_switch_state(pmd_model, i) - _PMD.constraint_mc_switch_current_limit(pmd_model, i) - end - - for i in _PMD.ids(pmd_model, :transformer) - _PMD.constraint_mc_transformer_power(pmd_model, i) - end - - - # ------------------------------------------------- - # --- PMITD(T&D) INDEPENDENT Constraints ---------- - - for i in ids(pmitd, :boundary) - constraint_boundary_current(pmitd, i) - constraint_boundary_voltage_magnitude(pmitd, i) - constraint_boundary_voltage_angle(pmitd, i) - end - - # Note: Both of these need to consider flow on boundaries if bus is connected to boundary - # # ---- Transmission Power Balance --- - boundary_buses = Vector{Int}() # empty vector that stores the boundary buses, so they are not repeated by the other constraint - for i in _PM.ids(pm_model, :bus) - for j in ids(pmitd, :boundary) - constraint_transmission_current_balance_boundary(pmitd, i, j, boundary_buses) - end - if !(i in boundary_buses) - _PM.constraint_current_balance(pm_model, i) - end - end - - # # ---- Distribution Power Balance --- - boundary_buses = Vector{Int}() # empty vector that stores the boundary buses, so they are not repeated by the other constraint - for i in _PMD.ids(pmd_model, :bus) - for j in ids(pmitd, :boundary) - constraint_distribution_current_balance_boundary(pmitd, i, j, boundary_buses) - end - if !(i in boundary_buses) - _PMD.constraint_mc_current_balance(pmd_model, i) - end - end - - # ------------------------------------------------- - # --- PMITD(T&D) Cost Functions ------------------- - objective_itd_min_fuel_cost_storage(pmitd) + @error "IVR-IVRU formulation not yet supported for storage problems." + + # # Get Models + # pm_model = _get_powermodel_from_powermodelitd(pmitd) + # pmd_model = _get_powermodeldistribution_from_powermodelitd(pmitd) + + # # PM(Transmission) Variables + # _PM.variable_bus_voltage(pm_model) + # _PM.variable_branch_current(pm_model) + # _PM.variable_gen_current(pm_model) + # _PM.variable_dcline_current(pm_model) + # _PM.variable_storage_power(pm_model) + + # # PMD(Distribution) Variables + # _PMD.variable_mc_bus_voltage(pmd_model) + # _PMD.variable_mc_branch_current(pmd_model) + # _PMD.variable_mc_switch_current(pmd_model) + # _PMD.variable_mc_transformer_current(pmd_model) + # _PMD.variable_mc_generator_current(pmd_model) + # _PMD.variable_mc_load_current(pmd_model) + + # # PMITD (Boundary) Current Variables + # variable_boundary_current(pmitd) + + # # --- PM(Transmission) Constraints --- + # # reference buses (this only needs to happen for pm(transmission)) + # for i in _PM.ids(pm_model, :ref_buses) + # _PM.constraint_theta_ref(pm_model, i) + # end + + # for i in _PM.ids(pm_model, :storage) + # _PM.constraint_storage_state(pm_model, i) + # _PM.constraint_storage_complementarity_nl(pm_model, i) + # _PM.constraint_storage_losses(pm_model, i) + # _PM.constraint_storage_thermal_limit(pm_model, i) + # end + + # for i in _PM.ids(pm_model, :branch) + # _PM.constraint_current_from(pm_model, i) + # _PM.constraint_current_to(pm_model, i) + + # _PM.constraint_voltage_drop(pm_model, i) + # _PM.constraint_voltage_angle_difference(pm_model, i) + + # _PM.constraint_thermal_limit_from(pm_model, i) + # _PM.constraint_thermal_limit_to(pm_model, i) + # end + + # for i in _PM.ids(pm_model, :dcline) + # _PM.constraint_dcline_power_losses(pm_model, i) + # end + + + # # --- PMD(Distribution) Constraints --- + # # gens should be constrained before KCL, or Pd/Qd undefined + # for id in _PMD.ids(pmd_model, :gen) + # _PMD.constraint_mc_generator_power(pmd_model, id) + # end + + # # loads should be constrained before KCL, or Pd/Qd undefined + # for id in _PMD.ids(pmd_model, :load) + # _PMD.constraint_mc_load_power(pmd_model, id) + # end + + + # for i in _PMD.ids(pmd_model, :branch) + # _PMD.constraint_mc_current_from(pmd_model, i) + # _PMD.constraint_mc_current_to(pmd_model, i) + # _PMD.constraint_mc_bus_voltage_drop(pmd_model, i) + # _PMD.constraint_mc_voltage_angle_difference(pmd_model, i) + # _PMD.constraint_mc_thermal_limit_from(pmd_model, i) + # _PMD.constraint_mc_thermal_limit_to(pmd_model, i) + # end + + # for i in _PMD.ids(pmd_model, :switch) + # _PMD.constraint_mc_switch_state(pmd_model, i) + # _PMD.constraint_mc_switch_current_limit(pmd_model, i) + # end + + # for i in _PMD.ids(pmd_model, :transformer) + # _PMD.constraint_mc_transformer_power(pmd_model, i) + # end + + + # # ------------------------------------------------- + # # --- PMITD(T&D) INDEPENDENT Constraints ---------- + + # for i in ids(pmitd, :boundary) + # constraint_boundary_current(pmitd, i) + # constraint_boundary_voltage_magnitude(pmitd, i) + # constraint_boundary_voltage_angle(pmitd, i) + # end + + # # Note: Both of these need to consider flow on boundaries if bus is connected to boundary + # # # ---- Transmission Power Balance --- + # boundary_buses = Vector{Int}() # empty vector that stores the boundary buses, so they are not repeated by the other constraint + # for i in _PM.ids(pm_model, :bus) + # for j in ids(pmitd, :boundary) + # constraint_transmission_current_balance_boundary(pmitd, i, j, boundary_buses) + # end + # if !(i in boundary_buses) + # _PM.constraint_current_balance(pm_model, i) + # end + # end + + # # # ---- Distribution Power Balance --- + # boundary_buses = Vector{Int}() # empty vector that stores the boundary buses, so they are not repeated by the other constraint + # for i in _PMD.ids(pmd_model, :bus) + # for j in ids(pmitd, :boundary) + # constraint_distribution_current_balance_boundary(pmitd, i, j, boundary_buses) + # end + # if !(i in boundary_buses) + # _PMD.constraint_mc_current_balance(pmd_model, i) + # end + # end + + # # ------------------------------------------------- + # # --- PMITD(T&D) Cost Functions ------------------- + # objective_itd_min_fuel_cost_storage(pmitd) end @@ -877,163 +879,165 @@ Constructor for Multinetwork Integrated T&D Optimal Power Flow in current-voltag """ function build_mn_opfitd_storage(pmitd::AbstractIVRPowerModelITD) - # Get Models - pm_model = _get_powermodel_from_powermodelitd(pmitd) - pmd_model = _get_powermodeldistribution_from_powermodelitd(pmitd) - - for (n, network) in nws(pmitd) - # PM(Transmission) Variables - _PM.variable_bus_voltage(pm_model, nw=n) - _PM.variable_branch_current(pm_model, nw=n) - _PM.variable_gen_current(pm_model, nw=n) - _PM.variable_dcline_current(pm_model, nw=n) - _PM.variable_storage_power(pm_model, nw=n) - - # PMD(Distribution) Variables - _PMD.variable_mc_bus_voltage(pmd_model; nw=n) - _PMD.variable_mc_branch_current(pmd_model; nw=n) - _PMD.variable_mc_switch_current(pmd_model; nw=n) - _PMD.variable_mc_transformer_current(pmd_model; nw=n) - _PMD.variable_mc_generator_current(pmd_model; nw=n) - _PMD.variable_mc_load_current(pmd_model; nw=n) - _PMD.variable_mc_storage_power(pmd_model; nw=n) - - # PMITD (Boundary) Current Variables - variable_boundary_current(pmitd; nw=n) - - # reference buses (this only needs to happen for pm(transmission)) - for i in _PM.ids(pm_model, :ref_buses, nw=n) - _PM.constraint_theta_ref(pm_model, i, nw=n) - end - - for i in _PM.ids(pm_model, :storage, nw=n) - _PM.constraint_storage_complementarity_nl(pm_model, i, nw=n) - _PM.constraint_storage_losses(pm_model, i, nw=n) - _PM.constraint_storage_thermal_limit(pm_model, i, nw=n) - end - - # PM branches - for i in _PM.ids(pm_model, :branch, nw=n) - _PM.constraint_current_from(pm_model, i, nw=n) - _PM.constraint_current_to(pm_model, i, nw=n) - - _PM.constraint_voltage_drop(pm_model, i, nw=n) - _PM.constraint_voltage_angle_difference(pm_model, i, nw=n) - - _PM.constraint_thermal_limit_from(pm_model, i, nw=n) - _PM.constraint_thermal_limit_to(pm_model, i, nw=n) - end - - # PM DC lines - for i in _PM.ids(pm_model, :dcline, nw=n) - _PM.constraint_dcline_power_losses(pm_model, i, nw=n) - end - - # ------------------------------------------------- - # --- PMD(Distribution) Constraints --- - - # generators should be constrained before KCL, or Pd/Qd undefined - for i in _PMD.ids(pmd_model, n, :gen) - _PMD.constraint_mc_generator_power(pmd_model, i; nw=n) - end - - # loads should be constrained before KCL, or Pd/Qd undefined - for i in _PMD.ids(pmd_model, n, :load) - _PMD.constraint_mc_load_power(pmd_model, i; nw=n) - end - - for i in _PMD.ids(pmd_model, n, :storage) - _PMD.constraint_storage_complementarity_nl(pmd_model, i; nw=n) - _PMD.constraint_mc_storage_losses(pmd_model, i; nw=n) - _PMD.constraint_mc_storage_thermal_limit(pmd_model, i; nw=n) - end - - for i in _PMD.ids(pmd_model, n, :branch) - _PMD.constraint_mc_current_from(pmd_model, i; nw=n) - _PMD.constraint_mc_current_to(pmd_model, i; nw=n) - _PMD.constraint_mc_bus_voltage_drop(pmd_model, i; nw=n) - _PMD.constraint_mc_voltage_angle_difference(pmd_model, i; nw=n) - _PMD.constraint_mc_thermal_limit_from(pmd_model, i; nw=n) - _PMD.constraint_mc_thermal_limit_to(pmd_model, i; nw=n) - end - - for i in _PMD.ids(pmd_model, n, :switch) - _PMD.constraint_mc_switch_state(pmd_model, i; nw=n) - _PMD.constraint_mc_switch_current_limit(pmd_model, i; nw=n) - end - - for i in _PMD.ids(pmd_model, n, :transformer) - _PMD.constraint_mc_transformer_power(pmd_model, i; nw=n) - end - - # ------------------------------------------------- - # --- PMITD(T&D) INDEPENDENT Constraints ---------- - - for i in ids(pmitd, :boundary; nw=n) - constraint_boundary_current(pmitd, i; nw=n) - constraint_boundary_voltage_magnitude(pmitd, i; nw=n) - constraint_boundary_voltage_angle(pmitd, i; nw=n) - end - - # Note: Both of these need to consider flow on boundaries if bus is connected to boundary - # # ---- Transmission Power Balance --- - boundary_buses = Vector{Int}() # empty vector that stores the boundary buses, so they are not repeated by the other constraint - for i in _PM.ids(pm_model, :bus, nw=n) - for j in ids(pmitd, :boundary; nw=n) - constraint_transmission_current_balance_boundary(pmitd, i, j, boundary_buses; nw_pmitd=n) - end - if !(i in boundary_buses) - _PM.constraint_current_balance(pm_model, i, nw=n) - end - end - - # # ---- Distribution Power Balance --- - boundary_buses = Vector{Int}() # empty vector that stores the boundary buses, so they are not repeated by the other constraint - for i in _PMD.ids(pmd_model, n, :bus) - for j in ids(pmitd, :boundary; nw=n) - constraint_distribution_current_balance_boundary(pmitd, i, j, boundary_buses; nw_pmitd=n) - end - if !(i in boundary_buses) - _PMD.constraint_mc_current_balance(pmd_model, i; nw=n) - end - end - end - - # --- PM energy storage state constraint --- - network_ids_pm = sort(collect(_PM.nw_ids(pm_model))) - - n_1_pm = network_ids_pm[1] - for i in _PM.ids(pm_model, :storage, nw=n_1_pm) - _PM.constraint_storage_state(pm_model, i, nw=n_1_pm) - end - - for n_2_pm in network_ids_pm[2:end] - for i in _PM.ids(pm_model, :storage, nw=n_2_pm) - _PM.constraint_storage_state(pm_model, i, n_1_pm, n_2_pm) - end - n_1_pm = n_2_pm - end - - # --- PMD energy storage state constraint --- - network_ids_pmd = sort(collect(_PMD.nw_ids(pmd_model))) - - n_1_pmd = network_ids_pmd[1] - - for i in _PMD.ids(pmd_model, :storage; nw=n_1_pmd) - _PMD.constraint_storage_state(pmd_model, i; nw=n_1_pmd) - end - - for n_2_pmd in network_ids_pmd[2:end] - for i in _PMD.ids(pmd_model, :storage; nw=n_2_pmd) - _PMD.constraint_storage_state(pmd_model, i, n_1_pmd, n_2_pmd) - end - - n_1_pmd = n_2_pmd - end - - # ------------------------------------------------- - # --- PMITD(T&D) Cost Functions ------------------- - objective_itd_min_fuel_cost_storage(pmitd) + @error "IVR-IVRU formulation not yet supported for multinetwork storage problems." + + # # Get Models + # pm_model = _get_powermodel_from_powermodelitd(pmitd) + # pmd_model = _get_powermodeldistribution_from_powermodelitd(pmitd) + + # for (n, network) in nws(pmitd) + # # PM(Transmission) Variables + # _PM.variable_bus_voltage(pm_model, nw=n) + # _PM.variable_branch_current(pm_model, nw=n) + # _PM.variable_gen_current(pm_model, nw=n) + # _PM.variable_dcline_current(pm_model, nw=n) + # _PM.variable_storage_power(pm_model, nw=n) + + # # PMD(Distribution) Variables + # _PMD.variable_mc_bus_voltage(pmd_model; nw=n) + # _PMD.variable_mc_branch_current(pmd_model; nw=n) + # _PMD.variable_mc_switch_current(pmd_model; nw=n) + # _PMD.variable_mc_transformer_current(pmd_model; nw=n) + # _PMD.variable_mc_generator_current(pmd_model; nw=n) + # _PMD.variable_mc_load_current(pmd_model; nw=n) + # _PMD.variable_mc_storage_power(pmd_model; nw=n) + + # # PMITD (Boundary) Current Variables + # variable_boundary_current(pmitd; nw=n) + + # # reference buses (this only needs to happen for pm(transmission)) + # for i in _PM.ids(pm_model, :ref_buses, nw=n) + # _PM.constraint_theta_ref(pm_model, i, nw=n) + # end + + # for i in _PM.ids(pm_model, :storage, nw=n) + # _PM.constraint_storage_complementarity_nl(pm_model, i, nw=n) + # _PM.constraint_storage_losses(pm_model, i, nw=n) + # _PM.constraint_storage_thermal_limit(pm_model, i, nw=n) + # end + + # # PM branches + # for i in _PM.ids(pm_model, :branch, nw=n) + # _PM.constraint_current_from(pm_model, i, nw=n) + # _PM.constraint_current_to(pm_model, i, nw=n) + + # _PM.constraint_voltage_drop(pm_model, i, nw=n) + # _PM.constraint_voltage_angle_difference(pm_model, i, nw=n) + + # _PM.constraint_thermal_limit_from(pm_model, i, nw=n) + # _PM.constraint_thermal_limit_to(pm_model, i, nw=n) + # end + + # # PM DC lines + # for i in _PM.ids(pm_model, :dcline, nw=n) + # _PM.constraint_dcline_power_losses(pm_model, i, nw=n) + # end + + # # ------------------------------------------------- + # # --- PMD(Distribution) Constraints --- + + # # generators should be constrained before KCL, or Pd/Qd undefined + # for i in _PMD.ids(pmd_model, n, :gen) + # _PMD.constraint_mc_generator_power(pmd_model, i; nw=n) + # end + + # # loads should be constrained before KCL, or Pd/Qd undefined + # for i in _PMD.ids(pmd_model, n, :load) + # _PMD.constraint_mc_load_power(pmd_model, i; nw=n) + # end + + # for i in _PMD.ids(pmd_model, n, :storage) + # _PMD.constraint_storage_complementarity_nl(pmd_model, i; nw=n) + # _PMD.constraint_mc_storage_losses(pmd_model, i; nw=n) + # _PMD.constraint_mc_storage_thermal_limit(pmd_model, i; nw=n) + # end + + # for i in _PMD.ids(pmd_model, n, :branch) + # _PMD.constraint_mc_current_from(pmd_model, i; nw=n) + # _PMD.constraint_mc_current_to(pmd_model, i; nw=n) + # _PMD.constraint_mc_bus_voltage_drop(pmd_model, i; nw=n) + # _PMD.constraint_mc_voltage_angle_difference(pmd_model, i; nw=n) + # _PMD.constraint_mc_thermal_limit_from(pmd_model, i; nw=n) + # _PMD.constraint_mc_thermal_limit_to(pmd_model, i; nw=n) + # end + + # for i in _PMD.ids(pmd_model, n, :switch) + # _PMD.constraint_mc_switch_state(pmd_model, i; nw=n) + # _PMD.constraint_mc_switch_current_limit(pmd_model, i; nw=n) + # end + + # for i in _PMD.ids(pmd_model, n, :transformer) + # _PMD.constraint_mc_transformer_power(pmd_model, i; nw=n) + # end + + # # ------------------------------------------------- + # # --- PMITD(T&D) INDEPENDENT Constraints ---------- + + # for i in ids(pmitd, :boundary; nw=n) + # constraint_boundary_current(pmitd, i; nw=n) + # constraint_boundary_voltage_magnitude(pmitd, i; nw=n) + # constraint_boundary_voltage_angle(pmitd, i; nw=n) + # end + + # # Note: Both of these need to consider flow on boundaries if bus is connected to boundary + # # # ---- Transmission Power Balance --- + # boundary_buses = Vector{Int}() # empty vector that stores the boundary buses, so they are not repeated by the other constraint + # for i in _PM.ids(pm_model, :bus, nw=n) + # for j in ids(pmitd, :boundary; nw=n) + # constraint_transmission_current_balance_boundary(pmitd, i, j, boundary_buses; nw_pmitd=n) + # end + # if !(i in boundary_buses) + # _PM.constraint_current_balance(pm_model, i, nw=n) + # end + # end + + # # # ---- Distribution Power Balance --- + # boundary_buses = Vector{Int}() # empty vector that stores the boundary buses, so they are not repeated by the other constraint + # for i in _PMD.ids(pmd_model, n, :bus) + # for j in ids(pmitd, :boundary; nw=n) + # constraint_distribution_current_balance_boundary(pmitd, i, j, boundary_buses; nw_pmitd=n) + # end + # if !(i in boundary_buses) + # _PMD.constraint_mc_current_balance(pmd_model, i; nw=n) + # end + # end + # end + + # # --- PM energy storage state constraint --- + # network_ids_pm = sort(collect(_PM.nw_ids(pm_model))) + + # n_1_pm = network_ids_pm[1] + # for i in _PM.ids(pm_model, :storage, nw=n_1_pm) + # _PM.constraint_storage_state(pm_model, i, nw=n_1_pm) + # end + + # for n_2_pm in network_ids_pm[2:end] + # for i in _PM.ids(pm_model, :storage, nw=n_2_pm) + # _PM.constraint_storage_state(pm_model, i, n_1_pm, n_2_pm) + # end + # n_1_pm = n_2_pm + # end + + # # --- PMD energy storage state constraint --- + # network_ids_pmd = sort(collect(_PMD.nw_ids(pmd_model))) + + # n_1_pmd = network_ids_pmd[1] + + # for i in _PMD.ids(pmd_model, :storage; nw=n_1_pmd) + # _PMD.constraint_storage_state(pmd_model, i; nw=n_1_pmd) + # end + + # for n_2_pmd in network_ids_pmd[2:end] + # for i in _PMD.ids(pmd_model, :storage; nw=n_2_pmd) + # _PMD.constraint_storage_state(pmd_model, i, n_1_pmd, n_2_pmd) + # end + + # n_1_pmd = n_2_pmd + # end + + # # ------------------------------------------------- + # # --- PMITD(T&D) Cost Functions ------------------- + # objective_itd_min_fuel_cost_storage(pmitd) end diff --git a/test/opfitd_storage.jl b/test/opfitd_storage.jl index a08e8af..5dadd05 100644 --- a/test/opfitd_storage.jl +++ b/test/opfitd_storage.jl @@ -25,6 +25,98 @@ @test storage_ref[1]["cost"] == 0.25 end + @testset "Check that eng2math_passthrough value is being added to the instantiated model: " begin + pm_file = joinpath(dirname(trans_path), "case5_withload.m") + pmd_file = joinpath(dirname(dist_path), "case3_balanced_withBattery.dss") + pmitd_file = joinpath(dirname(bound_path), "case5_case3_bal_battery.json") + pmitd_type = BFPowerModelITD{BFAPowerModel, LinDist3FlowPowerModel} + pmitd_data = parse_files(pm_file, pmd_file, pmitd_file) + + # add cost to storages in PMD + for (st_name, st_data) in pmitd_data["it"]["pmd"]["storage"] + st_data["cost"] = 0.25 + end + + # instantiate model with eng2math_passthrough + eng2math_passthrough = Dict("storage"=>["cost"]) + pmitd_inst_model = instantiate_model(pmitd_data, pmitd_type, build_opfitd; eng2math_passthrough=eng2math_passthrough) + + # get storage reference + storage_ref = _IM.ref(pmitd_inst_model, _PMD.pmd_it_sym, nw=nw_id_default, :storage) + + # test that the "cost" value in storage exists. + @test storage_ref[1]["cost"] == 0.25 + end + + @testset "Check that eng2math_passthrough value is being added to the instantiated model: " begin + pm_file = joinpath(dirname(trans_path), "case5_withload.m") + pmd_file = joinpath(dirname(dist_path), "case3_balanced_withBattery.dss") + pmitd_file = joinpath(dirname(bound_path), "case5_case3_bal_battery.json") + pmitd_type = NLBFPowerModelITD{ACRPowerModel, FBSUBFPowerModel} + pmitd_data = parse_files(pm_file, pmd_file, pmitd_file) + + # add cost to storages in PMD + for (st_name, st_data) in pmitd_data["it"]["pmd"]["storage"] + st_data["cost"] = 0.25 + end + + # instantiate model with eng2math_passthrough + eng2math_passthrough = Dict("storage"=>["cost"]) + pmitd_inst_model = instantiate_model(pmitd_data, pmitd_type, build_opfitd; eng2math_passthrough=eng2math_passthrough) + + # get storage reference + storage_ref = _IM.ref(pmitd_inst_model, _PMD.pmd_it_sym, nw=nw_id_default, :storage) + + # test that the "cost" value in storage exists. + @test storage_ref[1]["cost"] == 0.25 + end + + @testset "Check that IVR-IVRU gives warning error when instantiated: " begin + pm_file = joinpath(dirname(trans_path), "case5_withload.m") + pmd_file = joinpath(dirname(dist_path), "case3_balanced_withBattery.dss") + pmitd_file = joinpath(dirname(bound_path), "case5_case3_bal_battery.json") + pmitd_type = IVRPowerModelITD{IVRPowerModel, IVRUPowerModel} + pmitd_data = parse_files(pm_file, pmd_file, pmitd_file) + # instantiate model with eng2math_passthrough + eng2math_passthrough = Dict("storage"=>["cost"]) + @test_throws ErrorException instantiate_model(pmitd_data, pmitd_type, build_opfitd; eng2math_passthrough=eng2math_passthrough) + end + + @testset "Check that IVR-IVRU gives warning error when instantiated: (Multinetwork): " begin + pm_file = joinpath(dirname(trans_path), "case5_withload.m") + pmd_file = joinpath(dirname(dist_path), "case3_balanced_withBattery_mn.dss") + pmitd_file = joinpath(dirname(bound_path), "case5_case3_bal_battery_mn.json") + pmitd_type = IVRPowerModelITD{IVRPowerModel, IVRUPowerModel} + pmitd_data = parse_files(pm_file, pmd_file, pmitd_file; multinetwork=true) + # instantiate model with eng2math_passthrough + eng2math_passthrough = Dict("storage"=>["cost"]) + @test_throws ErrorException instantiate_model(pmitd_data, pmitd_type, build_mn_opfitd; multinetwork=true, eng2math_passthrough=eng2math_passthrough) + end + + @testset "solve_model (with network inputs) build_opfitd_storage: case5-case3 ACR-ACR with polynomial nl terms above quadratic" begin + pm_file = joinpath(dirname(trans_path), "case5_withload.m") + pmd_file = joinpath(dirname(dist_path), "case3_balanced_withBattery.dss") + pmitd_file = joinpath(dirname(bound_path), "case5_case3_bal_battery.json") + pmitd_type = NLPowerModelITD{ACRPowerModel, ACRUPowerModel} + pmitd_data = parse_files(pm_file, pmd_file, pmitd_file) + # add cost to storages in PMD + for (st_name, st_data) in pmitd_data["it"]["pmd"]["storage"] + st_data["cost"] = 0.25 + end + eng2math_passthrough = Dict("storage"=>["cost"]) + # transform pmd to math, to change gens cost models + pmitd_data["it"]["pmd"] = _PMD.transform_data_model(pmitd_data["it"]["pmd"]; eng2math_passthrough=eng2math_passthrough) + # Modify (internally) all gens (both T&D) costs so that polynomial nl terms are used. + pmitd_data["it"]["pmd"]["gen"]["1"]["cost"] = [20.0, 35.0, 110.0, 140.0, 1.0] # modify dist. system gen. + pmitd_data["it"]["pmd"]["gen"]["1"]["ncost"] = 5 + pmitd_data["it"]["pm"]["gen"]["4"]["cost"] = [25.0, 1.0] # modify trans. system gen. + pmitd_data["it"]["pm"]["gen"]["1"]["cost"] = [30.0, 1.0] # modify trans. system gen. + pmitd_data["it"]["pm"]["gen"]["5"]["cost"] = [10.0, 100.0, 300.0, 1400.0, 1.0] # modify trans. system gen. + pmitd_data["it"]["pm"]["gen"]["5"]["ncost"] = 5 + result = solve_model(pmitd_data, pmitd_type, ipopt, build_opfitd_storage) + @test result["termination_status"] == LOCALLY_SOLVED + end + @testset "Check that eng2math_passthrough value is being added to the instantiated model (Multinetwork): " begin pm_file = joinpath(dirname(trans_path), "case5_withload.m") pmd_file = joinpath(dirname(dist_path), "case3_balanced_withBattery_mn.dss") @@ -168,7 +260,7 @@ end - ## IVRU is missing critical current variables (real currentr) for solving opfs with storage in PMD + # # IVRU is missing critical current variables (real currentr) for solving opfs with storage in PMD # @testset "solve_mn_opfitd_storage: Multinetwork Balanced case5-case3 With Battery IVR-IVRU " begin # pm_file = joinpath(dirname(trans_path), "case5_withload.m") # pmd_file = joinpath(dirname(dist_path), "case3_unbalanced_withBattery_mn_diff.dss") @@ -178,7 +270,7 @@ # # cost to assign to energy storage # # Units $/kWh - # strg_cost = 0.0025 + # strg_cost = 0.001 # # add cost to storages in PMD # for (nw_id, nw_data) in pmitd_data["it"]["pmd"]["nw"] @@ -188,7 +280,8 @@ # end # pmitd_result_strg = solve_mn_opfitd_storage(pmitd_data, pmitd_type, ipopt) - # @test isapprox(pmitd_result_strg["solution"]["it"]["pmd"]["nw"]["10"]["storage"]["3bus_bal_battery_mn.s1"]["sd"], 500.0; atol = 1e-1) + # @test isapprox(pmitd_result_strg["solution"]["it"]["pmd"]["nw"]["4"]["storage"]["3bus_bal_battery_mn.s1"]["sd"], 500.0; atol = 1e-1) + # @test isapprox(pmitd_result_strg["solution"]["it"]["pmd"]["nw"]["2"]["storage"]["3bus_bal_battery_mn.s1"]["sc"], 500.0; atol = 1e-1) # end From 2cfe22b836bb4a38213ad2ae233a3cc1e16ff09b Mon Sep 17 00:00:00 2001 From: Juan Ospina Date: Wed, 22 Nov 2023 14:42:53 -0700 Subject: [PATCH 16/16] FIX: small issues with tests for increasing testing coverage for storage unit tests. --- src/prob/opfitd_storage.jl | 2 ++ .../distribution/case3_balanced_withBattery.dss | 2 ++ test/opfitd_storage.jl | 14 +++++++------- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/prob/opfitd_storage.jl b/src/prob/opfitd_storage.jl index 11699c8..703d7d3 100755 --- a/src/prob/opfitd_storage.jl +++ b/src/prob/opfitd_storage.jl @@ -271,6 +271,7 @@ Constructor for Integrated T&D Optimal Power Flow in current-voltage (IV) variab function build_opfitd_storage(pmitd::AbstractIVRPowerModelITD) @error "IVR-IVRU formulation not yet supported for storage problems." + throw(error()) # # Get Models # pm_model = _get_powermodel_from_powermodelitd(pmitd) @@ -880,6 +881,7 @@ Constructor for Multinetwork Integrated T&D Optimal Power Flow in current-voltag function build_mn_opfitd_storage(pmitd::AbstractIVRPowerModelITD) @error "IVR-IVRU formulation not yet supported for multinetwork storage problems." + throw(error()) # # Get Models # pm_model = _get_powermodel_from_powermodelitd(pmitd) diff --git a/test/data/distribution/case3_balanced_withBattery.dss b/test/data/distribution/case3_balanced_withBattery.dss index e234d6d..f27e2ff 100755 --- a/test/data/distribution/case3_balanced_withBattery.dss +++ b/test/data/distribution/case3_balanced_withBattery.dss @@ -40,6 +40,8 @@ New Load.L3 phases=1 loadbus.3.0 ( 13.8 3 sqrt / ) kW=3000 kvar=1500 mod New Storage.S1 phases=3 bus1=primary.1.2.3 kv=( 13.8 3 sqrt / ) kwhstored=50 kwhrated=50 kva=100 kvar=100 ~ %charge=100 %discharge=100 %effcharge=100 %effdischarge=100 %idlingkw=1 %r=0 %x=50 +!GENERATORS DEFINITIONS +New generator.gen1 Bus1=loadbus.1.2.3 Phases=3 kV=( 13.8 3 sqrt / ) kW=20 pf=1 conn=wye Model=3 Set VoltageBases = "230,13.8" Set tolerance=0.000001 diff --git a/test/opfitd_storage.jl b/test/opfitd_storage.jl index 5dadd05..ed32540 100644 --- a/test/opfitd_storage.jl +++ b/test/opfitd_storage.jl @@ -16,7 +16,7 @@ # instantiate model with eng2math_passthrough eng2math_passthrough = Dict("storage"=>["cost"]) - pmitd_inst_model = instantiate_model(pmitd_data, pmitd_type, build_opfitd; eng2math_passthrough=eng2math_passthrough) + pmitd_inst_model = instantiate_model(pmitd_data, pmitd_type, build_opfitd_storage; eng2math_passthrough=eng2math_passthrough) # get storage reference storage_ref = _IM.ref(pmitd_inst_model, _PMD.pmd_it_sym, nw=nw_id_default, :storage) @@ -39,7 +39,7 @@ # instantiate model with eng2math_passthrough eng2math_passthrough = Dict("storage"=>["cost"]) - pmitd_inst_model = instantiate_model(pmitd_data, pmitd_type, build_opfitd; eng2math_passthrough=eng2math_passthrough) + pmitd_inst_model = instantiate_model(pmitd_data, pmitd_type, build_opfitd_storage; eng2math_passthrough=eng2math_passthrough) # get storage reference storage_ref = _IM.ref(pmitd_inst_model, _PMD.pmd_it_sym, nw=nw_id_default, :storage) @@ -62,7 +62,7 @@ # instantiate model with eng2math_passthrough eng2math_passthrough = Dict("storage"=>["cost"]) - pmitd_inst_model = instantiate_model(pmitd_data, pmitd_type, build_opfitd; eng2math_passthrough=eng2math_passthrough) + pmitd_inst_model = instantiate_model(pmitd_data, pmitd_type, build_opfitd_storage; eng2math_passthrough=eng2math_passthrough) # get storage reference storage_ref = _IM.ref(pmitd_inst_model, _PMD.pmd_it_sym, nw=nw_id_default, :storage) @@ -79,7 +79,7 @@ pmitd_data = parse_files(pm_file, pmd_file, pmitd_file) # instantiate model with eng2math_passthrough eng2math_passthrough = Dict("storage"=>["cost"]) - @test_throws ErrorException instantiate_model(pmitd_data, pmitd_type, build_opfitd; eng2math_passthrough=eng2math_passthrough) + @test_throws ErrorException instantiate_model(pmitd_data, pmitd_type, build_opfitd_storage; eng2math_passthrough=eng2math_passthrough) end @testset "Check that IVR-IVRU gives warning error when instantiated: (Multinetwork): " begin @@ -90,7 +90,7 @@ pmitd_data = parse_files(pm_file, pmd_file, pmitd_file; multinetwork=true) # instantiate model with eng2math_passthrough eng2math_passthrough = Dict("storage"=>["cost"]) - @test_throws ErrorException instantiate_model(pmitd_data, pmitd_type, build_mn_opfitd; multinetwork=true, eng2math_passthrough=eng2math_passthrough) + @test_throws ErrorException instantiate_model(pmitd_data, pmitd_type, build_mn_opfitd_storage; multinetwork=true, eng2math_passthrough=eng2math_passthrough) end @testset "solve_model (with network inputs) build_opfitd_storage: case5-case3 ACR-ACR with polynomial nl terms above quadratic" begin @@ -133,7 +133,7 @@ # instantiate model with eng2math_passthrough eng2math_passthrough = Dict("storage"=>["cost"]) - pmitd_inst_model = instantiate_model(pmitd_data, pmitd_type, build_mn_opfitd; multinetwork=true, eng2math_passthrough=eng2math_passthrough) + pmitd_inst_model = instantiate_model(pmitd_data, pmitd_type, build_mn_opfitd_storage; multinetwork=true, eng2math_passthrough=eng2math_passthrough) # get storage reference from nw=4 storage_ref_nw4 = _IM.ref(pmitd_inst_model, _PMD.pmd_it_sym, nw=4, :storage) @@ -160,7 +160,7 @@ # with storage cost problem pmitd_result_strg = solve_opfitd_storage(pmitd_data, pmitd_type, ipopt) - @test isapprox(pmitd_result_strg["objective"], 17977.48172498742+(pmitd_result_strg["solution"]["it"]["pmd"]["storage"]["3bus_bal_battery.s1"]["sd"]*strg_cost/100000); atol = 1e-3) + @test isapprox(pmitd_result_strg["objective"], 17976.959438016635+(pmitd_result_strg["solution"]["it"]["pmd"]["storage"]["3bus_bal_battery.s1"]["sd"]*strg_cost/100000); atol = 1e-3) end