From 066ac2d7f66c8f0f672d837187032a95ba42ddbd Mon Sep 17 00:00:00 2001 From: Jose Daniel Lara Date: Tue, 24 Sep 2024 13:23:00 -0600 Subject: [PATCH] WIP change IC handling for must run --- src/core/initial_conditions.jl | 2 +- src/core/optimization_container.jl | 3 +- .../device_constructors/branch_constructor.jl | 2 +- .../devices/common/duration_constraints.jl | 41 ++++++--- .../devices/thermal_generation.jl | 16 ++-- .../add_initial_condition.jl | 91 ++++++++++++++----- src/operation/operation_model_interface.jl | 5 +- ..._device_thermal_generation_constructors.jl | 11 ++- 8 files changed, 118 insertions(+), 53 deletions(-) diff --git a/src/core/initial_conditions.jl b/src/core/initial_conditions.jl index 329cb47c94..5474275422 100644 --- a/src/core/initial_conditions.jl +++ b/src/core/initial_conditions.jl @@ -3,7 +3,7 @@ Container for the initial condition data """ mutable struct InitialCondition{ T <: InitialConditionType, - U <: Union{JuMP.VariableRef, Float64}, + U <: Union{JuMP.VariableRef, Float64, Nothing}, } component::PSY.Component value::U diff --git a/src/core/optimization_container.jl b/src/core/optimization_container.jl index c0fc9dfba6..084bd32103 100644 --- a/src/core/optimization_container.jl +++ b/src/core/optimization_container.jl @@ -1503,7 +1503,8 @@ function _add_initial_condition_container!( else param_type = Float64 end - ini_conds = Vector{InitialCondition{T, param_type}}(undef, length_devices) + ini_type = Union{InitialCondition{T, param_type}, InitialCondition{T, Nothing}} + ini_conds = Vector{ini_type}(undef, length_devices) _assign_container!(container.initial_conditions, ic_key, ini_conds) return ini_conds end diff --git a/src/devices_models/device_constructors/branch_constructor.jl b/src/devices_models/device_constructors/branch_constructor.jl index 9aff973682..616e82d74b 100644 --- a/src/devices_models/device_constructors/branch_constructor.jl +++ b/src/devices_models/device_constructors/branch_constructor.jl @@ -167,7 +167,7 @@ function construct_device!( end function construct_device!( - ::OptimizationContainer, + container::OptimizationContainer, sys::PSY.System, ::ModelConstructStage, model::DeviceModel{<:PSY.ACBranch, StaticBranchUnbounded}, diff --git a/src/devices_models/devices/common/duration_constraints.jl b/src/devices_models/devices/common/duration_constraints.jl index 954a7e2aa5..12a1f8d7b8 100644 --- a/src/devices_models/devices/common/duration_constraints.jl +++ b/src/devices_models/devices/common/duration_constraints.jl @@ -52,7 +52,10 @@ function device_duration_retrospective!( varstart = get_variable(container, var_types[2], T) varstop = get_variable(container, var_types[3], T) - set_names = [get_component_name(ic) for ic in initial_duration[:, 1]] + set_names = [ + get_component_name(ic) for + ic in initial_duration[:, 1] if !isnothing(get_value(ic)) + ] con_up = add_constraints_container!( container, cons_type, @@ -72,6 +75,7 @@ function device_duration_retrospective!( for t in time_steps for (ix, ic) in enumerate(initial_duration[:, 1]) + isnothing(get_value(ic)) && continue name = get_component_name(ic) # Minimum Up-time Constraint lhs_on = JuMP.GenericAffExpr{Float64, JuMP.VariableRef}(0) @@ -84,10 +88,11 @@ function device_duration_retrospective!( JuMP.add_to_expression!(lhs_on, 1) end con_up[name, t] = - JuMP.@constraint(container.JuMPmodel, lhs_on - varon[name, t] <= 0.0) + JuMP.@constraint(get_jump_model(container), lhs_on - varon[name, t] <= 0.0) end for (ix, ic) in enumerate(initial_duration[:, 2]) + isnothing(get_value(ic)) && continue name = get_component_name(ic) # Minimum Down-time Constraint lhs_off = JuMP.GenericAffExpr{Float64, JuMP.VariableRef}(0) @@ -100,11 +105,12 @@ function device_duration_retrospective!( JuMP.add_to_expression!(lhs_off, 1) end con_down[name, t] = - JuMP.@constraint(container.JuMPmodel, lhs_off + varon[name, t] <= 1.0) + JuMP.@constraint(get_jump_model(container), lhs_off + varon[name, t] <= 1.0) end end return end + @doc raw""" This formulation of the duration constraints looks ahead in the time frame of the model. @@ -165,6 +171,7 @@ function device_duration_look_ahead!( for t in time_steps for (ix, ic) in enumerate(initial_duration[:, 1]) + isnothing(get_value(ic)) && continue name = get_component_name(ic) # Minimum Up-time Constraint lhs_on = JuMP.GenericAffExpr{Float64, JuMP.VariableRef}(0) @@ -177,12 +184,13 @@ function device_duration_look_ahead!( lhs_on += get_value(ic) end con_up[name, t] = JuMP.@constraint( - container.JuMPmodel, + get_jump_model(container), varstop[name, t] * duration_data[ix].up - lhs_on <= 0.0 ) end for (ix, ic) in enumerate(initial_duration[:, 2]) + isnothing(get_value(ic)) && continue name = get_component_name(ic) # Minimum Down-time Constraint lhs_off = JuMP.GenericAffExpr{Float64, JuMP.VariableRef}(0) @@ -195,7 +203,7 @@ function device_duration_look_ahead!( lhs_off += get_value(ic) end con_down[name, t] = JuMP.@constraint( - container.JuMPmodel, + get_jump_model(container), varstart[name, t] * duration_data[ix].down - lhs_off <= 0.0 ) end @@ -278,8 +286,8 @@ function device_duration_parameters!( for t in time_steps for (ix, ic) in enumerate(initial_duration[:, 1]) + isnothing(get_value(ic)) && continue name = get_component_name(ic) - # Minimum Up-time Constraint lhs_on = JuMP.GenericAffExpr{Float64, JuMP.VariableRef}(0) for i in UnitRange{Int}(Int(t - duration_data[ix].up + 1), t) @@ -294,16 +302,20 @@ function device_duration_parameters!( if t <= duration_data[ix].up lhs_on += get_value(ic) con_up[name, t] = JuMP.@constraint( - container.JuMPmodel, + get_jump_model(container), varstop[name, t] * duration_data[ix].up - lhs_on <= 0.0 ) else con_up[name, t] = - JuMP.@constraint(container.JuMPmodel, lhs_on - varon[name, t] <= 0.0) + JuMP.@constraint( + get_jump_model(container), + lhs_on - varon[name, t] <= 0.0 + ) end end for (ix, ic) in enumerate(initial_duration[:, 2]) + isnothing(get_value(ic)) && continue name = get_component_name(ic) # Minimum Down-time Constraint lhs_off = JuMP.GenericAffExpr{Float64, JuMP.VariableRef}(0) @@ -319,12 +331,15 @@ function device_duration_parameters!( if t <= duration_data[ix].down lhs_off += get_value(ic) con_down[name, t] = JuMP.@constraint( - container.JuMPmodel, + get_jump_model(container), varstart[name, t] * duration_data[ix].down - lhs_off <= 0.0 ) else con_down[name, t] = - JuMP.@constraint(container.JuMPmodel, lhs_off + varon[name, t] <= 1.0) + JuMP.@constraint( + get_jump_model(container), + lhs_off + varon[name, t] <= 1.0 + ) end end end @@ -395,6 +410,7 @@ function device_duration_compact_retrospective!( total_time_steps = length(time_steps) for t in time_steps for (ix, ic) in enumerate(initial_duration[:, 1]) + isnothing(get_value(ic)) && continue name = get_component_name(ic) # Minimum Up-time Constraint lhs_on = JuMP.GenericAffExpr{Float64, JuMP.VariableRef}(0) @@ -413,10 +429,11 @@ function device_duration_compact_retrospective!( continue end con_up[name, t] = - JuMP.@constraint(container.JuMPmodel, lhs_on - varon[name, t] <= 0.0) + JuMP.@constraint(get_jump_model(container), lhs_on - varon[name, t] <= 0.0) end for (ix, ic) in enumerate(initial_duration[:, 2]) + isnothing(get_value(ic)) && continue name = get_component_name(ic) # Minimum Down-time Constraint lhs_off = JuMP.GenericAffExpr{Float64, JuMP.VariableRef}(0) @@ -435,7 +452,7 @@ function device_duration_compact_retrospective!( continue end con_down[name, t] = - JuMP.@constraint(container.JuMPmodel, lhs_off + varon[name, t] <= 1.0) + JuMP.@constraint(get_jump_model(container), lhs_off + varon[name, t] <= 1.0) end end for c in [con_up, con_down] diff --git a/src/devices_models/devices/thermal_generation.jl b/src/devices_models/devices/thermal_generation.jl index 5f61b348ef..bf8f5cbc7c 100644 --- a/src/devices_models/devices/thermal_generation.jl +++ b/src/devices_models/devices/thermal_generation.jl @@ -61,7 +61,7 @@ get_expression_multiplier(::OnStatusParameter, ::ActivePowerRangeExpressionLB, d get_expression_multiplier(::OnStatusParameter, ::ActivePowerBalance, d::PSY.ThermalGen, ::AbstractThermalFormulation) = PSY.get_active_power_limits(d).min #################### Initial Conditions for models ############### -initial_condition_default(::DeviceStatus, d::PSY.ThermalGen, ::AbstractThermalFormulation) = max(PSY.get_must_run(d), PSY.get_status(d)) +initial_condition_default(::DeviceStatus, d::PSY.ThermalGen, ::AbstractThermalFormulation) = PSY.get_status(d) ? 1.0 : 0.0 initial_condition_variable(::DeviceStatus, d::PSY.ThermalGen, ::AbstractThermalFormulation) = OnVariable() initial_condition_default(::DevicePower, d::PSY.ThermalGen, ::AbstractThermalFormulation) = PSY.get_active_power(d) initial_condition_variable(::DevicePower, d::PSY.ThermalGen, ::AbstractThermalFormulation) = ActivePowerVariable() @@ -818,14 +818,14 @@ function calculate_aux_variable_value!( time_steps = get_time_steps(container) for ix in eachindex(JuMP.axes(aux_variable_container)[1]) - IS.@assert_op JuMP.axes(aux_variable_container)[1][ix] == - JuMP.axes(on_variable_results)[1][ix] - IS.@assert_op JuMP.axes(aux_variable_container)[1][ix] == - get_component_name(ini_cond[ix]) - on_var = jump_value.(on_variable_results.data[ix, :]) ini_cond_value = get_condition(ini_cond[ix]) - aux_variable_container.data[ix, :] .= ini_cond_value - sum_on_var = sum(on_var) + if isnothing(get_value(ini_cond[ix])) + sum_on_var = time_steps[end] + else + on_var = jump_value.(on_variable_results.data[ix, :]) + aux_variable_container.data[ix, :] .= ini_cond_value + sum_on_var = sum(on_var) + end if sum_on_var == time_steps[end] # Unit was always on aux_variable_container.data[ix, :] += time_steps elseif sum_on_var == 0.0 # Unit was always off diff --git a/src/initial_conditions/add_initial_condition.jl b/src/initial_conditions/add_initial_condition.jl index 2d28e418d0..3a079c572c 100644 --- a/src/initial_conditions/add_initial_condition.jl +++ b/src/initial_conditions/add_initial_condition.jl @@ -5,7 +5,7 @@ function _get_initial_conditions_value( ::V, container::OptimizationContainer, ) where { - T <: InitialCondition{U, Float64}, + T <: Union{InitialCondition{U, Float64}, InitialCondition{U, Nothing}}, V <: Union{AbstractDeviceFormulation, AbstractServiceFormulation}, W <: PSY.Component, } where {U <: InitialConditionType} @@ -18,7 +18,7 @@ function _get_initial_conditions_value( end @debug "Device $(PSY.get_name(component)) initialized $U as $var_type" _group = LOG_GROUP_BUILD_INITIAL_CONDITIONS - return T(component, val) + return InitialCondition{U, Float64}(component, val) end function _get_initial_conditions_value( @@ -28,8 +28,8 @@ function _get_initial_conditions_value( ::V, container::OptimizationContainer, ) where { - T <: InitialCondition{U, JuMP.VariableRef}, - V <: Union{AbstractDeviceFormulation, AbstractServiceFormulation}, + T <: Union{InitialCondition{U, JuMP.VariableRef}, InitialCondition{U, Nothing}}, + V <: AbstractThermalFormulation, W <: PSY.Component, } where {U <: InitialConditionType} ic_data = get_initial_conditions_data(container) @@ -41,7 +41,10 @@ function _get_initial_conditions_value( end @debug "Device $(PSY.get_name(component)) initialized $U as $var_type" _group = LOG_GROUP_BUILD_INITIAL_CONDITIONS - return T(component, add_jump_parameter(get_jump_model(container), val)) + return InitialCondition{U, JuMP.VariableRef}( + component, + add_jump_parameter(get_jump_model(container), val), + ) end function _get_initial_conditions_value( @@ -51,14 +54,14 @@ function _get_initial_conditions_value( ::V, container::OptimizationContainer, ) where { - T <: InitialCondition{U, Float64}, - V <: Union{AbstractDeviceFormulation, AbstractServiceFormulation}, + T <: Union{InitialCondition{U, Float64}, InitialCondition{U, Nothing}}, + V <: AbstractThermalFormulation, W <: PSY.Component, } where {U <: InitialTimeDurationOff} ic_data = get_initial_conditions_data(container) var_type = initial_condition_variable(U(), component, V()) if !has_initial_condition_value(ic_data, var_type, W) - val = initial_condition_default(U(), component, V()) + @show val = initial_condition_default(U(), component, V()) else var = get_initial_condition_value(ic_data, var_type, W)[1, PSY.get_name(component)] val = 0.0 @@ -68,7 +71,7 @@ function _get_initial_conditions_value( end @debug "Device $(PSY.get_name(component)) initialized $U as $var_type" _group = LOG_GROUP_BUILD_INITIAL_CONDITIONS - return T(component, val) + return InitialCondition{U, Float64}(component, val) end function _get_initial_conditions_value( @@ -78,9 +81,9 @@ function _get_initial_conditions_value( ::V, container::OptimizationContainer, ) where { - T <: InitialCondition{U, JuMP.VariableRef}, - V <: Union{AbstractDeviceFormulation, AbstractServiceFormulation}, - W <: PSY.Component, + T <: Union{InitialCondition{U, JuMP.VariableRef}, InitialCondition{U, Nothing}}, + V <: AbstractThermalFormulation, + W <: PSY.ThermalGen, } where {U <: InitialTimeDurationOff} ic_data = get_initial_conditions_data(container) var_type = initial_condition_variable(U(), component, V()) @@ -95,7 +98,10 @@ function _get_initial_conditions_value( end @debug "Device $(PSY.get_name(component)) initialized $U as $var_type" _group = LOG_GROUP_BUILD_INITIAL_CONDITIONS - return T(component, add_jump_parameter(get_jump_model(container), val)) + return InitialCondition{U, JuMP.VariableRef}( + component, + add_jump_parameter(get_jump_model(container), val), + ) end function _get_initial_conditions_value( @@ -105,14 +111,14 @@ function _get_initial_conditions_value( ::V, container::OptimizationContainer, ) where { - T <: InitialCondition{U, Float64}, - V <: Union{AbstractDeviceFormulation, AbstractServiceFormulation}, - W <: PSY.Component, + T <: Union{InitialCondition{U, Float64}, InitialCondition{U, Nothing}}, + V <: AbstractDeviceFormulation, + W <: PSY.ThermalGen, } where {U <: InitialTimeDurationOn} ic_data = get_initial_conditions_data(container) var_type = initial_condition_variable(U(), component, V()) if !has_initial_condition_value(ic_data, var_type, W) - val = initial_condition_default(U(), component, V()) + @show val = initial_condition_default(U(), component, V()) else var = get_initial_condition_value(ic_data, var_type, W)[1, PSY.get_name(component)] val = 0.0 @@ -122,7 +128,7 @@ function _get_initial_conditions_value( end @debug "Device $(PSY.get_name(component)) initialized $U as $var_type" _group = LOG_GROUP_BUILD_INITIAL_CONDITIONS - return T(component, val) + return InitialCondition{U, Float64}(component, val) end function _get_initial_conditions_value( @@ -132,9 +138,9 @@ function _get_initial_conditions_value( ::V, container::OptimizationContainer, ) where { - T <: InitialCondition{U, JuMP.VariableRef}, - V <: Union{AbstractDeviceFormulation, AbstractServiceFormulation}, - W <: PSY.Component, + T <: Union{InitialCondition{U, JuMP.VariableRef}, InitialCondition{U, Nothing}}, + V <: AbstractDeviceFormulation, + W <: PSY.ThermalGen, } where {U <: InitialTimeDurationOn} ic_data = get_initial_conditions_data(container) var_type = initial_condition_variable(U(), component, V()) @@ -149,7 +155,10 @@ function _get_initial_conditions_value( end @debug "Device $(PSY.get_name(component)) initialized $U as $var_type" _group = LOG_GROUP_BUILD_INITIAL_CONDITIONS - return T(component, add_jump_parameter(get_jump_model(container), val)) + return InitialCondition{U, JuMP.VariableRef}( + component, + add_jump_parameter(get_jump_model(container), val), + ) end function _get_initial_conditions_value( @@ -160,8 +169,8 @@ function _get_initial_conditions_value( container::OptimizationContainer, ) where { T <: InitialCondition{U, JuMP.VariableRef}, - V <: Union{AbstractDeviceFormulation, AbstractServiceFormulation}, - W <: PSY.Component, + V <: AbstractDeviceFormulation, + W <: PSY.ThermalGen, } where {U <: InitialEnergyLevel} var_type = initial_condition_variable(U(), component, V()) val = initial_condition_default(U(), component, V()) @@ -178,7 +187,7 @@ function _get_initial_conditions_value( container::OptimizationContainer, ) where { T <: InitialCondition{U, Float64}, - V <: Union{AbstractDeviceFormulation, AbstractServiceFormulation}, + V <: AbstractDeviceFormulation, W <: PSY.Component, } where {U <: InitialEnergyLevel} var_type = initial_condition_variable(U(), component, V()) @@ -209,3 +218,35 @@ function add_initial_condition!( end return end + +function add_initial_condition!( + container::OptimizationContainer, + components::Union{Vector{T}, IS.FlattenIteratorWrapper{T}}, + ::U, + ::D, +) where { + T <: PSY.ThermalGen, + U <: AbstractThermalFormulation, + D <: InitialConditionType, +} + if get_rebuild_model(get_settings(container)) && has_container_key(container, D, T) + return + end + + ini_cond_vector = add_initial_condition_container!(container, D(), T, components) + for (ix, component) in enumerate(components) + if PSY.get_must_run(component) + ini_cond_vector[ix] = InitialCondition{D, Nothing}(component, nothing) + else + ini_cond_vector[ix] = + _get_initial_conditions_value( + ini_cond_vector, + component, + D(), + U(), + container, + ) + end + end + return +end diff --git a/src/operation/operation_model_interface.jl b/src/operation/operation_model_interface.jl index 704331b09a..e092cea01b 100644 --- a/src/operation/operation_model_interface.jl +++ b/src/operation/operation_model_interface.jl @@ -114,7 +114,10 @@ function solve_impl!(model::OperationModel) tss = replace("$(ts)", ":" => "_") model_export_path = joinpath(model_output_dir, "exported_$(model_name)_$(tss).json") serialize_optimization_model(container, model_export_path) - @debug write_lp_file(get_jump_model(container), replace(model_export_path, ".json" => ".lp")) + @debug write_lp_file( + get_jump_model(container), + replace(model_export_path, ".json" => ".lp"), + ) end status = solve_impl!(container, get_system(model)) diff --git a/test/test_device_thermal_generation_constructors.jl b/test/test_device_thermal_generation_constructors.jl index c6bedc670d..2e092a239c 100644 --- a/test/test_device_thermal_generation_constructors.jl +++ b/test/test_device_thermal_generation_constructors.jl @@ -885,24 +885,27 @@ end @testset "Test Must Run ThermalGen" begin sys_5 = build_system(PSITestSystems, "c_sys5_uc") template_uc = - ProblemTemplate(NetworkModel(PTDFPowerModel; PTDF_matrix = PTDF(sys_5))) + ProblemTemplate(NetworkModel(CopperPlatePowerModel)) set_device_model!(template_uc, ThermalStandard, ThermalStandardUnitCommitment) - set_device_model!(template_uc, RenewableDispatch, FixedOutput) + #set_device_model!(template_uc, RenewableDispatch, FixedOutput) set_device_model!(template_uc, PowerLoad, StaticPowerLoad) - set_device_model!(template_uc, DeviceModel(Line, StaticBranch)) + set_device_model!(template_uc, DeviceModel(Line, StaticBranchUnbounded)) # Set Must Run the most expensive one: Sundance sundance = get_component(ThermalStandard, sys_5, "Sundance") + set_status!(sundance, true) set_must_run!(sundance, true) model = DecisionModel( template_uc, sys_5; name = "UC", - optimizer = HiGHS_optimizer, + optimizer = Xpress.Optimizer, system_to_file = false, + store_variable_names = true, ) solve!(model; output_dir = mktempdir()) + serialize_optimization_model(model, "branch.json") ptdf_vars = get_variable_values(OptimizationProblemResults(model)) power = ptdf_vars[PowerSimulations.VariableKey{ActivePowerVariable, ThermalStandard}("")]