From 9a7fa05c262868cd57db7bc5e0ef45be4b8359c7 Mon Sep 17 00:00:00 2001 From: jjospina Date: Tue, 30 Jul 2024 17:04:07 -0600 Subject: [PATCH] REF: Multithread solver results and transformations. --- src/PowerModelsITD.jl | 14 -- src/core/base.jl | 204 ++++++++++++++++++++- src/core/solution_decomposition.jl | 282 +++-------------------------- src/form_decomposition/acp.jl | 53 +----- 4 files changed, 228 insertions(+), 325 deletions(-) diff --git a/src/PowerModelsITD.jl b/src/PowerModelsITD.jl index 03002f1..1646b0a 100755 --- a/src/PowerModelsITD.jl +++ b/src/PowerModelsITD.jl @@ -39,20 +39,6 @@ module PowerModelsITD # enables support for v[1] Base.getindex(v::JuMP.VariableRef, i::Int) = v - # const vectors with strings build function names availables. - "STANDARD_PROBLEMS constant vector that contains the different types of ITD problems supported." - const STANDARD_PROBLEMS = ["build_opfitd", "build_mn_opfitd", "build_pfitd", "build_mn_opfitd_oltc", "build_opfitd_oltc", "build_dmld_opfitd", "build_mn_dmld_opfitd_simple"] - - "DECOMPOSITION_PROBLEMS constant vector that contains the different types of ITD decomposition problems supported." - const DECOMPOSITION_PROBLEMS = ["build_opfitd_decomposition", "build_mn_opfitd_decomposition"] - # mutable struct to store pmitd models/JuMP models for decomposition applications. - mutable struct DecompositionStruct - pm # transmission system IM model - pmd # distribution system IM model - optimizer # IDEC optimizer struct - DecompositionStruct() = new() # Constructor - end - # Files to include in module include("io/common.jl") include("core/base.jl") diff --git a/src/core/base.jl b/src/core/base.jl index 6c4b0b4..6fbbb3b 100755 --- a/src/core/base.jl +++ b/src/core/base.jl @@ -287,9 +287,14 @@ distribution modeling extensions. The parameter `export_models` is a boolean that determines if the JuMP models are exported to the pwd as `.mof.json` files. """ function instantiate_model_decomposition( - pmitd_data::Dict{String,<:Any}, pmitd_type::Type, optimizer, build_method::Function; - multinetwork::Bool=false, pmitd_ref_extensions::Vector{<:Function}=Function[], - export_models::Bool=false, kwargs...) + pmitd_data::Dict{String,<:Any}, + pmitd_type::Type, + optimizer::_SDO.ParallelOptimizer, + build_method::Function; + multinetwork::Bool=false, + pmitd_ref_extensions::Vector{<:Function}=Function[], + export_models::Bool=false, kwargs... +) # Separate distro. ckts from a single dictionary to multiple dictionary entries distro_systems_separated = _separate_pmd_circuits(pmitd_data["it"][_PMD.pmd_it_name]; multinetwork=multinetwork) @@ -416,11 +421,146 @@ function instantiate_model_decomposition( optimizer.mp_string_vector_channel = mp_string_vector_rcs optimizer.sp_string_vector_channel = sp_string_vector_rcs + # return optimizer, master model return optimizer, master_instantiated end +function instantiate_model_decomposition( + pmitd_data::Dict{String,<:Any}, + pmitd_type::Type, + optimizer::_SDO.MultiThreadOptimizer, + build_method::Function; + multinetwork::Bool=false, + pmitd_ref_extensions::Vector{<:Function}=Function[], + export_models::Bool=false, kwargs... +) + + # Separate distro. ckts from a single dictionary to multiple dictionary entries + distro_systems_separated = _separate_pmd_circuits(pmitd_data["it"][_PMD.pmd_it_name]; multinetwork=multinetwork) + pmitd_data["it"][_PMD.pmd_it_name] = distro_systems_separated + + # Force call Garbage collector to reduce RAM usage + GC.gc() + + # Correct the network data and assign the respective boundary number values. + correct_network_data_decomposition!(pmitd_data; multinetwork=multinetwork) + + # ----- StsDOpt Optimizer ------ + + # Add pmitd(boundary) info. to pm ref + pmitd_data["it"][_PM.pm_it_name][pmitd_it_name] = pmitd_data["it"][pmitd_it_name] + + # Instantiate the PM model + master_instantiated = _IM.instantiate_model(pmitd_data["it"][_PM.pm_it_name], + pmitd_type.parameters[1], + build_method, + ref_add_core_decomposition_transmission!, + _PM._pm_global_keys, + _PM.pm_it_sym; kwargs... + ) + + # Export mof.json models + if (export_models == true) + JuMP.write_to_file(master_instantiated.model, "master_model_exported.mof.json") + end + + # Add master model to optimizer master + optimizer.master = master_instantiated.model + + # Set master optimizer + JuMP.set_optimizer(optimizer.master, _SDO.Optimizer; add_bridges = true) + + # Force call Garbage collector to reduce RAM usage + GC.gc() + + # Get the number of subproblems + number_of_subproblems = length(pmitd_data["it"][_PMD.pmd_it_name]) + + # Convert distro. dictionary to vectors of dictionaries so it can be used in threaded version + ckts_names_vector = Vector{String}(undef, number_of_subproblems) + ckts_data_vector = Vector{Dict}(undef, number_of_subproblems) + ckt_number = 0 + for (ckt_name, ckt_data) in pmitd_data["it"][_PMD.pmd_it_name] + ckt_number = ckt_number + 1 + ckts_names_vector[ckt_number] = ckt_name + ckts_data_vector[ckt_number] = ckt_data + end + + # Set-up and instantiate subproblem models & boundary linking vars + subproblems_instantiated_models = Vector{pmitd_type.parameters[2]}(undef, number_of_subproblems) + subproblems_JuMP_models = Vector{JuMP.Model}(undef, number_of_subproblems) + boundary_vars_vector = Vector{Vector{Vector{JuMP.VariableRef}}}(undef, number_of_subproblems) + + # Threaded loop for instantiating subproblems + Threads.@threads for i in 1:1:number_of_subproblems + + # Obtain ckt boundary data + boundary_info = pmitd_data["it"][pmitd_it_name] + boundary_number = findfirst(x -> ckts_names_vector[i] == x["ckt_name"], boundary_info) + boundary_for_ckt = Dict(boundary_number => boundary_info[boundary_number]) + + # Add ckt_name to ckt_data for instantiation + ckts_data_vector[i]["ckt_name"] = ckts_names_vector[i] + + # add pmitd(boundary) info. to pmd ref + ckts_data_vector[i][pmitd_it_name] = boundary_for_ckt + + # Instantiate the PMD model + subproblem_instantiated = _IM.instantiate_model(ckts_data_vector[i], + pmitd_type.parameters[2], + build_method, + ref_add_core_decomposition_distribution!, + _PMD._pmd_global_keys, + _PMD.pmd_it_sym; kwargs... + ) + + # Add instantiated subproblem to vector of instantiated subproblems + subproblems_instantiated_models[i] = subproblem_instantiated + + # Export mof.json models + if (export_models == true) + JuMP.write_to_file(subproblem_instantiated.model, "subproblem_$(i)_$(ckts_names_vector[i])_$(boundary_number)_model_exported.mof.json") + end + + # Set the optimizer to the instantiated subproblem JuMP model + JuMP.set_optimizer(subproblem_instantiated.model, _SDO.Optimizer; add_bridges = true) + + # Add the subproblem JuMP model into the vector of instantiated subproblems + subproblems_JuMP_models[i] = subproblem_instantiated.model + + # Generate the boundary linking vars. (ACP, ACR, etc.) + if (export_models == true) + linking_vars_vector = generate_boundary_linking_vars(master_instantiated, + subproblem_instantiated, + boundary_number; + export_models=export_models + ) + else + linking_vars_vector = generate_boundary_linking_vars(master_instantiated, + subproblem_instantiated, + boundary_number + ) + end + + # Add linking vars vector to vector containing all vectors of linking vars. + boundary_vars_vector[i] = linking_vars_vector + + end + + # Add vector of subproblems JuMP models to Optimizer + optimizer.subproblems = subproblems_JuMP_models + + # Add vecor of boundary linking vars to Optimizer + optimizer.list_linking_vars = boundary_vars_vector + + # return optimizer, master model, and subproblem models + return optimizer, master_instantiated, subproblems_instantiated_models + +end + + """ function solve_model( pm_file::String, @@ -649,23 +789,25 @@ function solve_model( end # Solve decomposition ITD problem - elseif (typeof(optimizer) == _SDO.MetaOptimizer) + elseif (typeof(optimizer) == _SDO.ParallelOptimizer) # Instantiate the Decomposition PowerModelsITD object. - pmitd_optimizer, master_prob_instantiated = instantiate_model_decomposition( - pmitd_data, pmitd_type, optimizer, build_method; + pmitd_optimizer, transmission_instantiated = instantiate_model_decomposition( + pmitd_data, + pmitd_type, + optimizer, + build_method; multinetwork=multinetwork, pmitd_ref_extensions=pmitd_ref_extensions, export_models=export_models, kwargs... ) - # Calls the _SDO optimize!(..) function and solves decomposition problem _, solve_time, solve_bytes_alloc, sec_in_gc = @timed _SDO.optimize!(pmitd_optimizer) # Build the master decomposition solution - result = build_pm_decomposition_solution(master_prob_instantiated, solve_time) + result = build_pm_decomposition_solution(transmission_instantiated, solve_time) # Inform about the time for solving the problem (*change to @debug) @info "pmitd decomposition model solution time (instantiate + optimization): $(time() - start_time)" @@ -680,6 +822,52 @@ function solve_model( # _transform_decomposition_solution_to_si!(result, pmitd_data; make_si, multinetwork=multinetwork, solution_model=solution_model) # end + elseif (typeof(optimizer) == _SDO.MultiThreadOptimizer) + + # Instantiate the Decomposition PowerModelsITD object. + pmitd_optimizer, transmission_instantiated, subproblems_instantiated = instantiate_model_decomposition( + pmitd_data, + pmitd_type, + optimizer, + build_method; + multinetwork=multinetwork, + pmitd_ref_extensions=pmitd_ref_extensions, + export_models=export_models, + kwargs... + ) + + # Calls the _SDO optimize!(..) function and solves decomposition problem + _, solve_time, solve_bytes_alloc, sec_in_gc = @timed _SDO.optimize!(pmitd_optimizer) + + # Build the decomposition result/solution + result = build_pm_decomposition_solution(transmission_instantiated, solve_time) + result["solution"]["it"][_PMD.pmd_it_name] = Dict() + for d in subproblems_instantiated + ckt_name = d.data["ckt_name"] + build_soln = build_pmd_decomposition_solution(d, solve_time) + result["solution"]["it"][_PMD.pmd_it_name][ckt_name] = build_soln["solution"]["it"]["pmd"]["solution"] + + # Add boundary info. to PMITD dict. + for (b_name, b_data) in result["solution"]["it"][_PMD.pmd_it_name][ckt_name]["boundary"] + result["solution"]["it"][pmitd_it_name]["boundary"][b_name]["pbound_aux"] = b_data["pbound_aux"] + result["solution"]["it"][pmitd_it_name]["boundary"][b_name]["qbound_aux"] = b_data["qbound_aux"] + end + + end + + # Inform about the time for solving the problem (*change to @debug) + @info "pmitd decomposition model solution time (instantiate + optimization): $(time() - start_time)" + + # Transform solution (both T&D) - SI or per unit - MATH or ENG. + if (make_si == false) + _transform_decomposition_solution_to_pu!(result, pmitd_data; make_si, multinetwork=multinetwork, solution_model=solution_model) + else + _transform_decomposition_solution_to_si!(result, pmitd_data; make_si, multinetwork=multinetwork, solution_model=solution_model) + end + + # Force call Garbage collector to reduce RAM usage + GC.gc() + else @error "The problem specification (build_method) or optimizer defined is not supported! Please use a supported optimizer or build_method." throw(error()) diff --git a/src/core/solution_decomposition.jl b/src/core/solution_decomposition.jl index 5c5c91c..f09a800 100644 --- a/src/core/solution_decomposition.jl +++ b/src/core/solution_decomposition.jl @@ -8,7 +8,7 @@ Runs decomposition process and returns organized result solution dictionary. """ -function build_pm_decomposition_solution(pm, solve_time) +function build_pm_decomposition_solution(pm, solve_time::Float64=0.0) # Build and organize the result dictionary result = Dict{String, Any}("solution" => Dict{String, Any}("it" => Dict{String, Any}(_PM.pm_it_name => Dict{String, Any}(), pmitd_it_name => Dict{String, Any}()))) @@ -19,10 +19,8 @@ function build_pm_decomposition_solution(pm, solve_time) end -function build_pmd_decomposition_solution(pmd) +function build_pmd_decomposition_solution(pmd, solve_time::Float64=0.0) - # solve_time default - solve_time = 0.0 # Build and organize the result dictionary result = Dict{String, Any}("solution" => Dict{String, Any}("it" => Dict{String, Any}(_PMD.pmd_it_name => Dict{String, Any}(), pmitd_it_name => Dict{String, Any}()))) result["solution"]["it"][_PMD.pmd_it_name] = _IM.build_result(pmd, solve_time) @@ -33,7 +31,8 @@ end # ----------------- Custom-made parallelize multiprocessing function ------------------------ - +# TODO: Move this function to its own file +# TODO: add export_models=true option to export models function optimize_subproblem_multiprocessing( data::Dict{String, Any}, type, @@ -71,29 +70,15 @@ function optimize_subproblem_multiprocessing( # Setup and initilize the subproblem JuMP.optimize!(subproblem_instantiated.model) # Setup the Subproblem model - # Initialize Subproblem - retval_init_sub = _SDO.InitializeSubproblemSolver( - subproblem_instantiated.model.moi_backend.optimizer.model.inner + # Solve the subproblem + _SDO.solve_subproblem!(subproblem_instantiated.model, + status_signal, + mp_string_rc, + sp_string_rc, + i, + number_of_subprobs ) - # Check initialization - @assert retval_init_sub == 1 - - # Keep alive the process with persistent subproblem data - while true - - # Retrieve 'status' signal - status_sig = Distributed.take!(status_signal) - - if (status_sig == "continue") - _SDO.solve_subproblem(subproblem_instantiated.model.moi_backend.optimizer.model.inner, mp_string_rc, sp_string_rc, i, number_of_subprobs) # Solve - else - sub_final_status = _SDO.SubproblemSolverFinalize(subproblem_instantiated.model.moi_backend.optimizer.model.inner) # Finalize Subproblem - break # break from while-true - end - - end - # Build, transform, and write result to file result = build_pmd_decomposition_solution(subproblem_instantiated) @@ -112,228 +97,7 @@ function optimize_subproblem_multiprocessing( end - - - - - - - - - - - - - -# ----------------- Custom-made build_result and build_solution_values function ------------------------ - -"" -function build_result_subproblems(aim::_IM.AbstractInfrastructureModel, solve_time; solution_processors=[]) - # try-catch is needed until solvers reliably support ResultCount() - result_count = 1 - try - result_count = JuMP.result_count(aim.model) - catch - @warn("the given optimizer does not provide the ResultCount() attribute, assuming the solver returned a solution which may be incorrect."); - end - - solution = Dict{String,Any}() - - if result_count > 0 - solution = build_solution_subproblem(aim, post_processors=solution_processors) - else - @warn("model has no results, solution cannot be built") - end - - result = Dict{String,Any}( - "optimizer" => JuMP.solver_name(aim.model), - "termination_status" => JuMP.termination_status(aim.model), - "primal_status" => JuMP.primal_status(aim.model), - "dual_status" => JuMP.dual_status(aim.model), - "objective" => _guard_objective_value(aim.model), - "objective_lb" => _guard_objective_bound(aim.model), - "solve_time" => solve_time, - "solution" => solution, - ) - - return result -end - - -"" -function _guard_objective_value(model) - obj_val = NaN - - try - obj_val = JuMP.objective_value(model) - catch - end - - return obj_val -end - - -"" -function _guard_objective_bound(model) - obj_lb = -Inf - - try - obj_lb = JuMP.objective_bound(model) - catch - end - - return obj_lb -end - - - -"" -function build_solution_subproblem(aim::_IM.AbstractInfrastructureModel; post_processors=[]) - - sol = Dict{String, Any}("it" => Dict{String, Any}()) - sol["multiinfrastructure"] = true - - for it in _IM.it_ids(aim) - sol["it"][string(it)] = build_solution_values_subproblem(aim.model, aim.sol[:it][it]) - sol["it"][string(it)]["multinetwork"] = true - end - - _IM.solution_preprocessor(aim, sol) - - for post_processor in post_processors - post_processor(aim, sol) - end - - for it in _IM.it_ids(aim) - it_str = string(it) - data_it = _IM.ismultiinfrastructure(aim) ? aim.data["it"][it_str] : aim.data - - if _IM.ismultinetwork(data_it) - sol["it"][it_str]["multinetwork"] = true - else - for (k, v) in sol["it"][it_str]["nw"]["$(nw_id_default)"] - sol["it"][it_str][k] = v - end - - sol["it"][it_str]["multinetwork"] = false - delete!(sol["it"][it_str], "nw") - end - - if !_IM.ismultiinfrastructure(aim) - for (k, v) in sol["it"][it_str] - sol[k] = v - end - - delete!(sol["it"], it_str) - end - end - - if !_IM.ismultiinfrastructure(aim) - sol["multiinfrastructure"] = false - delete!(sol, "it") - end - - return sol -end - - -"" -function build_solution_values_subproblem(model::JuMP.Model, var::Dict) - return build_solution_values(model, var) -end - - -"" -function build_solution_values(model::JuMP.Model, var::Dict) - sol = Dict{String, Any}() - for (key, val) in var - sol[string(key)] = build_solution_values(model, val) - end - return sol -end - -"" -function build_solution_values(model::JuMP.Model, var::JuMP.Containers.DenseAxisArray) - sol_tmp = [] - for val in eachindex(var) - push!(sol_tmp, build_solution_values(model, var[val])) - end - return sol_tmp -end - - -"" -function build_solution_values(model::JuMP.Model, var::Array{<:Any,1}) - return [build_solution_values(val) for val in var] -end - -"" -function build_solution_values(model::JuMP.Model, var::Array{<:Any,2}) - return [build_solution_values(var[i, j]) for i in 1:size(var, 1), j in 1:size(var, 2)] -end - -"" -function build_solution_values(model::JuMP.Model, var::Number) - return var -end - -"" -function build_solution_values(model::JuMP.Model, var::JuMP.VariableRef) - var_fr_model = JuMP.variable_by_name(model, string(var)) - return JuMP.value(var_fr_model) -end - -"" -function build_solution_values(model::JuMP.Model, var::JuMP.GenericAffExpr) - var_terms = var.terms # Get variable terms OrderedDict: (var => coeff) - var_keys = keys(var.terms) # Get the JuMP.VariableRef as keys - var_vector = collect(var_keys) # Collect the JuMP.VariableRef keys in a vector - - cmp_terms = [] # vector used to store the final computed terms (i.e., coeff*value) - for v in var_vector - v_name = string(v) # convert JuMP.VariableRef to string (for searching the value in the model) - v_coeff = var_terms[v] # get the coefficient from the OrderedDict of var_terms - v_model = JuMP.variable_by_name(model, v_name) # get the new value from the JuMP model. - push!(cmp_terms, v_coeff*JuMP.value(v_model)) # multiply the value obtained with the corresponding coefficient - add to vector of computed terms - end - - return sum(cmp_terms) # sum all the computed terms (coeff*value) - return the value -end - -"" -function build_solution_values(model::JuMP.Model, var::JuMP.GenericQuadExpr) - var_terms = var.terms # Get variable terms OrderedDict: (var => coeff) - var_keys = keys(var.terms) # Get the JuMP.VariableRef as keys - var_vector = collect(var_keys) # Collect the JuMP.VariableRef keys in a vector - - cmp_terms = [] # vector used to store the final computed terms (i.e., coeff*value) - for v in var_vector - v_name = string(v) # convert JuMP.VariableRef to string (for searching the value in the model) - v_coeff = var_terms[v] # get the coefficient from the OrderedDict of var_terms - v_model = JuMP.variable_by_name(model, v_name) # get the new value from the JuMP model. - push!(cmp_terms, v_coeff*JuMP.value(v_model)) # multiply the value obtained with the corresponding coefficient - add to vector of computed terms - end - - return sum(cmp_terms) # sum all the computed terms (coeff*value) - return the value -end - -"" -function build_solution_values(model::JuMP.Model, var::JuMP.NonlinearExpression) - return JuMP.value(var) -end - -"" -function build_solution_values(model::JuMP.Model, var::JuMP.ConstraintRef) - return JuMP.dual(var) -end - -"" -function build_solution_values(var::Any) - @warn("build_solution_values found unknown type $(typeof(var))") - return var -end - -# --------------------------------------------------------------------------------- +### ------ Transform solution functions ----- """ function _transform_decomposition_solution_to_pu!( @@ -383,14 +147,14 @@ function _transform_decomposition_solution_to_pu!(result, pmitd_data::Dict{Strin for (ckt_name, ckt_data) in pmitd_data["it"][_PMD.pmd_it_name] # Transform pmd MATH result to ENG - result["solution"]["it"][_PMD.pmd_it_name][ckt_name]["solution"] = _PMD.transform_solution( - result["solution"]["it"][_PMD.pmd_it_name][ckt_name]["solution"], + result["solution"]["it"][_PMD.pmd_it_name][ckt_name] = _PMD.transform_solution( + result["solution"]["it"][_PMD.pmd_it_name][ckt_name], pmitd_data["it"][_PMD.pmd_it_name][ckt_name]; make_si=make_si ) # Change PMD dictionary per_unit value (Not done automatically by PMD) - result["solution"]["it"][_PMD.pmd_it_name][ckt_name]["solution"]["per_unit"] = true + result["solution"]["it"][_PMD.pmd_it_name][ckt_name]["per_unit"] = true end # Transform pmitd MATH result ref to ENG result ref @@ -455,14 +219,14 @@ function _transform_decomposition_solution_to_si!(result, pmitd_data::Dict{Strin for (ckt_name, ckt_data) in pmitd_data["it"][_PMD.pmd_it_name] # Transform pmd MATH result to ENG - result["solution"]["it"][_PMD.pmd_it_name][ckt_name]["solution"] = _PMD.transform_solution( - result["solution"]["it"][_PMD.pmd_it_name][ckt_name]["solution"], + result["solution"]["it"][_PMD.pmd_it_name][ckt_name] = _PMD.transform_solution( + result["solution"]["it"][_PMD.pmd_it_name][ckt_name], pmitd_data["it"][_PMD.pmd_it_name][ckt_name]; make_si=make_si ) # Change PMD dictionary per_unit value (Not done automatically by PMD) - result["solution"]["it"][_PMD.pmd_it_name][ckt_name]["solution"]["per_unit"] = false + result["solution"]["it"][_PMD.pmd_it_name][ckt_name]["per_unit"] = false end # Transform pmitd MATH result ref to ENG result ref @@ -471,8 +235,8 @@ function _transform_decomposition_solution_to_si!(result, pmitd_data::Dict{Strin elseif (solution_model=="math") || (solution_model=="MATH") for (ckt_name, ckt_data) in pmitd_data["it"][_PMD.pmd_it_name] - result["solution"]["it"][_PMD.pmd_it_name][ckt_name]["solution"] = _PMD.solution_make_si( - result["solution"]["it"][_PMD.pmd_it_name][ckt_name]["solution"], + result["solution"]["it"][_PMD.pmd_it_name][ckt_name] = _PMD.solution_make_si( + result["solution"]["it"][_PMD.pmd_it_name][ckt_name], pmitd_data["it"][_PMD.pmd_it_name][ckt_name] ) end @@ -518,6 +282,12 @@ function _transform_pmitd_decomposition_solution_to_si!(result::Dict{String,<:An if haskey(boundary, "qbound_load") boundary["qbound_load"] = boundary["qbound_load"]*pmd_sbase end + if haskey(boundary, "pbound_load_scaled") + boundary["pbound_load_scaled"] = boundary["pbound_load_scaled"]*pmd_sbase + end + if haskey(boundary, "qbound_load_scaled") + boundary["qbound_load_scaled"] = boundary["qbound_load_scaled"]*pmd_sbase + end if haskey(boundary, "pbound_aux") boundary["pbound_aux"] = boundary["pbound_aux"].*pmd_sbase end diff --git a/src/form_decomposition/acp.jl b/src/form_decomposition/acp.jl index 8f9edd9..9cea255 100644 --- a/src/form_decomposition/acp.jl +++ b/src/form_decomposition/acp.jl @@ -209,57 +209,16 @@ The parameter `export_models` is a boolean that determines if the JuMP models' s """ function generate_boundary_linking_vars(pm::_PM.ACPPowerModel, pmd::_PMD.ACPUPowerModel, boundary_number::String; nw::Int=nw_id_default, export_models::Bool=false) - # Parse to Int - boundary_number = parse(Int64, boundary_number) - - # Get boundary info. - boundary = _PMD.ref(pmd, nw, :boundary, boundary_number) - - f_bus = boundary["f_bus"] # convention: from bus Transmission always! - t_bus = boundary["t_bus"] # convention: to bus Distribution always! - - # Distribution: Aux vars (subproblem) - f_idx = (boundary_number, f_bus, t_bus) - p_aux = _PMD.var(pmd, nw, :pbound_aux, f_idx) - q_aux = _PMD.var(pmd, nw, :qbound_aux, f_idx) - - # Distribution: vm (subproblem) - vm = _PMD.var(pmd, nw, :vm, t_bus) - va = _PMD.var(pmd, nw, :va, t_bus) - - # Transmission: Vm (master) - Vm = _PM.var(pm, nw, :vm, f_bus) - Va = _PM.var(pm, nw, :va, f_bus) + transmission_linking_vars = generate_boundary_linking_vars_transmission(pm, boundary_number; nw=nw, export_models=export_models) + distribution_linking_vars = generate_boundary_linking_vars_distribution(pmd, boundary_number; nw=nw, export_models=export_models) - # Transmission: Pload & Qload (master) - P_load = _PM.var(pm, nw, :pbound_load_scaled, f_idx) - Q_load = _PM.var(pm, nw, :qbound_load_scaled, f_idx) + boundary_linking_vars = [transmission_linking_vars[1], distribution_linking_vars[1]] # use 1 to extract the vector of linking vars - TODO: see if [1] can be removed maintaining compat. - # boundary_linking_vars = [[P_load[1], Q_load[1], Vm], [p_aux[1], q_aux[1], vm[1]]] - boundary_linking_vars = [[P_load[1], Q_load[1], Vm, Va], [p_aux[1], q_aux[1], vm[1], va[1]]] - - if (export_models == true) - # Open file where shared vars indices are going to be written - file = open("shared_vars.txt", "a") - # Loop through the vector of shared variables - for sh_vect in boundary_linking_vars - for sh_var in sh_vect - str_to_write = "Shared Variable ($(sh_var)) Index: $(sh_var.index)\n" - # Write the string to the file - write(file, str_to_write) - end - end - # Close the file - close(file) - end - - return boundary_linking_vars + return boundary_linking_vars end - - function generate_boundary_linking_vars_transmission(pm::_PM.ACPPowerModel, boundary_number::String; nw::Int=nw_id_default, export_models::Bool=false) # Parse to Int @@ -284,7 +243,7 @@ function generate_boundary_linking_vars_transmission(pm::_PM.ACPPowerModel, boun if (export_models == true) # Open file where shared vars indices are going to be written - file = open("shared_vars_master.txt", "a") + file = open("shared_vars_transmission.txt", "a") # Loop through the vector of shared variables for sh_vect in boundary_linking_vars for sh_var in sh_vect @@ -327,7 +286,7 @@ function generate_boundary_linking_vars_distribution(pmd::_PMD.ACPUPowerModel, b if (export_models == true) # Open file where shared vars indices are going to be written - file = open("shared_vars_subproblem_$(boundary).txt", "a") + file = open("shared_vars_distribution_$(boundary).txt", "a") # Loop through the vector of shared variables for sh_vect in boundary_linking_vars for sh_var in sh_vect