diff --git a/src/core/optimization_container.jl b/src/core/optimization_container.jl index 7f62169581..be85ae1a9c 100644 --- a/src/core/optimization_container.jl +++ b/src/core/optimization_container.jl @@ -1339,25 +1339,6 @@ function get_expression( return get_expression(container, ExpressionKey(T, U, meta)) end -# Special getter functions to handle system balance expressions -function get_expression( - container::OptimizationContainer, - ::T, - ::Type{U}, - meta = CONTAINER_KEY_EMPTY_META, -) where {T <: SystemBalanceExpressions, U <: PM.AbstractPowerModel} - return get_expression(container, ExpressionKey(T, PSY.ACBus, meta)) -end - -function get_expression( - container::OptimizationContainer, - ::T, - ::Type{CopperPlatePowerModel}, - meta = CONTAINER_KEY_EMPTY_META, -) where {T <: SystemBalanceExpressions} - return get_expression(container, ExpressionKey(T, PSY.System, meta)) -end - function read_expressions(container::OptimizationContainer) return Dict( k => to_dataframe(jump_value.(v), k) for (k, v) in get_expressions(container) if diff --git a/src/devices_models/device_constructors/branch_constructor.jl b/src/devices_models/device_constructors/branch_constructor.jl index 6566bb9a1a..b78218d256 100644 --- a/src/devices_models/device_constructors/branch_constructor.jl +++ b/src/devices_models/device_constructors/branch_constructor.jl @@ -646,6 +646,22 @@ function construct_device!( return end +function construct_device!( + container::OptimizationContainer, + sys::PSY.System, + ::ModelConstructStage, + model::DeviceModel{T, HVDCTwoTerminalDispatch}, + network_model::NetworkModel{CopperPlatePowerModel}, +) where {T <: TwoTerminalHVDCTypes} + devices = + get_available_components(T, sys, get_attribute(model, "filter_function")) + @warn "CopperPlatePowerModel models with HVDC ignores inter-area losses" + add_constraints!(container, FlowRateConstraintFromTo, devices, model, network_model) + add_constraints!(container, FlowRateConstraintToFrom, devices, model, network_model) + add_constraint_dual!(container, sys, model) + return +end + function construct_device!( container::OptimizationContainer, sys::PSY.System, diff --git a/src/devices_models/devices/AC_branches.jl b/src/devices_models/devices/AC_branches.jl index a5f5cd4252..852613a960 100644 --- a/src/devices_models/devices/AC_branches.jl +++ b/src/devices_models/devices/AC_branches.jl @@ -300,7 +300,7 @@ function add_constraints!( time_steps, ) nodal_balance_expressions = - get_expression(container, ActivePowerBalance(), StandardPTDFModel) + get_expression(container, ActivePowerBalance(), PSY.ACBus) flow_variables = get_variable(container, FlowActivePowerVariable(), B) jump_model = get_jump_model(container) diff --git a/src/devices_models/devices/TwoTerminalDC_branches.jl b/src/devices_models/devices/TwoTerminalDC_branches.jl index 5483ac226f..c29df3749f 100644 --- a/src/devices_models/devices/TwoTerminalDC_branches.jl +++ b/src/devices_models/devices/TwoTerminalDC_branches.jl @@ -76,6 +76,30 @@ get_variable_lower_bound( ::HVDCTwoTerminalDispatch, ) = 0.0 +get_variable_upper_bound( + ::FlowActivePowerFromToVariable, + d::PSY.TwoTerminalHVDCLine, + ::HVDCTwoTerminalDispatch, +) = PSY.get_active_power_limits_from(d).max + +get_variable_lower_bound( + ::FlowActivePowerFromToVariable, + d::PSY.TwoTerminalHVDCLine, + ::HVDCTwoTerminalDispatch, +) = PSY.get_active_power_limits_from(d).min + +get_variable_upper_bound( + ::FlowActivePowerToFromVariable, + d::PSY.TwoTerminalHVDCLine, + ::HVDCTwoTerminalDispatch, +) = PSY.get_active_power_limits_to(d).max + +get_variable_lower_bound( + ::FlowActivePowerToFromVariable, + d::PSY.TwoTerminalHVDCLine, + ::HVDCTwoTerminalDispatch, +) = PSY.get_active_power_limits_to(d).min + function get_variable_upper_bound( ::HVDCLosses, d::PSY.TwoTerminalHVDCLine, @@ -188,58 +212,58 @@ function add_constraints!( return end -function add_constraints!( +function _add_hvdc_flow_constraints!( container::OptimizationContainer, - ::Type{FlowRateConstraint}, - devices::IS.FlattenIteratorWrapper{T}, - model::DeviceModel{T, HVDCTwoTerminalLossless}, - network_model::NetworkModel{CopperPlatePowerModel}, + devices::Union{Vector{T}, IS.FlattenIteratorWrapper{T}}, + constraint::FlowRateConstraintFromTo, ) where {T <: PSY.TwoTerminalHVDCLine} - inter_network_branches = T[] - for d in devices - ref_bus_from = get_reference_bus(network_model, PSY.get_arc(d).from) - ref_bus_to = get_reference_bus(network_model, PSY.get_arc(d).to) - if ref_bus_from != ref_bus_to - push!(inter_network_branches, d) - end - end - if !isempty(inter_network_branches) - add_constraints!( - container, - FlowRateConstraint, - inter_network_branches, - model, - network_model, - ) - end - return + _add_hvdc_flow_constraints!( + container, + devices, + FlowActivePowerFromToVariable(), + constraint, + ) end -function add_constraints!( +function _add_hvdc_flow_constraints!( container::OptimizationContainer, - ::Type{T}, - devices::IS.FlattenIteratorWrapper{U}, - ::DeviceModel{U, HVDCTwoTerminalDispatch}, - ::NetworkModel{<:PM.AbstractDCPModel}, -) where {T <: FlowRateConstraintFromTo, U <: PSY.TwoTerminalHVDCLine} + devices::Union{Vector{T}, IS.FlattenIteratorWrapper{T}}, + constraint::FlowRateConstraintToFrom, +) where {T <: PSY.TwoTerminalHVDCLine} + _add_hvdc_flow_constraints!( + container, + devices, + FlowActivePowerToFromVariable(), + constraint, + ) +end + +function _add_hvdc_flow_constraints!( + container::OptimizationContainer, + devices::Union{Vector{T}, IS.FlattenIteratorWrapper{T}}, + var::Union{FlowActivePowerFromToVariable, FlowActivePowerToFromVariable}, + constraint::Union{FlowRateConstraintFromTo, FlowRateConstraintToFrom}, +) where {T <: PSY.TwoTerminalHVDCLine} time_steps = get_time_steps(container) names = [PSY.get_name(d) for d in devices] - var = get_variable(container, FlowActivePowerFromToVariable(), U) + variable = get_variable(container, var, T) constraint_ub = - add_constraints_container!(container, T(), U, names, time_steps; meta = "ub") + add_constraints_container!(container, constraint, T, names, time_steps; meta = "ub") constraint_lb = - add_constraints_container!(container, T(), U, names, time_steps; meta = "lb") + add_constraints_container!(container, constraint, T, names, time_steps; meta = "lb") for d in devices - min_rate, max_rate = PSY.get_active_power_limits_from(d) + check_hvdc_line_limits_consistency(d) + max_rate = get_variable_upper_bound(var, d, HVDCTwoTerminalDispatch()) + min_rate = get_variable_lower_bound(var, d, HVDCTwoTerminalDispatch()) for t in time_steps constraint_ub[PSY.get_name(d), t] = JuMP.@constraint( get_jump_model(container), - var[PSY.get_name(d), t] <= max_rate + variable[PSY.get_name(d), t] <= max_rate ) constraint_lb[PSY.get_name(d), t] = JuMP.@constraint( get_jump_model(container), - min_rate <= var[PSY.get_name(d), t] + min_rate <= variable[PSY.get_name(d), t] ) end end @@ -250,30 +274,35 @@ function add_constraints!( container::OptimizationContainer, ::Type{T}, devices::IS.FlattenIteratorWrapper{U}, - ::DeviceModel{U, HVDCTwoTerminalDispatch}, - ::NetworkModel{<:PM.AbstractDCPModel}, -) where {T <: FlowRateConstraintToFrom, U <: PSY.TwoTerminalHVDCLine} - time_steps = get_time_steps(container) - names = [PSY.get_name(d) for d in devices] - - var = get_variable(container, FlowActivePowerToFromVariable(), U) - constraint_ub = - add_constraints_container!(container, T(), U, names, time_steps; meta = "ub") - constraint_lb = - add_constraints_container!(container, T(), U, names, time_steps; meta = "lb") + model::DeviceModel{U, HVDCTwoTerminalDispatch}, + network_model::NetworkModel{CopperPlatePowerModel}, +) where { + T <: Union{FlowRateConstraintFromTo, FlowRateConstraintToFrom}, + U <: PSY.TwoTerminalHVDCLine, +} + inter_network_branches = U[] for d in devices - min_rate, max_rate = PSY.get_active_power_limits_to(d) - for t in time_steps - constraint_ub[PSY.get_name(d), t] = JuMP.@constraint( - get_jump_model(container), - var[PSY.get_name(d), t] <= max_rate - ) - constraint_lb[PSY.get_name(d), t] = JuMP.@constraint( - get_jump_model(container), - min_rate <= var[PSY.get_name(d), t] - ) + ref_bus_from = get_reference_bus(network_model, PSY.get_arc(d).from) + ref_bus_to = get_reference_bus(network_model, PSY.get_arc(d).to) + if ref_bus_from != ref_bus_to + push!(inter_network_branches, d) end end + if !isempty(inter_network_branches) + _add_hvdc_flow_constraints!(container, devices, T()) + end + return +end + +function add_constraints!( + container::OptimizationContainer, + ::Type{T}, + devices::IS.FlattenIteratorWrapper{U}, + ::DeviceModel{U, HVDCTwoTerminalDispatch}, + ::NetworkModel{<:PM.AbstractDCPModel}, +) where {T <: Union{FlowRateConstraintToFrom, FlowRateConstraintFromTo}, + U <: PSY.TwoTerminalHVDCLine} + _add_hvdc_flow_constraints!(container, devices, T()) return end diff --git a/src/devices_models/devices/common/add_to_expression.jl b/src/devices_models/devices/common/add_to_expression.jl index 5146cf131c..54aa14a590 100644 --- a/src/devices_models/devices/common/add_to_expression.jl +++ b/src/devices_models/devices/common/add_to_expression.jl @@ -93,7 +93,7 @@ function add_to_expression!( bus_number = PSY.get_number(PSY.get_bus(d)) name = PSY.get_name(d) _add_to_jump_expression!( - get_expression(container, T(), X)[bus_number, t], + get_expression(container, T(), PSY.ACBus)[bus_number, t], get_parameter_column_refs(param_container, name)[t], multiplier[name, t], ) @@ -122,7 +122,7 @@ function add_to_expression!( name = PSY.get_name(d) mult = get_expression_multiplier(U(), T(), d, W()) _add_to_jump_expression!( - get_expression(container, T(), X)[bus_number, t], + get_expression(container, T(), PSY.ACBus)[bus_number, t], parameter[name, t], mult, ) @@ -148,7 +148,7 @@ function add_to_expression!( X <: PM.AbstractPowerModel, } variable = get_variable(container, U(), V) - expression = get_expression(container, T(), X) + expression = get_expression(container, T(), PSY.ACBus) for d in devices, t in get_time_steps(container) name = PSY.get_name(d) bus_number = PSY.get_number(PSY.get_bus(d)) @@ -170,12 +170,13 @@ function add_to_expression!( ::Type{U}, devices::IS.FlattenIteratorWrapper{V}, ::DeviceModel{V, W}, - network_model::NetworkModel{StandardPTDFModel}, + network_model::NetworkModel{X}, ) where { T <: ActivePowerBalance, U <: HVDCLosses, V <: TwoTerminalHVDCTypes, W <: HVDCTwoTerminalDispatch, + X <: Union{StandardPTDFModel, CopperPlatePowerModel}, } variable = get_variable(container, U(), V) expression = get_expression(container, T(), PSY.System) @@ -194,6 +195,144 @@ function add_to_expression!( return end +""" +Default implementation to add branch variables to SystemBalanceExpressions +""" +function add_to_expression!( + container::OptimizationContainer, + ::Type{T}, + ::Type{U}, + devices::IS.FlattenIteratorWrapper{V}, + ::DeviceModel{V, W}, + network_model::NetworkModel{X}, +) where { + T <: ActivePowerBalance, + U <: FlowActivePowerToFromVariable, + V <: TwoTerminalHVDCTypes, + W <: AbstractDeviceFormulation, + X <: Union{PTDFPowerModel, StandardPTDFModel}, +} + var = get_variable(container, U(), V) + nodal_expr = get_expression(container, T(), PSY.ACBus) + sys_expr = get_expression(container, T(), PSY.System) + for d in devices + bus_no_to = PSY.get_number(PSY.get_arc(d).to) + ref_bus_from = get_reference_bus(network_model, PSY.get_arc(d).from) + ref_bus_to = get_reference_bus(network_model, PSY.get_arc(d).to) + for t in get_time_steps(container) + flow_variable = var[PSY.get_name(d), t] + _add_to_jump_expression!(nodal_expr[bus_no_to, t], flow_variable, 1.0) + if ref_bus_from != ref_bus_to + _add_to_jump_expression!(sys_expr[ref_bus_to, t], flow_variable, 1.0) + end + end + end + return +end + +""" +Default implementation to add branch variables to SystemBalanceExpressions +""" +function add_to_expression!( + container::OptimizationContainer, + ::Type{T}, + ::Type{U}, + devices::IS.FlattenIteratorWrapper{V}, + ::DeviceModel{V, W}, + network_model::NetworkModel{X}, +) where { + T <: ActivePowerBalance, + U <: FlowActivePowerFromToVariable, + V <: TwoTerminalHVDCTypes, + W <: AbstractTwoTerminalDCLineFormulation, + X <: Union{PTDFPowerModel, StandardPTDFModel}, +} + var = get_variable(container, U(), V) + nodal_expr = get_expression(container, T(), PSY.ACBus) + sys_expr = get_expression(container, T(), PSY.System) + for d in devices + bus_no_from = PSY.get_number(PSY.get_arc(d).from) + ref_bus_to = get_reference_bus(network_model, PSY.get_arc(d).to) + ref_bus_from = get_reference_bus(network_model, PSY.get_arc(d).from) + for t in get_time_steps(container) + flow_variable = var[PSY.get_name(d), t] + _add_to_jump_expression!(nodal_expr[bus_no_from, t], flow_variable, -1.0) + if ref_bus_from != ref_bus_to + _add_to_jump_expression!(sys_expr[ref_bus_from, t], flow_variable, -1.0) + end + end + end + return +end + +""" +Default implementation to add branch variables to SystemBalanceExpressions +""" +function add_to_expression!( + container::OptimizationContainer, + ::Type{T}, + ::Type{U}, + devices::IS.FlattenIteratorWrapper{V}, + ::DeviceModel{V, W}, + network_model::NetworkModel{X}, +) where { + T <: ActivePowerBalance, + U <: FlowActivePowerToFromVariable, + V <: TwoTerminalHVDCTypes, + W <: AbstractTwoTerminalDCLineFormulation, + X <: CopperPlatePowerModel, +} + if has_subnetworks(network_model) + var = get_variable(container, U(), V) + sys_expr = get_expression(container, T(), PSY.System) + for d in devices + ref_bus_from = get_reference_bus(network_model, PSY.get_arc(d).from) + ref_bus_to = get_reference_bus(network_model, PSY.get_arc(d).to) + for t in get_time_steps(container) + flow_variable = var[PSY.get_name(d), t] + if ref_bus_from != ref_bus_to + _add_to_jump_expression!(sys_expr[ref_bus_to, t], flow_variable, 1.0) + end + end + end + end + return +end + +""" +Default implementation to add branch variables to SystemBalanceExpressions +""" +function add_to_expression!( + container::OptimizationContainer, + ::Type{T}, + ::Type{U}, + devices::IS.FlattenIteratorWrapper{V}, + ::DeviceModel{V, W}, + network_model::NetworkModel{X}, +) where { + T <: ActivePowerBalance, + U <: FlowActivePowerFromToVariable, + V <: TwoTerminalHVDCTypes, + W <: AbstractTwoTerminalDCLineFormulation, + X <: CopperPlatePowerModel, +} + if has_subnetworks(network_model) + var = get_variable(container, U(), V) + sys_expr = get_expression(container, T(), PSY.System) + for d in devices + ref_bus_from = get_reference_bus(network_model, PSY.get_arc(d).from) + ref_bus_to = get_reference_bus(network_model, PSY.get_arc(d).to) + for t in get_time_steps(container) + flow_variable = var[PSY.get_name(d), t] + if ref_bus_from != ref_bus_to + _add_to_jump_expression!(sys_expr[ref_bus_to, t], flow_variable, -1.0) + end + end + end + end + return +end + """ Default implementation to add branch variables to SystemBalanceExpressions """ @@ -212,7 +351,7 @@ function add_to_expression!( X <: PM.AbstractPowerModel, } variable = get_variable(container, U(), V) - expression = get_expression(container, T(), X) + expression = get_expression(container, T(), PSY.ACBus) for d in devices name = PSY.get_name(d) bus_number = PSY.get_number(PSY.get_arc(d).from) @@ -240,12 +379,12 @@ function add_to_expression!( ) where { T <: ActivePowerBalance, U <: FlowActivePowerToFromVariable, - V <: PSY.Branch, + V <: PSY.ACBranch, W <: AbstractDeviceFormulation, X <: PM.AbstractPowerModel, } variable = get_variable(container, U(), V) - expression = get_expression(container, T(), X) + expression = get_expression(container, T(), PSY.ACBus) for d in devices name = PSY.get_name(d) bus_number = PSY.get_number(PSY.get_arc(d).to) @@ -275,7 +414,7 @@ function add_to_expression!( X <: PM.AbstractPowerModel, } variable = get_variable(container, U(), V) - expression = get_expression(container, T(), X) + expression = get_expression(container, T(), PSY.ACBus) for d in devices, t in get_time_steps(container) name = PSY.get_name(d) bus_number = PSY.get_number(PSY.get_bus(d)) @@ -307,7 +446,7 @@ function add_to_expression!( } param_container = get_parameter(container, U(), V) multiplier = get_multiplier_array(param_container) - expression = get_expression(container, T(), X) + expression = get_expression(container, T(), PSY.System) for d in devices device_bus = PSY.get_bus(d) ref_bus = get_reference_bus(network_model, device_bus) @@ -338,7 +477,7 @@ function add_to_expression!( X <: CopperPlatePowerModel, } parameter = get_parameter_array(container, U(), V) - expression = get_expression(container, T(), X) + expression = get_expression(container, T(), PSY.System) for d in devices name = PSY.get_name(d) device_bus = PSY.get_bus(d) @@ -575,7 +714,7 @@ function add_to_expression!( X <: PM.AbstractActivePowerModel, } var = get_variable(container, U(), V) - expression = get_expression(container, T(), X) + expression = get_expression(container, T(), PSY.ACBus) for d in devices for t in get_time_steps(container) flow_variable = var[PSY.get_name(d), t]