From c3d494475d6749d5e493ce48e0f9fdfcc961d9f8 Mon Sep 17 00:00:00 2001 From: noahrhodes Date: Thu, 25 Apr 2024 18:17:04 -0500 Subject: [PATCH] Add multiperiod threshold ops problem (#34) --- src/core/constraint.jl | 25 +++++++- src/prob/test.jl | 126 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 150 insertions(+), 1 deletion(-) diff --git a/src/core/constraint.jl b/src/core/constraint.jl index e825e98..650cd4d 100644 --- a/src/core/constraint.jl +++ b/src/core/constraint.jl @@ -107,7 +107,8 @@ function constraint_voltage_magnitude_sqr_on_off(pm::_PM.AbstractPowerModel, n:: end function constraint_load_served(pm::_PM.AbstractPowerModel) - threshold= _PM.ref(pm, :threshold) + nw1 = first(_PM.nw_ids(pm)) + threshold= _PM.ref(pm, nw1, :threshold) total_demand = sum(sum(load["pd"] for (id,load) in _PM.ref(pm, nwid, :load)) for nwid in _PM.nw_ids(pm)) z_demand = Dict(nwid => _PM.var(pm, nwid, :z_demand) for nwid in _PM.nw_ids(pm)) @@ -118,3 +119,25 @@ function constraint_load_served(pm::_PM.AbstractPowerModel) ) >= threshold*total_demand ) end + +function consistent_shutoff_variables(pm::_PM.AbstractPowerModel) + nw1 = first(_PM.nw_ids(pm)) + for nwid in _PM.nw_ids(pm) + for i in _PM.ids(pm, nwid, :branch) + z_branch_1 = _PM.var(pm, nw1, :z_branch, i) + z_branch_nwid = _PM.var(pm, nwid, :z_branch, i) + JuMP.@constraint(pm.model, z_branch_1 == z_branch_nwid) + end + for i in _PM.ids(pm, nwid, :gen) + z_gen_1 = _PM.var(pm, nw1, :z_gen, i) + z_gen_nwid = _PM.var(pm, nwid, :z_gen, i) + JuMP.@constraint(pm.model, z_gen_1 == z_gen_nwid) + end + for i in _PM.ids(pm, nwid, :bus) + z_bus_1 = _PM.var(pm, nw1, :z_bus, i) + z_bus_nwid = _PM.var(pm, nwid, :z_bus, i) + JuMP.@constraint(pm.model, z_bus_1 == z_bus_nwid) + end + end +end + diff --git a/src/prob/test.jl b/src/prob/test.jl index ebc7ea6..4e0f69a 100644 --- a/src/prob/test.jl +++ b/src/prob/test.jl @@ -794,4 +794,130 @@ function _build_redispatch(pm::_PM.AbstractPowerModel) ) end +# MOPS Threshold (single shutoff variable across periods) +"" +function _run_strg_mops_threshold(file, model_constructor::Type, optimizer; kwargs...) + return _PM.solve_model(file, model_constructor, optimizer, _build_mn_strg_ops_threshold; + multinetwork=true, ref_extensions=[_PM.ref_add_on_off_va_bounds!], kwargs...) +end + + +function _build_mn_strg_ops_threshold(pm::_PM.AbstractPowerModel) + for (n, network) in _PM.nws(pm) + + variable_bus_active_indicator(pm, nw=n) + variable_bus_voltage_on_off(pm, nw=n) + + _PM.variable_gen_indicator(pm, nw=n) + _PM.variable_gen_power_on_off(pm, nw=n) + + _PM.variable_storage_indicator(pm, nw=n) + _PM.variable_storage_power_mi_on_off(pm, nw=n) + + _PM.variable_branch_indicator(pm, nw=n) + _PM.variable_branch_power(pm, nw=n) + + _PM.variable_load_power_factor(pm, nw=n, relax=true) + _PM.variable_shunt_admittance_factor(pm, nw=n, relax=true) + + _PM.constraint_model_voltage_on_off(pm, nw=n) + for i in _PM.ids(pm, :ref_buses, nw=n) + _PM.constraint_theta_ref(pm, i, nw=n) + end + + for i in _PM.ids(pm, :gen, nw=n) + constraint_generation_active(pm, i, nw=n) + end + + for i in _PM.ids(pm, :bus, nw=n) + constraint_bus_voltage_on_off(pm, i, nw=n) + _PMR.constraint_power_balance_shed(pm, i, nw=n) + end + + for i in _PM.ids(pm, :storage, nw=n) + constraint_storage_active(pm, i, nw=n) + _PM.constraint_storage_state(pm, i, nw=n) + _PM.constraint_storage_complementarity_mi(pm, i, nw=n) + _PM.constraint_storage_on_off(pm,i, nw=n) + _PM.constraint_storage_losses(pm, i, nw=n) + _PM.constraint_storage_thermal_limit(pm, i, nw=n) + end + + for i in _PM.ids(pm, :branch, nw=n) + constraint_branch_active(pm, i, nw=n) + _PM.constraint_ohms_yt_from_on_off(pm, i, nw=n) + _PM.constraint_ohms_yt_to_on_off(pm, i, nw=n) + + _PM.constraint_voltage_angle_difference_on_off(pm, i, nw=n) + + _PM.constraint_thermal_limit_from_on_off(pm, i, nw=n) + _PM.constraint_thermal_limit_to_on_off(pm, i, nw=n) + end + + for i in _PM.ids(pm, :load, nw=n) + constraint_load_active(pm, i, nw=n) + end + end + + network_ids = sort(collect(_PM.nw_ids(pm))) + + n_1 = network_ids[1] + for i in _PM.ids(pm, :storage, nw=n_1) + _PM.constraint_storage_state(pm, i, nw=n_1) + end + + for n_2 in network_ids[2:end] + for i in _PM.ids(pm, :storage, nw=n_2) + _PM.constraint_storage_state(pm, i, n_1, n_2) + end + n_1 = n_2 + end + + constraint_load_served(pm) # threshold for load served + consistent_shutoff_variables(pm) + + # Add Objective Function + # ---------------------- + # Maximize power delivery while minimizing wildfire risk + n_1 = network_ids[1] + if haskey(_PM.ref(pm, n_1), :risk_weight) + alpha = _PM.ref(pm, n_1, :risk_weight) + else + Memento.warn(_PM._LOGGER, "network data should specify risk_weight, using 0.5 as a default") + alpha = 0.5 + end + + for comp_type in [:gen, :load, :bus, :branch] + for nwid in _PM.nw_ids(pm) + for (id,comp) in _PM.ref(pm, nwid, comp_type) + if ~haskey(comp, "power_risk") + Memento.warn(_PM._LOGGER, "$(comp_type) $(id) does not have a power_risk value, using 0.0 as a default") + comp["power_risk"] = 0.0 + end + end + end + end + + load_weight = Dict(nwid => + Dict(i => get(load, "weight", 1.0) for (i,load) in _PM.ref(pm, nwid, :load)) + for nwid in _PM.nw_ids(pm)) + + z_demand = Dict(nwid => _PM.var(pm, nwid, :z_demand) for nwid in _PM.nw_ids(pm)) + z_storage = Dict(nwid => _PM.var(pm, nwid, :z_storage) for nwid in _PM.nw_ids(pm)) + z_branch = Dict(nwid => _PM.var(pm, nwid, :z_branch) for nwid in _PM.nw_ids(pm)) + z_bus = Dict(nwid => _PM.var(pm, nwid, :z_bus) for nwid in _PM.nw_ids(pm)) + z_gen = Dict(nwid => _PM.var(pm, nwid, :z_gen) for nwid in _PM.nw_ids(pm)) + + JuMP.@objective(pm.model, Min, + sum( + sum(z_branch[nwid][i]*branch["power_risk"] for (i,branch) in _PM.ref(pm, nwid, :branch))+ + sum(z_bus[nwid][i]*bus["power_risk"] for (i,bus) in _PM.ref(pm, nwid, :bus))+ + sum(z_gen[nwid][i]*gen["power_risk"] for (i,gen) in _PM.ref(pm, nwid, :gen))+ + sum(z_demand[nwid][i]*load["power_risk"] for (i,load) in _PM.ref(pm, nwid, :load))+ + sum(z_storage[nwid][i]*storage["power_risk"] for (i,storage) in _PM.ref(pm, nwid, :storage)) + for nwid in _PM.nw_ids(pm) + ) + ) +end +