From 7004603040f6ca5ea80d4874a5f662e5d85215ef Mon Sep 17 00:00:00 2001 From: Jose Daniel Lara Date: Tue, 26 Mar 2024 18:07:36 -0700 Subject: [PATCH 01/69] add fix to service bid --- .../devices/common/objective_functions.jl | 155 ++++++++---------- 1 file changed, 68 insertions(+), 87 deletions(-) diff --git a/src/devices_models/devices/common/objective_functions.jl b/src/devices_models/devices/common/objective_functions.jl index d19cb00e4c..ac7e2f8bfe 100644 --- a/src/devices_models/devices/common/objective_functions.jl +++ b/src/devices_models/devices/common/objective_functions.jl @@ -167,7 +167,7 @@ function _add_variable_cost_to_objective!( set_parameter!( parameter_container, jump_model, - PSY.get_cost(variable_cost_forecast_values[t]), + PSY.get_raw_data(variable_cost_forecast_values[t]), component_name, t, ) @@ -201,7 +201,6 @@ function _add_variable_cost_to_objective!( time_steps = get_time_steps(container) variable_cost_forecast = get_time_series(container, component, "variable_cost") variable_cost_forecast_values = TimeSeries.values(variable_cost_forecast) - variable_cost_forecast_values = map(PSY.VariableCost, variable_cost_forecast_values) parameter_container = _get_cost_function_parameter_container( container, CostFunctionParameter(), @@ -225,7 +224,7 @@ function _add_variable_cost_to_objective!( set_parameter!( parameter_container, jump_model, - PSY.get_cost(variable_cost_forecast_values[t]), + PSY.get_raw_data(variable_cost_forecast_values[t]), component_name, t, ) @@ -286,15 +285,13 @@ function _add_start_up_cost_to_objective!( return end -_get_cost_function_data_type(::Type{PSY.VariableCost{T}}) where {T} = T - function _get_cost_function_parameter_container( container::OptimizationContainer, ::S, component::T, ::U, ::V, - cost_type::DataType, + cost_type::Type{<:PSY.FunctionData}, ) where { S <: ObjectiveFunctionParameter, T <: PSY.Component, @@ -317,7 +314,7 @@ function _get_cost_function_parameter_container( U, sos_val, uses_compact_power(component, V()), - _get_cost_function_data_type(cost_type), + PSY.get_raw_data_type(cost_type), container_axes..., ) end @@ -326,8 +323,8 @@ end function _add_service_bid_cost!( container::OptimizationContainer, component::PSY.Component, - service::PSY.Reserve{T}, -) where {T <: PSY.ReserveDirection} + service::T, +) where {T <: PSY.Reserve{<:PSY.ReserveDirection}} time_steps = get_time_steps(container) initial_time = get_initial_time(container) base_power = get_base_power(container) @@ -343,13 +340,14 @@ function _add_service_bid_cost!( if eltype(forecast_data_values) == Float64 data_values = forecast_data_values # Single Price/Quantity Bid - elseif eltype(forecast_data_values) == NTuple{2, Float64} - data_values = [v[1] for v in forecast_data_values] + elseif eltype(forecast_data_values) == Vector{NTuple{2, Float64}} + data_values = [v[1][1] for v in forecast_data_values] else error("$(eltype(forecast_data_values)) not supported for MarketBidCost") end - reserve_variable = get_variable(container, U(), T, PSY.get_name(service)) + reserve_variable = + get_variable(container, ActivePowerReserveVariable(), T, PSY.get_name(service)) component_name = PSY.get_name(component) for t in time_steps add_to_objective_invariant_expression!( @@ -383,18 +381,18 @@ Adds to the cost function cost terms for sum of variables with common factor to - container::OptimizationContainer : the optimization_container model built in PowerSimulations - var_key::VariableKey: The variable name - component_name::String: The component_name of the variable container - - cost_component::PSY.VariableCost{Float64} : container for cost to be associated with variable + - cost_component::PSY.LinearFunctionData : container for cost to be associated with variable """ function _add_variable_cost_to_objective!( container::OptimizationContainer, ::T, component::PSY.Component, - cost_component::PSY.VariableCost{Float64}, + cost_component::PSY.LinearFunctionData, ::U, ) where {T <: VariableType, U <: AbstractDeviceFormulation} multiplier = objective_function_multiplier(T(), U()) base_power = get_base_power(container) - cost_data = PSY.get_cost(cost_component) + cost_data = PSY.get_proportional_term(cost_component) resolution = get_resolution(container) dt = Dates.value(Dates.Second(resolution)) / SECONDS_IN_HOUR for time_period in get_time_steps(container) @@ -435,27 +433,31 @@ linear cost term `sum(variable)*cost_data[2]` * container::OptimizationContainer : the optimization_container model built in PowerSimulations * var_key::VariableKey: The variable name * component_name::String: The component_name of the variable container -* cost_component::PSY.VariableCost{NTuple{2, Float64}} : container for quadratic and linear factors +* cost_component::PSY.QuadraticFunctionData : container for quadratic factors """ function _add_variable_cost_to_objective!( container::OptimizationContainer, ::T, component::PSY.Component, - cost_component::PSY.VariableCost{NTuple{2, Float64}}, + cost_component::PSY.QuadraticFunctionData, ::U, ) where {T <: VariableType, U <: AbstractDeviceFormulation} multiplier = objective_function_multiplier(T(), U()) base_power = get_base_power(container) - cost_data = PSY.get_cost(cost_component) + quadratic_term = PSY.get_quadratic_term(cost_component) + proportional_term = PSY.get_proportional_term(cost_component) + constant_term = PSY.get_constant_term(cost_component) + (constant_term == 0) || + throw(ArgumentError("Not yet implemented for nonzero constant term")) resolution = get_resolution(container) dt = Dates.value(Dates.Second(resolution)) / SECONDS_IN_HOUR for time_period in get_time_steps(container) - if cost_data[1] >= eps() + if quadratic_term >= eps() cost_term = _add_quadratic_term!( container, T(), component, - cost_data, + (quadratic_term, proportional_term), base_power, multiplier * dt, time_period, @@ -465,7 +467,7 @@ function _add_variable_cost_to_objective!( container, T(), component, - cost_data[2] * multiplier * base_power * dt, + proportional_term * multiplier * base_power * dt, time_period, ) end @@ -488,25 +490,24 @@ Creates piecewise linear cost function using a sum of variables and expression w - container::OptimizationContainer : the optimization_container model built in PowerSimulations - var_key::VariableKey: The variable name - component_name::String: The component_name of the variable container - - cost_component::PSY.VariableCost{Vector{NTuple{2, Float64}}} + - cost_component::PSY.PiecewiseLinearPointData: container for piecewise linear cost """ function _add_variable_cost_to_objective!( container::OptimizationContainer, ::T, component::PSY.Component, - cost_component::PSY.VariableCost{Vector{NTuple{2, Float64}}}, + cost_component::PSY.PiecewiseLinearPointData, ::U, ) where {T <: VariableType, U <: AbstractDeviceFormulation} component_name = PSY.get_name(component) @debug "PWL Variable Cost" _group = LOG_GROUP_COST_FUNCTIONS component_name # If array is full of tuples with zeros return 0.0 - cost_data = PSY.get_cost(cost_component) - if all(iszero.(last.(cost_data))) + if all(iszero.((point -> point.y).(PSY.get_points(cost_component)))) # TODO I think this should have been first. before? @debug "All cost terms for component $(component_name) are 0.0" _group = LOG_GROUP_COST_FUNCTIONS return end - pwl_cost_expressions = _add_pwl_term!(container, component, cost_data, T(), U()) + pwl_cost_expressions = _add_pwl_term!(container, component, cost_component, T(), U()) for t in get_time_steps(container) add_to_expression!( container, @@ -520,20 +521,6 @@ function _add_variable_cost_to_objective!( return end -""" -Returns True/False depending on compatibility of the cost data with the convex implementation method -""" -function _slope_convexity_check(slopes::Vector{Float64}) - flag = true - for ix in 1:(length(slopes) - 1) - if slopes[ix] > slopes[ix + 1] - @debug slopes _group = LOG_GROUP_COST_FUNCTIONS - return flag = false - end - end - return flag -end - function _get_sos_value( container::OptimizationContainer, ::Type{V}, @@ -558,7 +545,7 @@ end function _add_pwl_term!( container::OptimizationContainer, component::T, - cost_data::Vector{PSY.VariableCost{Float64}}, + cost_data::AbstractVector{PSY.LinearFunctionData}, ::U, ::V, ) where {T <: PSY.Component, U <: VariableType, V <: AbstractDeviceFormulation} @@ -570,9 +557,10 @@ function _add_pwl_term!( time_steps = get_time_steps(container) cost_expressions = Vector{JuMP.AffExpr}(undef, time_steps[end]) for t in time_steps - proportial_value = PSY.get_cost(cost_data[t]) * multiplier * base_power * dt + proportional_value = + PSY.get_proportional_term(cost_data[t]) * multiplier * base_power * dt cost_expressions[t] = - _add_proportional_term!(container, U(), component, proportial_value, t) + _add_proportional_term!(container, U(), component, proportional_value, t) end return cost_expressions end @@ -583,7 +571,7 @@ Add PWL cost terms for data coming from the MarketBidCost function _add_pwl_term!( container::OptimizationContainer, component::T, - cost_data::Vector{PSY.VariableCost{Vector{Tuple{Float64, Float64}}}}, + cost_data::AbstractVector{PSY.PiecewiseLinearPointData}, ::U, ::V, ) where {T <: PSY.Component, U <: VariableType, V <: AbstractDeviceFormulation} @@ -598,7 +586,7 @@ function _add_pwl_term!( sos_val = _get_sos_value(container, V, component) for t in time_steps # Run checks in every time step because each time step has a PWL cost function - data = PSY.get_cost(cost_data[t]) + data = cost_data[t] compact_status = validate_compact_pwl_data(component, data, base_power) if !uses_compact_power(component, V()) && compact_status == COMPACT_PWL_STATUS.VALID error( @@ -608,20 +596,17 @@ function _add_pwl_term!( elseif uses_compact_power(component, V()) && compact_status != COMPACT_PWL_STATUS.VALID @warn( - "The cost data provided is not in compact form. Will atempt to convert. Errors may occur." + "The cost data provided is not in compact form. Will attempt to convert. Errors may occur." ) data = _convert_to_compact_variable_cost(data) else @debug uses_compact_power(component, V()) compact_status name T V end - slopes = PSY.get_slopes(data) - # First element of the return is the average cost at P_min. - # Shouldn't be passed for convexity check - is_convex = _slope_convexity_check(slopes[2:end]) - break_points = map(x -> last(x), data) ./ base_power + cost_is_convex = PSY.is_convex(data) + break_points = PSY.get_x_coords(data) ./ base_power # TODO should this be get_x_lengths? _add_pwl_variables!(container, T, name, t, data) _add_pwl_constraint!(container, component, U(), break_points, sos_val, t) - if !is_convex + if !cost_is_convex _add_pwl_sos_constraint!(container, component, U(), break_points, sos_val, t) end pwl_cost = _get_pwl_cost_expression(container, component, t, data, multiplier * dt) @@ -633,7 +618,7 @@ end function _add_pwl_term!( container::OptimizationContainer, component::T, - cost_data::Vector{PSY.VariableCost{Vector{Tuple{Float64, Float64}}}}, + cost_data::AbstractVector{PSY.PiecewiseLinearPointData}, ::U, ::V, ) where {T <: PSY.Component, U <: VariableType, V <: AbstractServiceFormulation} @@ -647,15 +632,11 @@ function _add_pwl_term!( pwl_cost_expressions = Vector{JuMP.AffExpr}(undef, time_steps[end]) sos_val = _get_sos_value(container, V, component) for t in time_steps - data = PSY.get_cost(cost_data[t]) - # Shouldn't be passed for convexity check - is_convex = false - break_points = map(x -> last(x), data) ./ base_power + data = cost_data[t] + break_points = PSY.get_x_coords(data) ./ base_power _add_pwl_variables!(container, T, name, t, data) _add_pwl_constraint!(container, component, U(), break_points, sos_val, t) - if !is_convex - _add_pwl_sos_constraint!(container, component, U(), break_points, sos_val, t) - end + _add_pwl_sos_constraint!(container, component, U(), break_points, sos_val, t) pwl_cost = _get_pwl_cost_expression(container, component, t, data, multiplier * dt) pwl_cost_expressions[t] = pwl_cost end @@ -668,7 +649,7 @@ Add PWL cost terms for data coming from a constant PWL cost function function _add_pwl_term!( container::OptimizationContainer, component::T, - data::Vector{NTuple{2, Float64}}, + data::PSY.PiecewiseLinearPointData, ::U, ::V, ) where {T <: PSY.Component, U <: VariableType, V <: AbstractDeviceFormulation} @@ -687,25 +668,22 @@ function _add_pwl_term!( # data = _convert_to_full_variable_cost(data, component) elseif uses_compact_power(component, V()) && compact_status != COMPACT_PWL_STATUS.VALID @warn( - "The cost data provided is not in compact form. Will atempt to convert. Errors may occur." + "The cost data provided is not in compact form. Will attempt to convert. Errors may occur." ) data = _convert_to_compact_variable_cost(data) else @debug uses_compact_power(component, V()) compact_status name T V end - slopes = PSY.get_slopes(data) - # First element of the return is the average cost at P_min. - # Shouldn't be passed for convexity check - is_convex = _slope_convexity_check(slopes[2:end]) + cost_is_convex = PSY.is_convex(data) + break_points = PSY.get_x_coords(data) ./ base_power time_steps = get_time_steps(container) pwl_cost_expressions = Vector{JuMP.AffExpr}(undef, time_steps[end]) - break_points = map(x -> last(x), data) ./ base_power sos_val = _get_sos_value(container, V, component) for t in time_steps _add_pwl_variables!(container, T, name, t, data) _add_pwl_constraint!(container, component, U(), break_points, sos_val, t) - if !is_convex + if !cost_is_convex _add_pwl_sos_constraint!(container, component, U(), break_points, sos_val, t) end pwl_cost = _get_pwl_cost_expression(container, component, t, data, multiplier * dt) @@ -717,7 +695,7 @@ end function _add_pwl_term!( container::OptimizationContainer, component::T, - data::Vector{NTuple{2, Float64}}, + data::PSY.PiecewiseLinearPointData, ::U, ::V, ) where {T <: PSY.ThermalGen, U <: VariableType, V <: ThermalDispatchNoMin} @@ -728,7 +706,7 @@ function _add_pwl_term!( @debug "PWL cost function detected for device $(component_name) using $V" base_power = get_base_power(container) slopes = PSY.get_slopes(data) - if any(slopes .< 0) || !_slope_convexity_check(slopes[2:end]) + if any(slopes .< 0) || !PSY.is_convex(data) throw( IS.InvalidValue( "The PWL cost data provided for generator $(component_name) is not compatible with $U.", @@ -743,16 +721,15 @@ function _add_pwl_term!( if slopes[1] != 0.0 @debug "PWL has no 0.0 intercept for generator $(component_name)" - # adds a first intercept a x = 0.0 and Y below the intercept of the first tuple to make convex equivalent - first_pair = data[1] - intercept_point = (0.0, first_pair[2] - COST_EPSILON) - data = vcat(intercept_point, data) - @assert _slope_convexity_check(slopes) + # adds a first intercept a x = 0.0 and y below the intercept of the first tuple to make convex equivalent + intercept_point = (x = 0.0, y = first(data).y - COST_EPSILON) + data = PSY.PiecewiseLinearPointData(vcat(intercept_point, get_points(data))) + @assert PSY.is_convex(slopes) end time_steps = get_time_steps(container) pwl_cost_expressions = Vector{JuMP.AffExpr}(undef, time_steps[end]) - break_points = map(x -> last(x), data) ./ base_power + break_points = PSY.get_x_coords(data) ./ base_power sos_val = _get_sos_value(container, V, component) for t in time_steps _add_pwl_variables!(container, T, component_name, t, data) @@ -768,11 +745,12 @@ function _add_pwl_variables!( ::Type{T}, component_name::String, time_period::Int, - cost_data::Vector{NTuple{2, Float64}}, + cost_data::PSY.PiecewiseLinearPointData, ) where {T <: PSY.Component} var_container = lazy_container_addition!(container, PieceWiseLinearCostVariable(), T) - pwlvars = Array{JuMP.VariableRef}(undef, length(cost_data)) - for i in 1:length(cost_data) + # length(PiecewiseLinearPointData) gets number of segments, here we want number of points + pwlvars = Array{JuMP.VariableRef}(undef, length(cost_data) + 1) + for i in 1:(length(cost_data) + 1) pwlvars[i] = var_container[(component_name, i, time_period)] = JuMP.@variable( get_jump_model(container), @@ -861,16 +839,17 @@ function _get_pwl_cost_expression( container::OptimizationContainer, component::T, time_period::Int, - cost_data::Vector{NTuple{2, Float64}}, + cost_data::PSY.PiecewiseLinearPointData, multiplier::Float64, ) where {T <: PSY.Component} name = PSY.get_name(component) pwl_var_container = get_variable(container, PieceWiseLinearCostVariable(), T) gen_cost = JuMP.AffExpr(0.0) + cost_data = PSY.get_points(cost_data) for i in 1:length(cost_data) JuMP.add_to_expression!( gen_cost, - cost_data[i][1] * multiplier * pwl_var_container[(name, i, time_period)], + cost_data[i].y * multiplier * pwl_var_container[(name, i, time_period)], ) end return gen_cost @@ -885,16 +864,18 @@ function _get_no_load_cost( end function _convert_to_compact_variable_cost( - var_cost::Vector{NTuple{2, Float64}}, - no_load_cost::Float64, + var_cost::PSY.PiecewiseLinearPointData, p_min::Float64, + no_load_cost::Float64, ) - return [(c - no_load_cost, pp - p_min) for (c, pp) in var_cost] + points = PSY.get_points(var_cost) + new_points = [(pp - p_min, c - no_load_cost) for (pp, c) in points] + return PSY.PiecewiseLinearPointData(new_points) end -function _convert_to_compact_variable_cost(var_cost::Vector{NTuple{2, Float64}}) - no_load_cost, p_min = var_cost[1] - return _convert_to_compact_variable_cost(var_cost, no_load_cost, p_min) +function _convert_to_compact_variable_cost(var_cost::PSY.PiecewiseLinearPointData) + p_min, no_load_cost = first(PSY.get_points(var_cost)) + return _convert_to_compact_variable_cost(var_cost, p_min, no_load_cost) end function _add_proportional_term!( From 36ef59bac2bf579274f90ac552b6b314dd29c5f6 Mon Sep 17 00:00:00 2001 From: Jose Daniel Lara Date: Tue, 26 Mar 2024 18:10:54 -0700 Subject: [PATCH 02/69] undo changes --- .../devices/common/objective_functions.jl | 144 ++++++++++-------- 1 file changed, 82 insertions(+), 62 deletions(-) diff --git a/src/devices_models/devices/common/objective_functions.jl b/src/devices_models/devices/common/objective_functions.jl index ac7e2f8bfe..57bceb1156 100644 --- a/src/devices_models/devices/common/objective_functions.jl +++ b/src/devices_models/devices/common/objective_functions.jl @@ -167,7 +167,7 @@ function _add_variable_cost_to_objective!( set_parameter!( parameter_container, jump_model, - PSY.get_raw_data(variable_cost_forecast_values[t]), + PSY.get_cost(variable_cost_forecast_values[t]), component_name, t, ) @@ -201,6 +201,7 @@ function _add_variable_cost_to_objective!( time_steps = get_time_steps(container) variable_cost_forecast = get_time_series(container, component, "variable_cost") variable_cost_forecast_values = TimeSeries.values(variable_cost_forecast) + variable_cost_forecast_values = map(PSY.VariableCost, variable_cost_forecast_values) parameter_container = _get_cost_function_parameter_container( container, CostFunctionParameter(), @@ -224,7 +225,7 @@ function _add_variable_cost_to_objective!( set_parameter!( parameter_container, jump_model, - PSY.get_raw_data(variable_cost_forecast_values[t]), + PSY.get_cost(variable_cost_forecast_values[t]), component_name, t, ) @@ -285,13 +286,15 @@ function _add_start_up_cost_to_objective!( return end +_get_cost_function_data_type(::Type{PSY.VariableCost{T}}) where {T} = T + function _get_cost_function_parameter_container( container::OptimizationContainer, ::S, component::T, ::U, ::V, - cost_type::Type{<:PSY.FunctionData}, + cost_type::DataType, ) where { S <: ObjectiveFunctionParameter, T <: PSY.Component, @@ -314,7 +317,7 @@ function _get_cost_function_parameter_container( U, sos_val, uses_compact_power(component, V()), - PSY.get_raw_data_type(cost_type), + _get_cost_function_data_type(cost_type), container_axes..., ) end @@ -381,18 +384,18 @@ Adds to the cost function cost terms for sum of variables with common factor to - container::OptimizationContainer : the optimization_container model built in PowerSimulations - var_key::VariableKey: The variable name - component_name::String: The component_name of the variable container - - cost_component::PSY.LinearFunctionData : container for cost to be associated with variable + - cost_component::PSY.VariableCost{Float64} : container for cost to be associated with variable """ function _add_variable_cost_to_objective!( container::OptimizationContainer, ::T, component::PSY.Component, - cost_component::PSY.LinearFunctionData, + cost_component::PSY.VariableCost{Float64}, ::U, ) where {T <: VariableType, U <: AbstractDeviceFormulation} multiplier = objective_function_multiplier(T(), U()) base_power = get_base_power(container) - cost_data = PSY.get_proportional_term(cost_component) + cost_data = PSY.get_cost(cost_component) resolution = get_resolution(container) dt = Dates.value(Dates.Second(resolution)) / SECONDS_IN_HOUR for time_period in get_time_steps(container) @@ -433,31 +436,27 @@ linear cost term `sum(variable)*cost_data[2]` * container::OptimizationContainer : the optimization_container model built in PowerSimulations * var_key::VariableKey: The variable name * component_name::String: The component_name of the variable container -* cost_component::PSY.QuadraticFunctionData : container for quadratic factors +* cost_component::PSY.VariableCost{NTuple{2, Float64}} : container for quadratic and linear factors """ function _add_variable_cost_to_objective!( container::OptimizationContainer, ::T, component::PSY.Component, - cost_component::PSY.QuadraticFunctionData, + cost_component::PSY.VariableCost{NTuple{2, Float64}}, ::U, ) where {T <: VariableType, U <: AbstractDeviceFormulation} multiplier = objective_function_multiplier(T(), U()) base_power = get_base_power(container) - quadratic_term = PSY.get_quadratic_term(cost_component) - proportional_term = PSY.get_proportional_term(cost_component) - constant_term = PSY.get_constant_term(cost_component) - (constant_term == 0) || - throw(ArgumentError("Not yet implemented for nonzero constant term")) + cost_data = PSY.get_cost(cost_component) resolution = get_resolution(container) dt = Dates.value(Dates.Second(resolution)) / SECONDS_IN_HOUR for time_period in get_time_steps(container) - if quadratic_term >= eps() + if cost_data[1] >= eps() cost_term = _add_quadratic_term!( container, T(), component, - (quadratic_term, proportional_term), + cost_data, base_power, multiplier * dt, time_period, @@ -467,7 +466,7 @@ function _add_variable_cost_to_objective!( container, T(), component, - proportional_term * multiplier * base_power * dt, + cost_data[2] * multiplier * base_power * dt, time_period, ) end @@ -490,24 +489,25 @@ Creates piecewise linear cost function using a sum of variables and expression w - container::OptimizationContainer : the optimization_container model built in PowerSimulations - var_key::VariableKey: The variable name - component_name::String: The component_name of the variable container - - cost_component::PSY.PiecewiseLinearPointData: container for piecewise linear cost + - cost_component::PSY.VariableCost{Vector{NTuple{2, Float64}}} """ function _add_variable_cost_to_objective!( container::OptimizationContainer, ::T, component::PSY.Component, - cost_component::PSY.PiecewiseLinearPointData, + cost_component::PSY.VariableCost{Vector{NTuple{2, Float64}}}, ::U, ) where {T <: VariableType, U <: AbstractDeviceFormulation} component_name = PSY.get_name(component) @debug "PWL Variable Cost" _group = LOG_GROUP_COST_FUNCTIONS component_name # If array is full of tuples with zeros return 0.0 - if all(iszero.((point -> point.y).(PSY.get_points(cost_component)))) # TODO I think this should have been first. before? + cost_data = PSY.get_cost(cost_component) + if all(iszero.(last.(cost_data))) @debug "All cost terms for component $(component_name) are 0.0" _group = LOG_GROUP_COST_FUNCTIONS return end - pwl_cost_expressions = _add_pwl_term!(container, component, cost_component, T(), U()) + pwl_cost_expressions = _add_pwl_term!(container, component, cost_data, T(), U()) for t in get_time_steps(container) add_to_expression!( container, @@ -521,6 +521,20 @@ function _add_variable_cost_to_objective!( return end +""" +Returns True/False depending on compatibility of the cost data with the convex implementation method +""" +function _slope_convexity_check(slopes::Vector{Float64}) + flag = true + for ix in 1:(length(slopes) - 1) + if slopes[ix] > slopes[ix + 1] + @debug slopes _group = LOG_GROUP_COST_FUNCTIONS + return flag = false + end + end + return flag +end + function _get_sos_value( container::OptimizationContainer, ::Type{V}, @@ -545,7 +559,7 @@ end function _add_pwl_term!( container::OptimizationContainer, component::T, - cost_data::AbstractVector{PSY.LinearFunctionData}, + cost_data::Vector{PSY.VariableCost{Float64}}, ::U, ::V, ) where {T <: PSY.Component, U <: VariableType, V <: AbstractDeviceFormulation} @@ -557,10 +571,9 @@ function _add_pwl_term!( time_steps = get_time_steps(container) cost_expressions = Vector{JuMP.AffExpr}(undef, time_steps[end]) for t in time_steps - proportional_value = - PSY.get_proportional_term(cost_data[t]) * multiplier * base_power * dt + proportial_value = PSY.get_cost(cost_data[t]) * multiplier * base_power * dt cost_expressions[t] = - _add_proportional_term!(container, U(), component, proportional_value, t) + _add_proportional_term!(container, U(), component, proportial_value, t) end return cost_expressions end @@ -571,7 +584,7 @@ Add PWL cost terms for data coming from the MarketBidCost function _add_pwl_term!( container::OptimizationContainer, component::T, - cost_data::AbstractVector{PSY.PiecewiseLinearPointData}, + cost_data::Vector{PSY.VariableCost{Vector{Tuple{Float64, Float64}}}}, ::U, ::V, ) where {T <: PSY.Component, U <: VariableType, V <: AbstractDeviceFormulation} @@ -586,7 +599,7 @@ function _add_pwl_term!( sos_val = _get_sos_value(container, V, component) for t in time_steps # Run checks in every time step because each time step has a PWL cost function - data = cost_data[t] + data = PSY.get_cost(cost_data[t]) compact_status = validate_compact_pwl_data(component, data, base_power) if !uses_compact_power(component, V()) && compact_status == COMPACT_PWL_STATUS.VALID error( @@ -596,17 +609,20 @@ function _add_pwl_term!( elseif uses_compact_power(component, V()) && compact_status != COMPACT_PWL_STATUS.VALID @warn( - "The cost data provided is not in compact form. Will attempt to convert. Errors may occur." + "The cost data provided is not in compact form. Will atempt to convert. Errors may occur." ) data = _convert_to_compact_variable_cost(data) else @debug uses_compact_power(component, V()) compact_status name T V end - cost_is_convex = PSY.is_convex(data) - break_points = PSY.get_x_coords(data) ./ base_power # TODO should this be get_x_lengths? + slopes = PSY.get_slopes(data) + # First element of the return is the average cost at P_min. + # Shouldn't be passed for convexity check + is_convex = _slope_convexity_check(slopes[2:end]) + break_points = map(x -> last(x), data) ./ base_power _add_pwl_variables!(container, T, name, t, data) _add_pwl_constraint!(container, component, U(), break_points, sos_val, t) - if !cost_is_convex + if !is_convex _add_pwl_sos_constraint!(container, component, U(), break_points, sos_val, t) end pwl_cost = _get_pwl_cost_expression(container, component, t, data, multiplier * dt) @@ -618,7 +634,7 @@ end function _add_pwl_term!( container::OptimizationContainer, component::T, - cost_data::AbstractVector{PSY.PiecewiseLinearPointData}, + cost_data::Vector{PSY.VariableCost{Vector{Tuple{Float64, Float64}}}}, ::U, ::V, ) where {T <: PSY.Component, U <: VariableType, V <: AbstractServiceFormulation} @@ -632,11 +648,15 @@ function _add_pwl_term!( pwl_cost_expressions = Vector{JuMP.AffExpr}(undef, time_steps[end]) sos_val = _get_sos_value(container, V, component) for t in time_steps - data = cost_data[t] - break_points = PSY.get_x_coords(data) ./ base_power + data = PSY.get_cost(cost_data[t]) + # Shouldn't be passed for convexity check + is_convex = false + break_points = map(x -> last(x), data) ./ base_power _add_pwl_variables!(container, T, name, t, data) _add_pwl_constraint!(container, component, U(), break_points, sos_val, t) - _add_pwl_sos_constraint!(container, component, U(), break_points, sos_val, t) + if !is_convex + _add_pwl_sos_constraint!(container, component, U(), break_points, sos_val, t) + end pwl_cost = _get_pwl_cost_expression(container, component, t, data, multiplier * dt) pwl_cost_expressions[t] = pwl_cost end @@ -649,7 +669,7 @@ Add PWL cost terms for data coming from a constant PWL cost function function _add_pwl_term!( container::OptimizationContainer, component::T, - data::PSY.PiecewiseLinearPointData, + data::Vector{NTuple{2, Float64}}, ::U, ::V, ) where {T <: PSY.Component, U <: VariableType, V <: AbstractDeviceFormulation} @@ -668,22 +688,25 @@ function _add_pwl_term!( # data = _convert_to_full_variable_cost(data, component) elseif uses_compact_power(component, V()) && compact_status != COMPACT_PWL_STATUS.VALID @warn( - "The cost data provided is not in compact form. Will attempt to convert. Errors may occur." + "The cost data provided is not in compact form. Will atempt to convert. Errors may occur." ) data = _convert_to_compact_variable_cost(data) else @debug uses_compact_power(component, V()) compact_status name T V end - cost_is_convex = PSY.is_convex(data) - break_points = PSY.get_x_coords(data) ./ base_power + slopes = PSY.get_slopes(data) + # First element of the return is the average cost at P_min. + # Shouldn't be passed for convexity check + is_convex = _slope_convexity_check(slopes[2:end]) time_steps = get_time_steps(container) pwl_cost_expressions = Vector{JuMP.AffExpr}(undef, time_steps[end]) + break_points = map(x -> last(x), data) ./ base_power sos_val = _get_sos_value(container, V, component) for t in time_steps _add_pwl_variables!(container, T, name, t, data) _add_pwl_constraint!(container, component, U(), break_points, sos_val, t) - if !cost_is_convex + if !is_convex _add_pwl_sos_constraint!(container, component, U(), break_points, sos_val, t) end pwl_cost = _get_pwl_cost_expression(container, component, t, data, multiplier * dt) @@ -695,7 +718,7 @@ end function _add_pwl_term!( container::OptimizationContainer, component::T, - data::PSY.PiecewiseLinearPointData, + data::Vector{NTuple{2, Float64}}, ::U, ::V, ) where {T <: PSY.ThermalGen, U <: VariableType, V <: ThermalDispatchNoMin} @@ -706,7 +729,7 @@ function _add_pwl_term!( @debug "PWL cost function detected for device $(component_name) using $V" base_power = get_base_power(container) slopes = PSY.get_slopes(data) - if any(slopes .< 0) || !PSY.is_convex(data) + if any(slopes .< 0) || !_slope_convexity_check(slopes[2:end]) throw( IS.InvalidValue( "The PWL cost data provided for generator $(component_name) is not compatible with $U.", @@ -721,15 +744,16 @@ function _add_pwl_term!( if slopes[1] != 0.0 @debug "PWL has no 0.0 intercept for generator $(component_name)" - # adds a first intercept a x = 0.0 and y below the intercept of the first tuple to make convex equivalent - intercept_point = (x = 0.0, y = first(data).y - COST_EPSILON) - data = PSY.PiecewiseLinearPointData(vcat(intercept_point, get_points(data))) - @assert PSY.is_convex(slopes) + # adds a first intercept a x = 0.0 and Y below the intercept of the first tuple to make convex equivalent + first_pair = data[1] + intercept_point = (0.0, first_pair[2] - COST_EPSILON) + data = vcat(intercept_point, data) + @assert _slope_convexity_check(slopes) end time_steps = get_time_steps(container) pwl_cost_expressions = Vector{JuMP.AffExpr}(undef, time_steps[end]) - break_points = PSY.get_x_coords(data) ./ base_power + break_points = map(x -> last(x), data) ./ base_power sos_val = _get_sos_value(container, V, component) for t in time_steps _add_pwl_variables!(container, T, component_name, t, data) @@ -745,12 +769,11 @@ function _add_pwl_variables!( ::Type{T}, component_name::String, time_period::Int, - cost_data::PSY.PiecewiseLinearPointData, + cost_data::Vector{NTuple{2, Float64}}, ) where {T <: PSY.Component} var_container = lazy_container_addition!(container, PieceWiseLinearCostVariable(), T) - # length(PiecewiseLinearPointData) gets number of segments, here we want number of points - pwlvars = Array{JuMP.VariableRef}(undef, length(cost_data) + 1) - for i in 1:(length(cost_data) + 1) + pwlvars = Array{JuMP.VariableRef}(undef, length(cost_data)) + for i in 1:length(cost_data) pwlvars[i] = var_container[(component_name, i, time_period)] = JuMP.@variable( get_jump_model(container), @@ -839,17 +862,16 @@ function _get_pwl_cost_expression( container::OptimizationContainer, component::T, time_period::Int, - cost_data::PSY.PiecewiseLinearPointData, + cost_data::Vector{NTuple{2, Float64}}, multiplier::Float64, ) where {T <: PSY.Component} name = PSY.get_name(component) pwl_var_container = get_variable(container, PieceWiseLinearCostVariable(), T) gen_cost = JuMP.AffExpr(0.0) - cost_data = PSY.get_points(cost_data) for i in 1:length(cost_data) JuMP.add_to_expression!( gen_cost, - cost_data[i].y * multiplier * pwl_var_container[(name, i, time_period)], + cost_data[i][1] * multiplier * pwl_var_container[(name, i, time_period)], ) end return gen_cost @@ -864,18 +886,16 @@ function _get_no_load_cost( end function _convert_to_compact_variable_cost( - var_cost::PSY.PiecewiseLinearPointData, - p_min::Float64, + var_cost::Vector{NTuple{2, Float64}}, no_load_cost::Float64, + p_min::Float64, ) - points = PSY.get_points(var_cost) - new_points = [(pp - p_min, c - no_load_cost) for (pp, c) in points] - return PSY.PiecewiseLinearPointData(new_points) + return [(c - no_load_cost, pp - p_min) for (c, pp) in var_cost] end -function _convert_to_compact_variable_cost(var_cost::PSY.PiecewiseLinearPointData) - p_min, no_load_cost = first(PSY.get_points(var_cost)) - return _convert_to_compact_variable_cost(var_cost, p_min, no_load_cost) +function _convert_to_compact_variable_cost(var_cost::Vector{NTuple{2, Float64}}) + no_load_cost, p_min = var_cost[1] + return _convert_to_compact_variable_cost(var_cost, no_load_cost, p_min) end function _add_proportional_term!( From 23343648da438365649a2f19a141778960062a0b Mon Sep 17 00:00:00 2001 From: Jose Daniel Lara Date: Tue, 26 Mar 2024 20:39:29 -0700 Subject: [PATCH 03/69] Update Project.toml --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 946764b9cd..a1a8e309ac 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "PowerSimulations" uuid = "e690365d-45e2-57bb-ac84-44ba829e73c4" authors = ["Jose Daniel Lara", "Clayton Barrows", "Daniel Thom", "Dheepak Krishnamurthy", "Sourabh Dalvi"] -version = "0.27.7" +version = "0.27.8" [deps] CSV = "336ed68f-0bac-5ca0-87d4-7b16caf5d00b" From 4c825586852b285b587fac9ee1f420a47a917371 Mon Sep 17 00:00:00 2001 From: rodrigomha Date: Mon, 22 Apr 2024 17:01:43 -0700 Subject: [PATCH 04/69] add Introduction for formulations --- docs/src/formulation_library/Introduction.md | 67 ++++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 docs/src/formulation_library/Introduction.md diff --git a/docs/src/formulation_library/Introduction.md b/docs/src/formulation_library/Introduction.md new file mode 100644 index 0000000000..a29c8d0008 --- /dev/null +++ b/docs/src/formulation_library/Introduction.md @@ -0,0 +1,67 @@ +# Formulations Introduction + +PowerSimulations.jl enables modularity in its formulations by assigning a `DeviceModel` to each `PowerSystems.jl` component type existing in a defined system. + +`PowerSimulations.jl` has a multiple `AbstractDeviceFormulation` subtypes that can be applied to different `PowerSystems.jl` device types, each dispatching to different methods for populating the optimization problem **variables**, **objective function**, **expressions** and **constraints**. + +## Example Formulation + +For example a typical optimization problem in a `DecisionModel` in `PowerSimulations.jl` with three `DeviceModel` has the abstract form of: + +```math +\begin{align*} + &\min_{\boldsymbol{x}}~ \text{Objective\_DeviceModelA} + \text{Objective\_DeviceModelB} + \text{Objective\_DeviceModelC} \\ + & ~~\text{s.t.} \\ + & \hspace{0.9cm} \text{Constraints\_NetworkModel} \\ + & \hspace{0.9cm} \text{Constraints\_DeviceModelA} \\ + & \hspace{0.9cm} \text{Constraints\_DeviceModelB} \\ + & \hspace{0.9cm} \text{Constraints\_DeviceModelC} +\end{align*} +``` + +Suppose this is a system with the following characteristics: +- Horizon: 48 hours +- Interval: 24 hours +- Resolution: 1 hour +- Three Buses: 1, 2 and 3 +- One `ThermalStandard` (device A) unit at bus 1 +- One `RenewableDispatch` (device B) unit at bus 2 +- One `PowerLoad` (device C) at bus 3 +- Three `Line` that connects all the buses + +Now, we assign the following `DeviceModel` to each `PowerSystems.jl` with: + +| Type | Formulation | +| ----------- | ----------- | +| Network | `CopperPlatePowerModel` | +| `ThermalStandard` | `ThermalDispatchNoMin` | +| `RenewableDispatch` | `RenewableFullDispatch` | +| `PowerLoad` | `StaticPowerLoad` | + +Note that we did not assigned any `DeviceModel` to `Line` since the `CopperPlatePowerModel` used for the network assumes that everything is lumped in the same node (like a copper plate with infinite capacity), and hence there are no flows between buses that branches can limit. + +Each `DeviceModel` formulation is described in specific in their respective page, but the overall optimization problem will end-up as: + +```math +\begin{align*} + &\min_{\boldsymbol{p}^\text{th}, \boldsymbol{p}^\text{re}}~ \sum_{t=1}^{48} C^\text{th} p_t^\text{th} - C^\text{re} p_t^\text{re} \\ + & ~~\text{s.t.} \\ + & \hspace{0.9cm} p_t^\text{th} + p_t^\text{re} = P_t^\text{load}, \quad \forall t \in {1,\dots, 48} \\ + & \hspace{0.9cm} 0 \le p_t^\text{th} \le P^\text{th,max} \\ + & \hspace{0.9cm} 0 \le p_t^\text{re} \le \text{ActivePowerTimeSeriesParameter}_t +\end{align*} +``` + +Note that the `StaticPowerLoad` does not impose any cost to the objective function or any constraint, but add its power demand to the supply-balance demand of the `CopperPlatePowerModel` used. Since we are using the `ThermalDispatchNoMin` formulation for the thermal generation, the lower bound for the power is 0, instead of ``P^\text{th,min}``. In addition, we are assuming a linear cost ``c^\text{th}``. Finally, the `RenewableFullDispatch` formulation allows the dispatch of the renewable unit to be between 0 and its maximum injection time series ``p_t^\text{re,param}``. + +# Nomenclature + +In the formulations described in the other pages, the nomenclature is as follows: +- Lowercase letters are used for variables, e.g., ``p`` for power. +- Uppercase letters are used for parameters, e.g., ``C`` for costs. +- Subscripts are used for indexing, e.g., ``(\cdot)_t`` for indexing at time ``t``. +- Superscripts are used for descriptions, e.g., ``(\cdot)^\text{th}`` to describe a thermal (th) variable/parameter. +- Bold letters are used for vectors, e.g., ``\boldsymbol{p} = \{p\}_{1,\dots,24}``. + + + From 37bc1a44e1e00e9bcff0cbc462b55961807af6e7 Mon Sep 17 00:00:00 2001 From: rodrigomha Date: Mon, 22 Apr 2024 17:01:52 -0700 Subject: [PATCH 05/69] update Network formulations --- docs/src/formulation_library/Network.md | 125 +++++++++++++++++++++++- 1 file changed, 124 insertions(+), 1 deletion(-) diff --git a/docs/src/formulation_library/Network.md b/docs/src/formulation_library/Network.md index e5f5e742e4..15f49c984e 100644 --- a/docs/src/formulation_library/Network.md +++ b/docs/src/formulation_library/Network.md @@ -1,3 +1,126 @@ # [Network Formulations](@id network_formulations) -TODO +Network formulations are used to describe how the network and buses are handled when constructing constraints. The most common constraint decided by the network formulation is the supply-demand balance constraint. Available Network Models are: + +| Formulation | Description | +| ----- | ---- | +| `CopperPlatePowerModel` | Copper plate connection between all components, i.e. infinite transmission capacity | +| `AreaBalancePowerModel` | Network model approximation to represent inter-area flow with each area represented as a single node | +| `PTDFPowerModel` | Uses the PTDF factor matrix to compute the fraction of power transferred in the network across the branches | + +[`PowerModels.jl`](https://github.com/lanl-ansi/PowerModels.jl) available formulations: +- Exact non-convex models: `ACPPowerModel`, `ACRPowerModel`, `ACTPowerModel`. +- Linear approximations: `DCPPowerModel`, `NFAPowerModel`. +- Quadratic approximations: `DCPLLPowerModel`, `LPACCPowerModel` +- Quadratic relaxations: `SOCWRPowerModel`, `SOCWRConicPowerModel`, `SOCBFPowerModel`, `SOCBFConicPowerModel`, `QCRMPowerModel`, `QCLSPowerModel`. +- SDP relaxations: `SDPWRMPowerModel`, `SparseSDPWRMPowerModel`. + +All of these formulations are described in the [PowerModels.jl documentation](https://lanl-ansi.github.io/PowerModels.jl/stable/formulation-details/) and will not be described here. + + +## `CopperPlatePowerModel` + +```@docs +CopperPlatePowerModel +``` + +**Variables:** + +If Slack variables are enabled: +- [`SystemBalanceSlackUp`](@ref): + - Bounds: [0.0, ] + - Default initial value: 0.0 + - Default proportional cost: 1e6 + - Symbol: ``p^\text{sl,up}`` +- [`SystemBalanceSlackDown`](@ref): + - Bounds: [0.0, ] + - Default initial value: 0.0 + - Default proportional cost: 1e6 + - Symbol: ``p^\text{sl,dn}`` + +**Objective:** + +Add a large proportional cost to the objective function if slack variables are used ``+ (p^\text{sl,up} + p^\text{sl,dn}) \cdot 10^6`` + +**Expressions:** + +Adds ``p^\text{sl,up}`` and ``p^\text{sl,dn}`` terms to the respective active power balance expressions `ActivePowerBalance` created by this `CopperPlatePowerModel` network formulation. + +**Constraints:** + +Adds the `CopperPlateBalanceConstraint` to balance the active power of all components available in the system + +```math +\begin{align} +& \sum_{c \in \text{components}} p_t^c = 0, \quad \forall t \in \{1, \dots, T\} +\end{align} +``` + +## `AreaBalancePowerModel` + +```@docs +AreaBalancePowerModel +``` + +**Variables:** + +Slack variables are not supported for `AreaBalancePowerModel` + +**Objective:** + +No changes to the objective function. + +**Expressions:** + +Creates `ActivePowerBalance` expressions for each bus that then are used to balance active power for all buses within a single area. + +**Constraints:** + +Adds the `AreaDispatchBalanceConstraint` to balance the active power of all components available in an area. + +```math +\begin{align} +& \sum_{c \in \text{components}_a} p_t^c = 0, \quad \forall a\in \{1,\dots, A\}, t \in \{1, \dots, T\} +\end{align} +``` + +## `PTDFPowerModel` + +```@docs +PTDFPowerModel +``` + +**Variables:** + +If Slack variables are enabled: +- [`SystemBalanceSlackUp`](@ref): + - Bounds: [0.0, ] + - Default initial value: 0.0 + - Default proportional cost: 1e6 + - Symbol: ``p^\text{sl,up}`` +- [`SystemBalanceSlackDown`](@ref): + - Bounds: [0.0, ] + - Default initial value: 0.0 + - Default proportional cost: 1e6 + - Symbol: ``p^\text{sl,dn}`` + +**Objective:** + +Add a large proportional cost to the objective function if slack variables are used ``+ (p^\text{sl,up} + p^\text{sl,dn}) \cdot 10^6`` + +**Expressions:** + +Adds ``p^\text{sl,up}`` and ``p^\text{sl,dn}`` terms to the respective active power balance expressions `ActivePowerBalance` created by this `CopperPlatePowerModel` network formulation. + +**Constraints:** + +Adds the `CopperPlateBalanceConstraint` to balance the active power of all components available in the system + +```math +\begin{align} +& \sum_{c \in \text{components}} p_t^c = 0, \quad \forall t \in \{1, \dots, T\} +\end{align} +``` + +In addition creates `NodalBalanceActiveConstraint` for HVDC buses balance, if DC components are connected to an HVDC network. + From badd6a74a341013b45c88e968c46b05a01b58d78 Mon Sep 17 00:00:00 2001 From: rodrigomha Date: Mon, 22 Apr 2024 17:02:01 -0700 Subject: [PATCH 06/69] update docstrings --- src/core/formulations.jl | 4 ++-- src/core/variables.jl | 22 ++++++++++++++++------ 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/src/core/formulations.jl b/src/core/formulations.jl index 2afdff9183..30942fa116 100644 --- a/src/core/formulations.jl +++ b/src/core/formulations.jl @@ -153,11 +153,11 @@ struct LossLessLine <: AbstractBranchFormulation end abstract type AbstractPTDFModel <: PM.AbstractDCPModel end """ -Linear active power approximation using the power transfer distribution factor ((PTDF)[https://nrel-sienna.github.io/PowerNetworkMatrices.jl/stable/tutorials/tutorial_PTDF_matrix/]) matrix. +Linear active power approximation using the power transfer distribution factor [PTDF](https://nrel-sienna.github.io/PowerNetworkMatrices.jl/stable/tutorials/tutorial_PTDF_matrix/) matrix. """ struct PTDFPowerModel <: AbstractPTDFModel end """ -Infinate capacity approximation of network flow to represent entire system with a single node. +Infinite capacity approximation of network flow to represent entire system with a single node. """ struct CopperPlatePowerModel <: PM.AbstractActivePowerModel end """ diff --git a/src/core/variables.jl b/src/core/variables.jl index ad47bb6169..a2da686677 100644 --- a/src/core/variables.jl +++ b/src/core/variables.jl @@ -34,28 +34,28 @@ get_component_type( """ Struct to dispatch the creation of Active Power Variables -Docs abbreviation: ``Pg`` +Docs abbreviation: ``p`` """ struct ActivePowerVariable <: VariableType end """ Struct to dispatch the creation of Active Power Variables above minimum power for Thermal Compact formulations -Docs abbreviation: ``\\hat{Pg}`` +Docs abbreviation: ``\\hat{p}`` """ struct PowerAboveMinimumVariable <: VariableType end """ Struct to dispatch the creation of Active Power Input Variables for 2-directional devices. For instance storage or pump-hydro -Docs abbreviation: ``Pg^{in}`` +Docs abbreviation: ``P^\text{in}`` """ struct ActivePowerInVariable <: VariableType end """ Struct to dispatch the creation of Active Power Output Variables for 2-directional devices. For instance storage or pump-hydro -Docs abbreviation: ``Pg^{out}`` +Docs abbreviation: ``P^\text{out}`` """ struct ActivePowerOutVariable <: VariableType end @@ -83,7 +83,7 @@ struct ColdStartVariable <: VariableType end """ Struct to dispatch the creation of a variable for energy storage level (state of charge) -Docs abbreviation: ``E`` +Docs abbreviation: ``e`` """ struct EnergyVariable <: VariableType end @@ -99,7 +99,7 @@ struct OnVariable <: VariableType end """ Struct to dispatch the creation of Reactive Power Variables -Docs abbreviation: ``Qg`` +Docs abbreviation: ``q`` """ struct ReactivePowerVariable <: VariableType end @@ -147,8 +147,18 @@ struct AdditionalDeltaActivePowerDownVariable <: VariableType end struct SmoothACE <: VariableType end +""" +Struct to dispatch the creation of System-wide slack up variables. Used when there is not enough generation. + +Docs abbreviation: ``p^\text{sl,up}`` +""" struct SystemBalanceSlackUp <: VariableType end +""" +Struct to dispatch the creation of System-wide slack down variables. Used when there is not enough load curtailment. + +Docs abbreviation: ``p^\text{sl,dn}`` +""" struct SystemBalanceSlackDown <: VariableType end struct ReserveRequirementSlack <: VariableType end From a6cb30b43bcb1c622fef11a761c51195dabc9915 Mon Sep 17 00:00:00 2001 From: rodrigomha Date: Mon, 22 Apr 2024 17:02:08 -0700 Subject: [PATCH 07/69] reorder make.jl --- docs/make.jl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/make.jl b/docs/make.jl index dda574be59..d12e2e7e69 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -31,11 +31,12 @@ pages = OrderedDict( "Troubleshooting" => "code_base_developer_guide/troubleshooting.md", ], "Formulation Library" => Any[ + "Introduction" => "formulation_library/Introduction.md", "General" => "formulation_library/General.md", + "Network" => "formulation_library/Network.md", "Thermal Generation" => "formulation_library/ThermalGen.md", "Renewable Generation" => "formulation_library/RenewableGen.md", "Load" => "formulation_library/Load.md", - "Network" => "formulation_library/Network.md", "Branch" => "formulation_library/Branch.md", ], "API Reference" => "api/PowerSimulations.md", From 2805166643f5df001e188cd9200029dff7b72732 Mon Sep 17 00:00:00 2001 From: rodrigomha Date: Thu, 25 Apr 2024 17:12:46 -0700 Subject: [PATCH 08/69] add three thermal formulations --- docs/src/formulation_library/Introduction.md | 2 +- docs/src/formulation_library/Service.md | 2 +- docs/src/formulation_library/ThermalGen.md | 159 ++++++++++++++++--- 3 files changed, 143 insertions(+), 20 deletions(-) diff --git a/docs/src/formulation_library/Introduction.md b/docs/src/formulation_library/Introduction.md index a29c8d0008..3ddfa205e6 100644 --- a/docs/src/formulation_library/Introduction.md +++ b/docs/src/formulation_library/Introduction.md @@ -38,7 +38,7 @@ Now, we assign the following `DeviceModel` to each `PowerSystems.jl` with: | `RenewableDispatch` | `RenewableFullDispatch` | | `PowerLoad` | `StaticPowerLoad` | -Note that we did not assigned any `DeviceModel` to `Line` since the `CopperPlatePowerModel` used for the network assumes that everything is lumped in the same node (like a copper plate with infinite capacity), and hence there are no flows between buses that branches can limit. +Note that we did not assign any `DeviceModel` to `Line` since the `CopperPlatePowerModel` used for the network assumes that everything is lumped in the same node (like a copper plate with infinite capacity), and hence there are no flows between buses that branches can limit. Each `DeviceModel` formulation is described in specific in their respective page, but the overall optimization problem will end-up as: diff --git a/docs/src/formulation_library/Service.md b/docs/src/formulation_library/Service.md index f4331eba63..b2b749754a 100644 --- a/docs/src/formulation_library/Service.md +++ b/docs/src/formulation_library/Service.md @@ -1,3 +1,3 @@ -# `PowerSystems.Service` Formulations +# [`PowerSystems.Service` Formulations][@id service_formulations] TODO diff --git a/docs/src/formulation_library/ThermalGen.md b/docs/src/formulation_library/ThermalGen.md index d80072ff2b..79b2f47261 100644 --- a/docs/src/formulation_library/ThermalGen.md +++ b/docs/src/formulation_library/ThermalGen.md @@ -1,21 +1,17 @@ # `ThermalGen` Formulations -Valid `DeviceModel`s for subtypes of `ThermalGen` include the following: +Thermal generation formulations define the optimization models that describe thermal unit operations in different operational settings, such as economic dispatch and unit commitment. -```@eval -using PowerSimulations -using PowerSystems -using DataFrames -using Latexify -combos = PowerSimulations.generate_device_formulation_combinations() -filter!(x -> x["device_type"] <: ThermalGen, combos) -combo_table = DataFrame( - "Valid DeviceModel" => ["`DeviceModel($(c["device_type"]), $(c["formulation"]))`" for c in combos], - "Device Type" => ["[$(c["device_type"])](https://nrel-Sienna.github.io/PowerSystems.jl/stable/model_library/generated_$(c["device_type"])/)" for c in combos], - "Formulation" => ["[$(c["formulation"])](@ref)" for c in combos], - ) -mdtable(combo_table, latex = false) -``` + +!!! note + Thermal units can include multiple terms added to the objective function, such as no-load cost, turn-on/off cost, fixed cost and variable cost. In addition, variable costs can be linear, quadratic or piecewise-linear formulations. These methods are properly described in the cost function document: TODO. + + +!!! note + The usage of reactive power variables and constraints will depend on the network model used, i.e. if it uses (or not) reactive power. If the network model is purely active power based, then no variables and constraints related to reactive power are created. For the sake of completion, if the formulation allows the usage of reactive power it will be included. + +!!! note + Reserve variables for services are not included in the formulation, albeit their inclusion change the variables, expressions, constraints and objective functions created. A detailed description of the implications in the optimization models is described in the [Service formulation](@ref service_formulation) section. --- @@ -24,8 +20,40 @@ mdtable(combo_table, latex = false) ```@docs ThermalBasicDispatch ``` +**Variables:** -TODO +- [`ActivePowerVariable`](@ref): + - Bounds: [0.0, ] + - Symbol: ``p^\text{th}`` +- [`ReactivePowerVariable`](@ref): + - Bounds: [0.0, ] + - Symbol: ``q^\text{th}`` + +**Static Parameters:** + +- ``P^\text{th,min}`` = `PowerSystems.get_active_power_limits(device).min` +- ``P^\text{th,max}`` = `PowerSystems.get_active_power_limits(device).max` +- ``Q^\text{th,min}`` = `PowerSystems.get_reactive_power_limits(device).min` +- ``Q^\text{th,max}`` = `PowerSystems.get_reactive_power_limits(device).max` + +**Objective:** + +Add a cost to the objective function depending on the defined cost structure of the thermal unit by adding it to its `ProductionCostExpression`. + +**Expressions:** + +Adds ``p^\text{th}`` to the `ActivePowerBalance` expression and ``q^\text{th}`` to the `ReactivePowerBalance`, to be used in the supply-balance constraint depending on the network model used. + +**Constraints:** + +For each thermal unit creates the range constraints for its active and reactive power depending on its static parameters. + +```math +\begin{align} +& P^\text{th,min} \le p^\text{th}_t \le P^\text{th,max}, \quad \forall t\in \{1, \dots, T\} \\ +& Q^\text{th,min} \le q^\text{th}_t \le Q^\text{th,max}, \quad \forall t\in \{1, \dots, T\} +\end{align} +``` --- @@ -35,7 +63,53 @@ TODO ThermalCompactDispatch ``` -TODO +**Variables:** + +- [`PowerAboveMinimumVariable`](@ref): + - Bounds: [0.0, ] + - Symbol: ``\Delta p^\text{th}`` +- [`ReactivePowerVariable`](@ref): + - Bounds: [0.0, ] + - Symbol: ``q^\text{th}`` + +**Auxiliary Variables:** +- [`PowerOutput`](@ref): + - Symbol: ``P^\text{th}`` + - Definition: ``P^\text{th} = \text{on}^\text{th}P^\text{min} + \Delta p^\text{th}`` + +**Static Parameters:** + +- ``P^\text{th,min}`` = `PowerSystems.get_active_power_limits(device).min` +- ``P^\text{th,max}`` = `PowerSystems.get_active_power_limits(device).max` +- ``Q^\text{th,min}`` = `PowerSystems.get_reactive_power_limits(device).min` +- ``Q^\text{th,max}`` = `PowerSystems.get_reactive_power_limits(device).max` +- ``R^\text{th,up}`` = `PowerSystems.get_ramp_limits(device).up` +- ``R^\text{th,dn}`` = `PowerSystems.get_ramp_limits(device).down` + +**Variable Value Parameters:** + +- ``\text{on}^\text{th}``: Used in feedforwards to define if the unit is on/off at each time-step from another problem. If no feedforward is used, the parameter takes a {0,1} value if the unit is available or not. + +**Objective:** + +Add a cost to the objective function depending on the defined cost structure of the thermal unit by adding it to its `ProductionCostExpression`. + +**Expressions:** + +Adds ``\text{on}^\text{th}P^\text{th,min} + \Delta p^\text{th}`` to the `ActivePowerBalance` expression and ``q^\text{th}`` to the `ReactivePowerBalance`, to be used in the supply-balance constraint depending on the network model used. + +**Constraints:** + +For each thermal unit creates the range constraints for its active and reactive power depending on its static parameters. It also implements ramp constraints for the active power variable. + +```math +\begin{align} +& 0 \le \Delta p^\text{th}_t \le \text{on}^\text{th}_t\left(P^\text{th,max} - P^\text{th,min}\right), \quad \forall t\in \{1, \dots, T\} \\ +& \text{on}^\text{th}_t Q^\text{th,min} \le q^\text{th}_t \le \text{on}^\text{th}_t Q^\text{th,max}, \quad \forall t\in \{1, \dots, T\} \\ +& -R^\text{th,dn} \le \Delta p_1^\text{th} - \Delta p^\text{th, init} \le R^\text{th,up} \\ +& -R^\text{th,dn} \le \Delta p_t^\text{th} - \Delta p_{t-1}^\text{th} \le R^\text{th,up}, \quad \forall t\in \{2, \dots, T\} +\end{align} +``` --- @@ -45,7 +119,39 @@ TODO ThermalDispatchNoMin ``` -TODO +**Variables:** + +- [`ActivePowerVariable`](@ref): + - Bounds: [0.0, ] + - Symbol: ``p^\text{th}`` +- [`ReactivePowerVariable`](@ref): + - Bounds: [0.0, ] + - Symbol: ``q^\text{th}`` + +**Static Parameters:** + +- ``P^\text{th,max}`` = `PowerSystems.get_active_power_limits(device).max` +- ``Q^\text{th,min}`` = `PowerSystems.get_reactive_power_limits(device).min` +- ``Q^\text{th,max}`` = `PowerSystems.get_reactive_power_limits(device).max` + +**Objective:** + +Add a cost to the objective function depending on the defined cost structure of the thermal unit by adding it to its `ProductionCostExpression`. + +**Expressions:** + +Adds ``p^\text{th}`` to the `ActivePowerBalance` expression and ``q^\text{th}`` to the `ReactivePowerBalance`, to be used in the supply-balance constraint depending on the network model used. + +**Constraints:** + +For each thermal unit creates the range constraints for its active and reactive power depending on its static parameters. + +```math +\begin{align} +& 0 \le p^\text{th}_t \le P^\text{th,max}, \quad \forall t\in \{1, \dots, T\} \\ +& Q^\text{th,min} \le q^\text{th}_t \le Q^\text{th,max}, \quad \forall t\in \{1, \dots, T\} +\end{align} +``` --- @@ -108,3 +214,20 @@ ThermalStandardUnitCommitment TODO --- + +Valid `DeviceModel`s for subtypes of `ThermalGen` include the following: + +```@eval +using PowerSimulations +using PowerSystems +using DataFrames +using Latexify +combos = PowerSimulations.generate_device_formulation_combinations() +filter!(x -> x["device_type"] <: ThermalGen, combos) +combo_table = DataFrame( + "Valid DeviceModel" => ["`DeviceModel($(c["device_type"]), $(c["formulation"]))`" for c in combos], + "Device Type" => ["[$(c["device_type"])](https://nrel-Sienna.github.io/PowerSystems.jl/stable/model_library/generated_$(c["device_type"])/)" for c in combos], + "Formulation" => ["[$(c["formulation"])](@ref)" for c in combos], + ) +mdtable(combo_table, latex = false) +``` From 1d5339cd2d59704d129245a645017338bbfc7a04 Mon Sep 17 00:00:00 2001 From: rodrigomha Date: Fri, 26 Apr 2024 15:19:12 -0700 Subject: [PATCH 09/69] update formulation typo --- src/core/formulations.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/formulations.jl b/src/core/formulations.jl index 30942fa116..1b37b7a371 100644 --- a/src/core/formulations.jl +++ b/src/core/formulations.jl @@ -33,7 +33,7 @@ Formulation type to enable standard dispatch with a range and enforce intertempo """ struct ThermalStandardDispatch <: AbstractThermalDispatchFormulation end """ -Formulation type to enable basic dispatch without any intertemporal constraints and relaxed minimum generation. *may not work with PWL cost definitions* +Formulation type to enable basic dispatch without any intertemporal constraints and relaxed minimum generation. *May not work with PWL cost definitions* """ struct ThermalDispatchNoMin <: AbstractThermalDispatchFormulation end """ From f294d93f9f907a65f09c493f10ac343356acab1e Mon Sep 17 00:00:00 2001 From: rodrigomha Date: Fri, 26 Apr 2024 15:19:23 -0700 Subject: [PATCH 10/69] update docstring --- src/core/variables.jl | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/core/variables.jl b/src/core/variables.jl index a2da686677..c48d6566ae 100644 --- a/src/core/variables.jl +++ b/src/core/variables.jl @@ -48,35 +48,35 @@ struct PowerAboveMinimumVariable <: VariableType end """ Struct to dispatch the creation of Active Power Input Variables for 2-directional devices. For instance storage or pump-hydro -Docs abbreviation: ``P^\text{in}`` +Docs abbreviation: ``p^\text{in}`` """ struct ActivePowerInVariable <: VariableType end """ Struct to dispatch the creation of Active Power Output Variables for 2-directional devices. For instance storage or pump-hydro -Docs abbreviation: ``P^\text{out}`` +Docs abbreviation: ``p^\text{out}`` """ struct ActivePowerOutVariable <: VariableType end """ Struct to dispatch the creation of Hot Start Variable for Thermal units with temperature considerations -Docs abbreviation: TODO +Docs abbreviation: ``z^\text{th}`` """ struct HotStartVariable <: VariableType end """ Struct to dispatch the creation of Warm Start Variable for Thermal units with temperature considerations -Docs abbreviation: TODO +Docs abbreviation: ``y^\text{th}`` """ struct WarmStartVariable <: VariableType end """ Struct to dispatch the creation of Cold Start Variable for Thermal units with temperature considerations -Docs abbreviation: TODO +Docs abbreviation: ``x^\text{th}`` """ struct ColdStartVariable <: VariableType end @@ -122,14 +122,14 @@ struct ServiceRequirementVariable <: VariableType end """ Struct to dispatch the creation of Binary Start Variables -Docs abbreviation: TODO +Docs abbreviation: ``v`` """ struct StartVariable <: VariableType end """ Struct to dispatch the creation of Binary Stop Variables -Docs abbreviation: TODO +Docs abbreviation: ``w`` """ struct StopVariable <: VariableType end From cdf7410fdc5143e7a69e0aee0256936f42c64f63 Mon Sep 17 00:00:00 2001 From: rodrigomha Date: Fri, 26 Apr 2024 15:19:35 -0700 Subject: [PATCH 11/69] add additional thermal formulations --- docs/src/formulation_library/ThermalGen.md | 480 +++++++++++++++++++-- 1 file changed, 454 insertions(+), 26 deletions(-) diff --git a/docs/src/formulation_library/ThermalGen.md b/docs/src/formulation_library/ThermalGen.md index 79b2f47261..e6625fdcb6 100644 --- a/docs/src/formulation_library/ThermalGen.md +++ b/docs/src/formulation_library/ThermalGen.md @@ -1,6 +1,6 @@ # `ThermalGen` Formulations -Thermal generation formulations define the optimization models that describe thermal unit operations in different operational settings, such as economic dispatch and unit commitment. +Thermal generation formulations define the optimization models that describe thermal units mathematical model in different operational settings, such as economic dispatch and unit commitment. !!! note @@ -49,9 +49,52 @@ Adds ``p^\text{th}`` to the `ActivePowerBalance` expression and ``q^\text{th}`` For each thermal unit creates the range constraints for its active and reactive power depending on its static parameters. ```math -\begin{align} +\begin{align*} & P^\text{th,min} \le p^\text{th}_t \le P^\text{th,max}, \quad \forall t\in \{1, \dots, T\} \\ & Q^\text{th,min} \le q^\text{th}_t \le Q^\text{th,max}, \quad \forall t\in \{1, \dots, T\} +\end{align*} +``` + +--- + + +## `ThermalDispatchNoMin` + +```@docs +ThermalDispatchNoMin +``` + +**Variables:** + +- [`ActivePowerVariable`](@ref): + - Bounds: [0.0, ] + - Symbol: ``p^\text{th}`` +- [`ReactivePowerVariable`](@ref): + - Bounds: [0.0, ] + - Symbol: ``q^\text{th}`` + +**Static Parameters:** + +- ``P^\text{th,max}`` = `PowerSystems.get_active_power_limits(device).max` +- ``Q^\text{th,min}`` = `PowerSystems.get_reactive_power_limits(device).min` +- ``Q^\text{th,max}`` = `PowerSystems.get_reactive_power_limits(device).max` + +**Objective:** + +Add a cost to the objective function depending on the defined cost structure of the thermal unit by adding it to its `ProductionCostExpression`. + +**Expressions:** + +Adds ``p^\text{th}`` to the `ActivePowerBalance` expression and ``q^\text{th}`` to the `ReactivePowerBalance`, to be used in the supply-balance constraint depending on the network model used. + +**Constraints:** + +For each thermal unit creates the range constraints for its active and reactive power depending on its static parameters. + +```math +\begin{align} +& 0 \le p^\text{th}_t \le P^\text{th,max}, \quad \forall t\in \{1, \dots, T\} \\ +& Q^\text{th,min} \le q^\text{th}_t \le Q^\text{th,max}, \quad \forall t\in \{1, \dots, T\} \end{align} ``` @@ -103,20 +146,21 @@ Adds ``\text{on}^\text{th}P^\text{th,min} + \Delta p^\text{th}`` to the `ActiveP For each thermal unit creates the range constraints for its active and reactive power depending on its static parameters. It also implements ramp constraints for the active power variable. ```math -\begin{align} +\begin{align*} & 0 \le \Delta p^\text{th}_t \le \text{on}^\text{th}_t\left(P^\text{th,max} - P^\text{th,min}\right), \quad \forall t\in \{1, \dots, T\} \\ & \text{on}^\text{th}_t Q^\text{th,min} \le q^\text{th}_t \le \text{on}^\text{th}_t Q^\text{th,max}, \quad \forall t\in \{1, \dots, T\} \\ & -R^\text{th,dn} \le \Delta p_1^\text{th} - \Delta p^\text{th, init} \le R^\text{th,up} \\ & -R^\text{th,dn} \le \Delta p_t^\text{th} - \Delta p_{t-1}^\text{th} \le R^\text{th,up}, \quad \forall t\in \{2, \dots, T\} -\end{align} +\end{align*} ``` --- -## `ThermalDispatchNoMin` + +## `ThermalStandardDispatch` ```@docs -ThermalDispatchNoMin +ThermalStandardDispatch ``` **Variables:** @@ -130,9 +174,12 @@ ThermalDispatchNoMin **Static Parameters:** +- ``P^\text{th,min}`` = `PowerSystems.get_active_power_limits(device).min` - ``P^\text{th,max}`` = `PowerSystems.get_active_power_limits(device).max` - ``Q^\text{th,min}`` = `PowerSystems.get_reactive_power_limits(device).min` - ``Q^\text{th,max}`` = `PowerSystems.get_reactive_power_limits(device).max` +- ``R^\text{th,up}`` = `PowerSystems.get_ramp_limits(device).up` +- ``R^\text{th,dn}`` = `PowerSystems.get_ramp_limits(device).down` **Objective:** @@ -147,21 +194,70 @@ Adds ``p^\text{th}`` to the `ActivePowerBalance` expression and ``q^\text{th}`` For each thermal unit creates the range constraints for its active and reactive power depending on its static parameters. ```math -\begin{align} -& 0 \le p^\text{th}_t \le P^\text{th,max}, \quad \forall t\in \{1, \dots, T\} \\ -& Q^\text{th,min} \le q^\text{th}_t \le Q^\text{th,max}, \quad \forall t\in \{1, \dots, T\} -\end{align} +\begin{align*} +& P^\text{th,min} \le p^\text{th}_t \le P^\text{th,max}, \quad \forall t\in \{1, \dots, T\} \\ +& Q^\text{th,min} \le q^\text{th}_t \le Q^\text{th,max}, \quad \forall t\in \{1, \dots, T\} \\ +& -R^\text{th,dn} \le p_1^\text{th} - p^\text{th, init} \le R^\text{th,up} \\ +& -R^\text{th,dn} \le p_t^\text{th} - p_{t-1}^\text{th} \le R^\text{th,up}, \quad \forall t\in \{2, \dots, T\} +\end{align*} ``` --- -## `ThermalStandardDispatch` +## `ThermalBasicUnitCommitment` ```@docs -ThermalStandardDispatch +ThermalBasicUnitCommitment ``` -TODO +**Variables:** + +- [`ActivePowerVariable`](@ref): + - Bounds: [0.0, ] + - Symbol: ``p^\text{th}`` +- [`ReactivePowerVariable`](@ref): + - Bounds: [0.0, ] + - Symbol: ``q^\text{th}`` +- [`OnVariable`](@ref): + - Bounds: ``\{0,1\}`` + - Symbol: ``u_t^\text{th}`` +- [`StartVariable`](@ref): + - Bounds: ``\{0,1\}`` + - Symbol: ``v_t^\text{th}`` +- [`StopVariable`](@ref): + - Bounds: ``\{0,1\}`` + - Symbol: ``w_t^\text{th}`` + + +**Static Parameters:** + +- ``P^\text{th,min}`` = `PowerSystems.get_active_power_limits(device).min` +- ``P^\text{th,max}`` = `PowerSystems.get_active_power_limits(device).max` +- ``Q^\text{th,min}`` = `PowerSystems.get_reactive_power_limits(device).min` +- ``Q^\text{th,max}`` = `PowerSystems.get_reactive_power_limits(device).max` + + +**Objective:** + +Add a cost to the objective function depending on the defined cost structure of the thermal unit by adding it to its `ProductionCostExpression`. + +**Expressions:** + +Adds ``p^\text{th}`` to the `ActivePowerBalance` expression and ``q^\text{th}`` to the `ReactivePowerBalance`, to be used in the supply-balance constraint depending on the network model used. + +**Constraints:** + +For each thermal unit creates the range constraints for its active and reactive power depending on its static parameters. In addition, it creates the commitment constraint to turn on/off the device. + +```math +\begin{align*} +& u_t^\text{th} P^\text{th,min} \le p^\text{th}_t \le u_t^\text{th} P^\text{th,max}, \quad \forall t\in \{1, \dots, T\} \\ +& u_t^\text{th} Q^\text{th,min} \le q^\text{th}_t \le u_t^\text{th} Q^\text{th,max}, \quad \forall t\in \{1, \dots, T\} \\ +& u_1^\text{th} = u^\text{th,init} + v_1^\text{th} - w_1^\text{th} \\ +& u_t^\text{th} = u_{t-1}^\text{th} + v_t^\text{th} - w_t^\text{th}, \quad \forall t \in \{2,\dots,T\} \\ +& v_t^\text{th} + w_t^\text{th} \le 1, \quad \forall t \in \{1,\dots,T\} +\end{align*} +``` --- @@ -171,7 +267,60 @@ TODO ThermalBasicCompactUnitCommitment ``` -TODO + +**Variables:** + +- [`PowerAboveMinimumVariable`](@ref): + - Bounds: [0.0, ] + - Symbol: ``\Delta p^\text{th}`` +- [`ReactivePowerVariable`](@ref): + - Bounds: [0.0, ] + - Symbol: ``q^\text{th}`` +- [`OnVariable`](@ref): + - Bounds: ``\{0,1\}`` + - Symbol: ``u_t^\text{th}`` +- [`StartVariable`](@ref): + - Bounds: ``\{0,1\}`` + - Symbol: ``v_t^\text{th}`` +- [`StopVariable`](@ref): + - Bounds: ``\{0,1\}`` + - Symbol: ``w_t^\text{th}`` + +**Auxiliary Variables:** +- [`PowerOutput`](@ref): + - Symbol: ``P^\text{th}`` + - Definition: ``P^\text{th} = u^\text{th}P^\text{min} + \Delta p^\text{th}`` + + +**Static Parameters:** + +- ``P^\text{th,min}`` = `PowerSystems.get_active_power_limits(device).min` +- ``P^\text{th,max}`` = `PowerSystems.get_active_power_limits(device).max` +- ``Q^\text{th,min}`` = `PowerSystems.get_reactive_power_limits(device).min` +- ``Q^\text{th,max}`` = `PowerSystems.get_reactive_power_limits(device).max` + + +**Objective:** + +Add a cost to the objective function depending on the defined cost structure of the thermal unit by adding it to its `ProductionCostExpression`. + +**Expressions:** + +Adds ``u^\text{th}P^\text{th,min} + \Delta p^\text{th}`` to the `ActivePowerBalance` expression and ``q^\text{th}`` to the `ReactivePowerBalance`, to be used in the supply-balance constraint depending on the network model used. + +**Constraints:** + +For each thermal unit creates the range constraints for its active and reactive power depending on its static parameters. In addition, it creates the commitment constraint to turn on/off the device. + +```math +\begin{align*} +& 0 \le \Delta p^\text{th}_t \le u^\text{th}_t\left(P^\text{th,max} - P^\text{th,min}\right), \quad \forall t\in \{1, \dots, T\} \\ +& u_t^\text{th} Q^\text{th,min} \le q^\text{th}_t \le u_t^\text{th} Q^\text{th,max}, \quad \forall t\in \{1, \dots, T\} \\ +& u_1^\text{th} = u^\text{th,init} + v_1^\text{th} - w_1^\text{th} \\ +& u_t^\text{th} = u_{t-1}^\text{th} + v_t^\text{th} - w_t^\text{th}, \quad \forall t \in \{2,\dots,T\} \\ +& v_t^\text{th} + w_t^\text{th} \le 1, \quad \forall t \in \{1,\dots,T\} +\end{align*} +``` --- @@ -181,40 +330,319 @@ TODO ThermalCompactUnitCommitment ``` -TODO +**Variables:** ---- +- [`PowerAboveMinimumVariable`](@ref): + - Bounds: [0.0, ] + - Symbol: ``\Delta p^\text{th}`` +- [`ReactivePowerVariable`](@ref): + - Bounds: [0.0, ] + - Symbol: ``q^\text{th}`` +- [`OnVariable`](@ref): + - Bounds: ``\{0,1\}`` + - Symbol: ``u_t^\text{th}`` +- [`StartVariable`](@ref): + - Bounds: ``\{0,1\}`` + - Symbol: ``v_t^\text{th}`` +- [`StopVariable`](@ref): + - Bounds: ``\{0,1\}`` + - Symbol: ``w_t^\text{th}`` -## `ThermalMultiStartUnitCommitment` +**Auxiliary Variables:** +- [`PowerOutput`](@ref): + - Symbol: ``P^\text{th}`` + - Definition: ``P^\text{th} = u^\text{th}P^\text{min} + \Delta p^\text{th}`` +- [`TimeDurationOn`](@ref): + - Symbol: ``V_t^\text{th}`` + - Definition: Computed post optimization by adding consecutive turned on variable ``u_t^\text{th}`` +- [`TimeDurationOff`](@ref): + - Symbol: ``W_t^\text{th}`` + - Definition: Computed post optimization by adding consecutive turned off variable ``1 - u_t^\text{th}`` -```@docs -ThermalMultiStartUnitCommitment +**Static Parameters:** + +- ``P^\text{th,min}`` = `PowerSystems.get_active_power_limits(device).min` +- ``P^\text{th,max}`` = `PowerSystems.get_active_power_limits(device).max` +- ``Q^\text{th,min}`` = `PowerSystems.get_reactive_power_limits(device).min` +- ``Q^\text{th,max}`` = `PowerSystems.get_reactive_power_limits(device).max` +- ``R^\text{th,up}`` = `PowerSystems.get_ramp_limits(device).up` +- ``R^\text{th,dn}`` = `PowerSystems.get_ramp_limits(device).down` +- ``D^\text{min,up}`` = `PowerSystems.get_time_limits(device).up` +- ``D^\text{min,dn}`` = `PowerSystems.get_time_limits(device).down` + + +**Objective:** + +Add a cost to the objective function depending on the defined cost structure of the thermal unit by adding it to its `ProductionCostExpression`. + +**Expressions:** + +Adds ``u^\text{th}P^\text{th,min} + \Delta p^\text{th}`` to the `ActivePowerBalance` expression and ``q^\text{th}`` to the `ReactivePowerBalance`, to be used in the supply-balance constraint depending on the network model used. + +**Constraints:** + +For each thermal unit creates the range constraints for its active and reactive power depending on its static parameters. It also creates the commitment constraint to turn on/off the device. + +```math +\begin{align*} +& 0 \le \Delta p^\text{th}_t \le u^\text{th}_t\left(P^\text{th,max} - P^\text{th,min}\right), \quad \forall t\in \{1, \dots, T\} \\ +& u_t^\text{th} Q^\text{th,min} \le q^\text{th}_t \le u_t^\text{th} Q^\text{th,max}, \quad \forall t\in \{1, \dots, T\} \\ +& -R^\text{th,dn} \le \Delta p_1^\text{th} - \Delta p^\text{th, init} \le R^\text{th,up} \\ +& -R^\text{th,dn} \le \Delta p_t^\text{th} - \Delta p_{t-1}^\text{th} \le R^\text{th,up}, \quad \forall t\in \{2, \dots, T\} \\ +& u_1^\text{th} = u^\text{th,init} + v_1^\text{th} - w_1^\text{th} \\ +& u_t^\text{th} = u_{t-1}^\text{th} + v_t^\text{th} - w_t^\text{th}, \quad \forall t \in \{2,\dots,T\} \\ +& v_t^\text{th} + w_t^\text{th} \le 1, \quad \forall t \in \{1,\dots,T\} +\end{align*} +``` + +In addition, this formulation adds duration constraints, i.e. minimum-up time and minimum-down time constraints. The duration constraints are added over the start times looking backwards. + +The duration times ``D^\text{min,up}`` and ``D^\text{min,dn}`` are processed to be used in multiple of the time-steps, given the resolution of the specific problem. In addition, parameters ``D^\text{init,up}`` and ``D^\text{init,dn}`` are used to identify how long the unit was on or off, respectively, before the simulation started. + +Minimum up-time constraint for ``t \in \{1,\dots T\}``: +```math +\begin{align*} +& \text{If } t \leq D^\text{min,up} - D^\text{init,up} \text{ and } D^\text{init,up} > 0: \\ +& 1 + \sum_{i=t-D^\text{min,up} + 1}^t v_i^\text{th} \leq u_t^\text{th} \quad \text{(for } i \text{ in the set of time steps).} \\ +& \text{Otherwise:} \\ +& \sum_{i=t-D^\text{min,up} + 1}^t v_i^\text{th} \leq u_t^\text{th} +\end{align*} ``` -TODO +Minimum down-time constraint for ``t \in \{1,\dots T\}``: +```math +\begin{align*} +& \text{If } t \leq D^\text{min,dn} - D^\text{init,dn} \text{ and } D^\text{init,up} > 0: \\ +& 1 + \sum_{i=t-D^\text{min,dn} + 1}^t w_i^\text{th} \leq 1 - u_t^\text{th} \quad \text{(for } i \text{ in the set of time steps).} \\ +& \text{Otherwise:} \\ +& \sum_{i=t-D^\text{min,dn} + 1}^t w_i^\text{th} \leq 1 - u_t^\text{th} +\end{align*} +``` --- -## `ThermalBasicUnitCommitment` +## `ThermalStandardUnitCommitment` ```@docs -ThermalBasicUnitCommitment +ThermalStandardUnitCommitment +``` + +**Variables:** + +- [`ActivePowerVariable`](@ref): + - Bounds: [0.0, ] + - Symbol: ``p^\text{th}`` +- [`ReactivePowerVariable`](@ref): + - Bounds: [0.0, ] + - Symbol: ``q^\text{th}`` +- [`OnVariable`](@ref): + - Bounds: ``\{0,1\}`` + - Symbol: ``u_t^\text{th}`` +- [`StartVariable`](@ref): + - Bounds: ``\{0,1\}`` + - Symbol: ``v_t^\text{th}`` +- [`StopVariable`](@ref): + - Bounds: ``\{0,1\}`` + - Symbol: ``w_t^\text{th}`` + +**Auxiliary Variables:** +- [`TimeDurationOn`](@ref): + - Symbol: ``V_t^\text{th}`` + - Definition: Computed post optimization by adding consecutive turned on variable ``u_t^\text{th}`` +- [`TimeDurationOff`](@ref): + - Symbol: ``W_t^\text{th}`` + - Definition: Computed post optimization by adding consecutive turned off variable ``1 - u_t^\text{th}`` + +**Static Parameters:** + +- ``P^\text{th,min}`` = `PowerSystems.get_active_power_limits(device).min` +- ``P^\text{th,max}`` = `PowerSystems.get_active_power_limits(device).max` +- ``Q^\text{th,min}`` = `PowerSystems.get_reactive_power_limits(device).min` +- ``Q^\text{th,max}`` = `PowerSystems.get_reactive_power_limits(device).max` +- ``R^\text{th,up}`` = `PowerSystems.get_ramp_limits(device).up` +- ``R^\text{th,dn}`` = `PowerSystems.get_ramp_limits(device).down` +- ``D^\text{min,up}`` = `PowerSystems.get_time_limits(device).up` +- ``D^\text{min,dn}`` = `PowerSystems.get_time_limits(device).down` + + +**Objective:** + +Add a cost to the objective function depending on the defined cost structure of the thermal unit by adding it to its `ProductionCostExpression`. + +**Expressions:** + +Adds ``p^\text{th}`` to the `ActivePowerBalance` expression and ``q^\text{th}`` to the `ReactivePowerBalance`, to be used in the supply-balance constraint depending on the network model used. + +**Constraints:** + +For each thermal unit creates the range constraints for its active and reactive power depending on its static parameters. It also creates the commitment constraint to turn on/off the device. + +```math +\begin{align*} +& u^\text{th}_t P^\text{th,min} \le p^\text{th}_t \le u^\text{th}_t P^\text{th,max}, \quad \forall t\in \{1, \dots, T\} \\ +& u_t^\text{th} Q^\text{th,min} \le q^\text{th}_t \le u_t^\text{th} Q^\text{th,max}, \quad \forall t\in \{1, \dots, T\} \\ +& -R^\text{th,dn} \le p_1^\text{th} - p^\text{th, init} \le R^\text{th,up} \\ +& -R^\text{th,dn} \le p_t^\text{th} - p_{t-1}^\text{th} \le R^\text{th,up}, \quad \forall t\in \{2, \dots, T\} \\ +& u_1^\text{th} = u^\text{th,init} + v_1^\text{th} - w_1^\text{th} \\ +& u_t^\text{th} = u_{t-1}^\text{th} + v_t^\text{th} - w_t^\text{th}, \quad \forall t \in \{2,\dots,T\} \\ +& v_t^\text{th} + w_t^\text{th} \le 1, \quad \forall t \in \{1,\dots,T\} +\end{align*} +``` + +In addition, this formulation adds duration constraints, i.e. minimum-up time and minimum-down time constraints. The duration constraints are added over the start times looking backwards. + +The duration times ``D^\text{min,up}`` and ``D^\text{min,dn}`` are processed to be used in multiple of the time-steps, given the resolution of the specific problem. In addition, parameters ``D^\text{init,up}`` and ``D^\text{init,dn}`` are used to identify how long the unit was on or off, respectively, before the simulation started. + +Minimum up-time constraint for ``t \in \{1,\dots T\}``: +```math +\begin{align*} +& \text{If } t \leq D^\text{min,up} - D^\text{init,up} \text{ and } D^\text{init,up} > 0: \\ +& 1 + \sum_{i=t-D^\text{min,up} + 1}^t v_i^\text{th} \leq u_t^\text{th} \quad \text{(for } i \text{ in the set of time steps).} \\ +& \text{Otherwise:} \\ +& \sum_{i=t-D^\text{min,up} + 1}^t v_i^\text{th} \leq u_t^\text{th} +\end{align*} +``` + +Minimum down-time constraint for ``t \in \{1,\dots T\}``: +```math +\begin{align*} +& \text{If } t \leq D^\text{min,dn} - D^\text{init,dn} \text{ and } D^\text{init,up} > 0: \\ +& 1 + \sum_{i=t-D^\text{min,dn} + 1}^t w_i^\text{th} \leq 1 - u_t^\text{th} \quad \text{(for } i \text{ in the set of time steps).} \\ +& \text{Otherwise:} \\ +& \sum_{i=t-D^\text{min,dn} + 1}^t w_i^\text{th} \leq 1 - u_t^\text{th} +\end{align*} ``` -TODO --- -## `ThermalStandardUnitCommitment` +## `ThermalMultiStartUnitCommitment` ```@docs -ThermalStandardUnitCommitment +ThermalMultiStartUnitCommitment +``` + + +**Variables:** + +- [`PowerAboveMinimumVariable`](@ref): + - Bounds: [0.0, ] + - Symbol: ``\Delta p^\text{th}`` +- [`ReactivePowerVariable`](@ref): + - Bounds: [0.0, ] + - Symbol: ``q^\text{th}`` +- [`OnVariable`](@ref): + - Bounds: ``\{0,1\}`` + - Symbol: ``u_t^\text{th}`` +- [`StartVariable`](@ref): + - Bounds: ``\{0,1\}`` + - Symbol: ``v_t^\text{th}`` +- [`StopVariable`](@ref): + - Bounds: ``\{0,1\}`` + - Symbol: ``w_t^\text{th}`` +- [`ColdStartVariable`](@ref): + - Bounds: ``\{0,1\}`` + - Symbol: ``x_t^\text{th}`` +- [`WarmStartVariable`](@ref): + - Bounds: ``\{0,1\}`` + - Symbol: ``y_t^\text{th}`` +- [`HotStartVariable`](@ref): + - Bounds: ``\{0,1\}`` + - Symbol: ``z_t^\text{th}`` + +**Auxiliary Variables:** +- [`PowerOutput`](@ref): + - Symbol: ``P^\text{th}`` + - Definition: ``P^\text{th} = u^\text{th}P^\text{min} + \Delta p^\text{th}`` +- [`TimeDurationOn`](@ref): + - Symbol: ``V_t^\text{th}`` + - Definition: Computed post optimization by adding consecutive turned on variable ``u_t^\text{th}`` +- [`TimeDurationOff`](@ref): + - Symbol: ``W_t^\text{th}`` + - Definition: Computed post optimization by adding consecutive turned off variable ``1 - u_t^\text{th}`` + +**Static Parameters:** + +- ``P^\text{th,min}`` = `PowerSystems.get_active_power_limits(device).min` +- ``P^\text{th,max}`` = `PowerSystems.get_active_power_limits(device).max` +- ``Q^\text{th,min}`` = `PowerSystems.get_reactive_power_limits(device).min` +- ``Q^\text{th,max}`` = `PowerSystems.get_reactive_power_limits(device).max` +- ``R^\text{th,up}`` = `PowerSystems.get_ramp_limits(device).up` +- ``R^\text{th,dn}`` = `PowerSystems.get_ramp_limits(device).down` +- ``D^\text{min,up}`` = `PowerSystems.get_time_limits(device).up` +- ``D^\text{min,dn}`` = `PowerSystems.get_time_limits(device).down` +- ``D^\text{cold}`` = `PowerSystems.get_start_time_limits(device).cold` +- ``D^\text{warm}`` = `PowerSystems.get_start_time_limits(device).warm` +- ``D^\text{hot}`` = `PowerSystems.get_start_time_limits(device).hot` + + +**Objective:** + +Add a cost to the objective function depending on the defined cost structure of the thermal unit by adding it to its `ProductionCostExpression`. + +**Expressions:** + +Adds ``u^\text{th}P^\text{th,min} + \Delta p^\text{th}`` to the `ActivePowerBalance` expression and ``q^\text{th}`` to the `ReactivePowerBalance`, to be used in the supply-balance constraint depending on the network model used. + +**Constraints:** + +For each thermal unit creates the range constraints for its active and reactive power depending on its static parameters. It also creates the commitment constraint to turn on/off the device. + +```math +\begin{align*} +& 0 \le \Delta p^\text{th}_t \le u^\text{th}_t\left(P^\text{th,max} - P^\text{th,min}\right), \quad \forall t\in \{1, \dots, T\} \\ +& u_t^\text{th} Q^\text{th,min} \le q^\text{th}_t \le u_t^\text{th} Q^\text{th,max}, \quad \forall t\in \{1, \dots, T\} \\ +& -R^\text{th,dn} \le \Delta p_1^\text{th} - \Delta p^\text{th, init} \le R^\text{th,up} \\ +& -R^\text{th,dn} \le \Delta p_t^\text{th} - \Delta p_{t-1}^\text{th} \le R^\text{th,up}, \quad \forall t\in \{2, \dots, T\} \\ +& u_1^\text{th} = u^\text{th,init} + v_1^\text{th} - w_1^\text{th} \\ +& u_t^\text{th} = u_{t-1}^\text{th} + v_t^\text{th} - w_t^\text{th}, \quad \forall t \in \{2,\dots,T\} \\ +& v_t^\text{th} + w_t^\text{th} \le 1, \quad \forall t \in \{1,\dots,T\} +\end{align*} +``` + +In addition, this formulation adds duration constraints, i.e. minimum-up time and minimum-down time constraints. The duration constraints are added over the start times looking backwards. + +The duration times ``D^\text{min,up}`` and ``D^\text{min,dn}`` are processed to be used in multiple of the time-steps, given the resolution of the specific problem. In addition, parameters ``D^\text{init,up}`` and ``D^\text{init,dn}`` are used to identify how long the unit was on or off, respectively, before the simulation started. + +Minimum up-time constraint for ``t \in \{1,\dots T\}``: +```math +\begin{align*} +& \text{If } t \leq D^\text{min,up} - D^\text{init,up} \text{ and } D^\text{init,up} > 0: \\ +& 1 + \sum_{i=t-D^\text{min,up} + 1}^t v_i^\text{th} \leq u_t^\text{th} \quad \text{(for } i \text{ in the set of time steps).} \\ +& \text{Otherwise:} \\ +& \sum_{i=t-D^\text{min,up} + 1}^t v_i^\text{th} \leq u_t^\text{th} +\end{align*} +``` + +Minimum down-time constraint for ``t \in \{1,\dots T\}``: +```math +\begin{align*} +& \text{If } t \leq D^\text{min,dn} - D^\text{init,dn} \text{ and } D^\text{init,up} > 0: \\ +& 1 + \sum_{i=t-D^\text{min,dn} + 1}^t w_i^\text{th} \leq 1 - u_t^\text{th} \quad \text{(for } i \text{ in the set of time steps).} \\ +& \text{Otherwise:} \\ +& \sum_{i=t-D^\text{min,dn} + 1}^t w_i^\text{th} \leq 1 - u_t^\text{th} +\end{align*} +``` + +Finally, multi temperature start/stop constraints are implemented using the following constraints: + +```math +\begin{align*} +& v_t^\text{th} = x_t^\text{th} + y_t^\text{th} + z_t^\text{th}, \quad \forall t \in \{1, \dots, T\} \\ +& z_t^\text{th} \le \sum_{i \in [D^\text{hot}, D^\text{warm})}w_{t-i}^\text{th}, \quad \forall t \in \{D^\text{warm}, \dots, T\} \\ +& y_t^\text{th} \le \sum_{i \in [D^\text{warm}, D^\text{cold})}w_{t-i}^\text{th}, \quad \forall t \in \{D^\text{cold}, \dots, T\} \\ +& (D^\text{warm} - 1) z_t^\text{th} + (1 - z_t^\text{th}) M^\text{big} \ge \sum_{i=1}^t (1 - u_i^\text{th}) + D^\text{init,hot}, \quad \forall t \in \{1, \dots, T\} \\ +& D^\text{hot} z_t^\text{th} \le \sum_{i=1}^t (1 - u_i^\text{th}) + D^\text{init,hot}, \quad \forall t \in \{1, \dots, T\} \\ +& (D^\text{cold} - 1) y_t^\text{th} + (1 - y_t^\text{th}) M^\text{big} \ge \sum_{i=1}^t (1 - u_i^\text{th}) + D^\text{init,warm}, \quad \forall t \in \{1, \dots, T\} \\ +& D^\text{warm} y_t^\text{th} \le \sum_{i=1}^t (1 - u_i^\text{th}) + D^\text{init,warm}, \quad \forall t \in \{1, \dots, T\} \\ +\end{align*} ``` -TODO --- +## Valid configurations + Valid `DeviceModel`s for subtypes of `ThermalGen` include the following: ```@eval From 5177413236f2a1603d81f5ea08c558dd61647fd3 Mon Sep 17 00:00:00 2001 From: rodrigomha Date: Mon, 29 Apr 2024 12:15:51 -0700 Subject: [PATCH 12/69] update variable symbols --- src/core/variables.jl | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/src/core/variables.jl b/src/core/variables.jl index c48d6566ae..6dca0be96a 100644 --- a/src/core/variables.jl +++ b/src/core/variables.jl @@ -41,42 +41,42 @@ struct ActivePowerVariable <: VariableType end """ Struct to dispatch the creation of Active Power Variables above minimum power for Thermal Compact formulations -Docs abbreviation: ``\\hat{p}`` +Docs abbreviation: ``\\Delta p`` """ struct PowerAboveMinimumVariable <: VariableType end """ Struct to dispatch the creation of Active Power Input Variables for 2-directional devices. For instance storage or pump-hydro -Docs abbreviation: ``p^\text{in}`` +Docs abbreviation: ``p^\\text{in}`` """ struct ActivePowerInVariable <: VariableType end """ Struct to dispatch the creation of Active Power Output Variables for 2-directional devices. For instance storage or pump-hydro -Docs abbreviation: ``p^\text{out}`` +Docs abbreviation: ``p^\\text{out}`` """ struct ActivePowerOutVariable <: VariableType end """ Struct to dispatch the creation of Hot Start Variable for Thermal units with temperature considerations -Docs abbreviation: ``z^\text{th}`` +Docs abbreviation: ``z^\\text{th}`` """ struct HotStartVariable <: VariableType end """ Struct to dispatch the creation of Warm Start Variable for Thermal units with temperature considerations -Docs abbreviation: ``y^\text{th}`` +Docs abbreviation: ``y^\\text{th}`` """ struct WarmStartVariable <: VariableType end """ Struct to dispatch the creation of Cold Start Variable for Thermal units with temperature considerations -Docs abbreviation: ``x^\text{th}`` +Docs abbreviation: ``x^\\text{th}`` """ struct ColdStartVariable <: VariableType end @@ -150,21 +150,31 @@ struct SmoothACE <: VariableType end """ Struct to dispatch the creation of System-wide slack up variables. Used when there is not enough generation. -Docs abbreviation: ``p^\text{sl,up}`` +Docs abbreviation: ``p^\\text{sl,up}`` """ struct SystemBalanceSlackUp <: VariableType end """ Struct to dispatch the creation of System-wide slack down variables. Used when there is not enough load curtailment. -Docs abbreviation: ``p^\text{sl,dn}`` +Docs abbreviation: ``p^\\text{sl,dn}`` """ struct SystemBalanceSlackDown <: VariableType end struct ReserveRequirementSlack <: VariableType end +""" +Struct to dispatch the creation of active power flow upper bound slack variables. Used when there is not enough flow through the branch in the forward direction. + +Docs abbreviation: ``f^\\text{sl,up}`` +""" struct FlowActivePowerSlackUpperBound <: VariableType end +""" +Struct to dispatch the creation of active power flow lower bound slack variables. Used when there is not enough flow through the branch in the reverse direction. + +Docs abbreviation: ``f^\\text{sl,lo}`` +""" struct FlowActivePowerSlackLowerBound <: VariableType end """ @@ -184,7 +194,7 @@ struct VoltageAngle <: VariableType end """ Struct to dispatch the creation of bidirectional Active Power Flow Variables -Docs abbreviation: ``P`` +Docs abbreviation: ``f`` """ struct FlowActivePowerVariable <: VariableType end From 346a313383cff12150b0668b4ade622600c0a046 Mon Sep 17 00:00:00 2001 From: rodrigomha Date: Mon, 29 Apr 2024 12:16:01 -0700 Subject: [PATCH 13/69] update docstrings for formulations --- src/core/formulations.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/formulations.jl b/src/core/formulations.jl index 1b37b7a371..e88a7a2c3c 100644 --- a/src/core/formulations.jl +++ b/src/core/formulations.jl @@ -33,7 +33,7 @@ Formulation type to enable standard dispatch with a range and enforce intertempo """ struct ThermalStandardDispatch <: AbstractThermalDispatchFormulation end """ -Formulation type to enable basic dispatch without any intertemporal constraints and relaxed minimum generation. *May not work with PWL cost definitions* +Formulation type to enable basic dispatch without any intertemporal constraints and relaxed minimum generation. *May not work with non-convex PWL cost definitions* """ struct ThermalDispatchNoMin <: AbstractThermalDispatchFormulation end """ @@ -58,7 +58,7 @@ abstract type AbstractLoadFormulation <: AbstractDeviceFormulation end abstract type AbstractControllablePowerLoadFormulation <: AbstractLoadFormulation end """ -Formulation type to add a time series parameter for non-dispatchable `ElectricLoad` withdrawls to power balance constraints +Formulation type to add a time series parameter for non-dispatchable `ElectricLoad` withdrawals to power balance constraints """ struct StaticPowerLoad <: AbstractLoadFormulation end From add41c37b45c3501862c4cfb51eb76988667e07f Mon Sep 17 00:00:00 2001 From: rodrigomha Date: Mon, 29 Apr 2024 12:16:11 -0700 Subject: [PATCH 14/69] export slack for flows --- src/PowerSimulations.jl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/PowerSimulations.jl b/src/PowerSimulations.jl index 73cc11e950..c62b830d9b 100644 --- a/src/PowerSimulations.jl +++ b/src/PowerSimulations.jl @@ -223,6 +223,8 @@ export ReserveRequirementSlack export VoltageMagnitude export VoltageAngle export FlowActivePowerVariable +export FlowActivePowerSlackUpperBound +export FlowActivePowerSlackLowerBound export FlowActivePowerFromToVariable export FlowActivePowerToFromVariable export FlowReactivePowerFromToVariable From ff2b8f1ce5ffcff83542c8779de70773b1aca0df Mon Sep 17 00:00:00 2001 From: rodrigomha Date: Mon, 29 Apr 2024 12:16:27 -0700 Subject: [PATCH 15/69] add service TODO docs --- docs/make.jl | 1 + docs/src/formulation_library/Service.md | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/make.jl b/docs/make.jl index d12e2e7e69..1537f13100 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -38,6 +38,7 @@ pages = OrderedDict( "Renewable Generation" => "formulation_library/RenewableGen.md", "Load" => "formulation_library/Load.md", "Branch" => "formulation_library/Branch.md", + "Services" => "formulation_library/Service.md", ], "API Reference" => "api/PowerSimulations.md", ) diff --git a/docs/src/formulation_library/Service.md b/docs/src/formulation_library/Service.md index b2b749754a..4b713ebd4a 100644 --- a/docs/src/formulation_library/Service.md +++ b/docs/src/formulation_library/Service.md @@ -1,3 +1,3 @@ -# [`PowerSystems.Service` Formulations][@id service_formulations] +# [`PowerSystems.Service` Formulations](@id service_formulations) TODO From 018b28bf8f22f6203d953303ce40567129deae80 Mon Sep 17 00:00:00 2001 From: rodrigomha Date: Mon, 29 Apr 2024 12:16:39 -0700 Subject: [PATCH 16/69] update network docs --- docs/src/formulation_library/Network.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/formulation_library/Network.md b/docs/src/formulation_library/Network.md index 15f49c984e..eae0aff52a 100644 --- a/docs/src/formulation_library/Network.md +++ b/docs/src/formulation_library/Network.md @@ -110,7 +110,7 @@ Add a large proportional cost to the objective function if slack variables are u **Expressions:** -Adds ``p^\text{sl,up}`` and ``p^\text{sl,dn}`` terms to the respective active power balance expressions `ActivePowerBalance` created by this `CopperPlatePowerModel` network formulation. +Adds ``p^\text{sl,up}`` and ``p^\text{sl,dn}`` terms to the respective system-wide active power balance expressions `ActivePowerBalance` created by this `CopperPlatePowerModel` network formulation. In addition, it creates `ActivePowerBalance` expressions for each bus to be used in the calculation of branch flows. **Constraints:** From d332f45491e671757d508720392898eaad8798c1 Mon Sep 17 00:00:00 2001 From: rodrigomha Date: Mon, 29 Apr 2024 12:16:47 -0700 Subject: [PATCH 17/69] update renewable docs --- docs/src/formulation_library/RenewableGen.md | 82 ++++++++++++-------- 1 file changed, 48 insertions(+), 34 deletions(-) diff --git a/docs/src/formulation_library/RenewableGen.md b/docs/src/formulation_library/RenewableGen.md index 9a9a65f8d1..89de8e0b36 100644 --- a/docs/src/formulation_library/RenewableGen.md +++ b/docs/src/formulation_library/RenewableGen.md @@ -1,23 +1,12 @@ # `PowerSystems.RenewableGen` Formulations -Valid `DeviceModel`s for subtypes of `RenewableGen` include the following: +Renewable generation formulations define the optimization models that describe renewable units mathematical model in different operational settings, such as economic dispatch and unit commitment. -```@eval -using PowerSimulations -using PowerSystems -using DataFrames -using Latexify -combos = PowerSimulations.generate_device_formulation_combinations() -filter!(x -> x["device_type"] <: RenewableGen, combos) -combo_table = DataFrame( - "Valid DeviceModel" => ["`DeviceModel($(c["device_type"]), $(c["formulation"]))`" for c in combos], - "Device Type" => ["[$(c["device_type"])](https://nrel-Sienna.github.io/PowerSystems.jl/stable/model_library/generated_$(c["device_type"])/)" for c in combos], - "Formulation" => ["[$(c["formulation"])](@ref)" for c in combos], - ) -mdtable(combo_table, latex = false) -``` +!!! note + The usage of reactive power variables and constraints will depend on the network model used, i.e. if it uses (or not) reactive power. If the network model is purely active power based, then no variables and constraints related to reactive power are created. For the sake of completion, if the formulation allows the usage of reactive power it will be included. ---- +!!! note + Reserve variables for services are not included in the formulation, albeit their inclusion change the variables, expressions, constraints and objective functions created. A detailed description of the implications in the optimization models is described in the [Service formulation](@ref service_formulation) section. ## `RenewableFullDispatch` @@ -29,19 +18,21 @@ RenewableFullDispatch - [`ActivePowerVariable`](@ref): - Bounds: [0.0, ] - - Default initial value: `PowerSystems.get_active_power(device)` + - Symbol: ``p^\text{re}`` - [`ReactivePowerVariable`](@ref): - Bounds: [0.0, ] - - Default initial value: `PowerSystems.get_reactive_power(device)` + - Symbol: ``q^\text{re}`` **Static Parameters:** -- ``Pg^\text{min}`` = `PowerSystems.get_active_power_limits(device).min` -- ``Qg^\text{min}`` = `PowerSystems.get_reactive_power_limits(device).min` -- ``Qg^\text{max}`` = `PowerSystems.get_reactive_power_limits(device).max` +- ``P^\text{re,min}`` = `PowerSystems.get_active_power_limits(device).min` +- ``Q^\text{re,min}`` = `PowerSystems.get_reactive_power_limits(device).min` +- ``Q^\text{re,max}`` = `PowerSystems.get_reactive_power_limits(device).max` **Time Series Parameters:** +Uses the `max_active_power` timeseries parameter to limit the available active power at each time-step. + ```@eval using PowerSimulations using PowerSystems @@ -57,18 +48,18 @@ mdtable(combo_table, latex = false) **Objective:** -Creates an objective function term based on the [`VariableCost` Options](@ref) where the quantity term is defined as ``- Pg_t`` to incentivize generation from `RenewableGen` devices. +Creates an objective function term based on the [`VariableCost` Options](@ref) where the quantity term is defined as ``- p^\text{re}`` to incentivize generation from `RenewableGen` devices. **Expressions:** -Adds ``Pg`` and ``Qg`` terms to the respective active and reactive power balance expressions created by the selected [Network Formulations](@ref network_formulations) +Adds ``p^\text{re}`` and ``q^\text{re}`` terms to the respective active and reactive power balance expressions created by the selected [Network Formulations](@ref network_formulations). **Constraints:** ```math \begin{aligned} -& Pg^\text{min} \le Pg_t \le ActivePowerTimeSeriesParameter_t \\ -& Qg^\text{min} \le Qg_t \le Qg^\text{max} +& P^\text{re,min} \le p_t^\text{re} \le \text{ActivePowerTimeSeriesParameter}_t, \quad \forall t \in \{1,\dots, T\} \\ +& Q^\text{re,min} \le q_t^\text{re} \le Q^\text{re,max}, \quad \forall t \in \{1,\dots, T\} \end{aligned} ``` @@ -85,16 +76,18 @@ RenewableConstantPowerFactor - [`ActivePowerVariable`](@ref): - Bounds: [0.0, ] - Default initial value: `PowerSystems.get_active_power(device)` + - Symbol: ``p^\text{re}`` - [`ReactivePowerVariable`](@ref): - Bounds: [0.0, ] - Default initial value: `PowerSystems.get_reactive_power(device)` + - Symbol: ``q^\text{re}`` **Static Parameters:** -- ``Pg^\text{min}`` = `PowerSystems.get_active_power_limits(device).min` -- ``Qg^\text{min}`` = `PowerSystems.get_reactive_power_limits(device).min` -- ``Qg^\text{max}`` = `PowerSystems.get_reactive_power_limits(device).max` -- ``pf`` = `PowerSystems.get_power_factor(device)` +- ``P^\text{re,min}`` = `PowerSystems.get_active_power_limits(device).min` +- ``Q^\text{re,min}`` = `PowerSystems.get_reactive_power_limits(device).min` +- ``Q^\text{re,max}`` = `PowerSystems.get_reactive_power_limits(device).max` +- ``\text{pf}`` = `PowerSystems.get_power_factor(device)` **Time Series Parameters:** @@ -113,18 +106,39 @@ mdtable(combo_table, latex = false) **Objective:** -Creates an objective function term based on the [`VariableCost` Options](@ref) where the quantity term is defined as ``- Pg_t`` to incentivize generation from `RenewableGen` devices. +Creates an objective function term based on the [`VariableCost` Options](@ref) where the quantity term is defined as ``- p_t^\text{re}`` to incentivize generation from `RenewableGen` devices. **Expressions:** -Adds ``Pg`` and ``Qg`` terms to the respective active and reactive power balance expressions created by the selected [Network Formulations](@ref network_formulations) +Adds ``p^\text{re}`` and ``q^\text{re}`` terms to the respective active and reactive power balance expressions created by the selected [Network Formulations](@ref network_formulations) **Constraints:** ```math \begin{aligned} -& Pg^\text{min} \le Pg_t \le ActivePowerTimeSeriesParameter_t \\ -& Qg^\text{min} \le Qg_t \le Qg^\text{max} \\ -& Qg_t = pf * Pg_t +& P^\text{re,min} \le p_t^\text{re} \le \text{ActivePowerTimeSeriesParameter}_t, \quad \forall t \in \{1,\dots, T\} \\ +& q_t^\text{re} = \text{pf} \cdot p_t^\text{re}, \quad \forall t \in \{1,\dots, T\} \end{aligned} ``` + +--- + +## Valid configurations + +Valid `DeviceModel`s for subtypes of `RenewableGen` include the following: + +```@eval +using PowerSimulations +using PowerSystems +using DataFrames +using Latexify +combos = PowerSimulations.generate_device_formulation_combinations() +filter!(x -> x["device_type"] <: RenewableGen, combos) +combo_table = DataFrame( + "Valid DeviceModel" => ["`DeviceModel($(c["device_type"]), $(c["formulation"]))`" for c in combos], + "Device Type" => ["[$(c["device_type"])](https://nrel-Sienna.github.io/PowerSystems.jl/stable/model_library/generated_$(c["device_type"])/)" for c in combos], + "Formulation" => ["[$(c["formulation"])](@ref)" for c in combos], + ) +mdtable(combo_table, latex = false) +``` + From 7164f3af136947282555f3823364721cbcf58f81 Mon Sep 17 00:00:00 2001 From: rodrigomha Date: Mon, 29 Apr 2024 12:16:53 -0700 Subject: [PATCH 18/69] update load docs --- docs/src/formulation_library/Load.md | 84 ++++++++++++++++------------ 1 file changed, 49 insertions(+), 35 deletions(-) diff --git a/docs/src/formulation_library/Load.md b/docs/src/formulation_library/Load.md index f3fd31cb1a..a1d70064a6 100644 --- a/docs/src/formulation_library/Load.md +++ b/docs/src/formulation_library/Load.md @@ -1,23 +1,9 @@ # `PowerSystems.ElectricLoad` Formulations -Valid `DeviceModel`s for subtypes of `ElectricLoad` include the following: - -```@eval -using PowerSimulations -using PowerSystems -using DataFrames -using Latexify -combos = PowerSimulations.generate_device_formulation_combinations() -filter!(x -> x["device_type"] <: ElectricLoad, combos) -combo_table = DataFrame( - "Valid DeviceModel" => ["`DeviceModel($(c["device_type"]), $(c["formulation"]))`" for c in combos], - "Device Type" => ["[$(c["device_type"])](https://nrel-Sienna.github.io/PowerSystems.jl/stable/model_library/generated_$(c["device_type"])/)" for c in combos], - "Formulation" => ["[$(c["formulation"])](@ref)" for c in combos], - ) -mdtable(combo_table, latex = false) -``` +Electric load formulations define the optimization models that describe load units (demand) mathematical model in different operational settings, such as economic dispatch and unit commitment. ---- +!!! note + The usage of reactive power variables and constraints will depend on the network model used, i.e. if it uses (or not) reactive power. If the network model is purely active power based, then no variables and constraints related to reactive power are created. For the sake of completion, if the formulation allows the usage of reactive power it will be included. ## `StaticPowerLoad` @@ -31,6 +17,8 @@ No variables are created **Time Series Parameters:** +Uses the `max_active_power` timeseries parameter to determine the demand value at each time-step + ```@eval using PowerSimulations using PowerSystems @@ -46,7 +34,7 @@ mdtable(combo_table, latex = false) **Expressions:** -Subtracts the parameters listed above from the respective active and reactive power balance expressions created by the selected [Network Formulations](@ref network_formulations) +Subtracts the parameters listed above from the respective active and reactive power balance expressions created by the selected [Network Formulations](@ref network_formulations). **Constraints:** @@ -65,12 +53,19 @@ PowerLoadInterruption - [`ActivePowerVariable`](@ref): - Bounds: [0.0, ] - Default initial value: 0.0 + - Symbol: ``p^\text{ld}`` - [`ReactivePowerVariable`](@ref): - Bounds: [0.0, ] - Default initial value: 0.0 + - Symbol: ``q^\text{ld}`` - [`OnVariable`](@ref): - - Bounds: {0,1} + - Bounds: ``\{0,1\}`` - Default initial value: 1 + - Symbol: ``u^\text{ld}`` + +**Static Parameters:** +- ``P^\text{ld,max}`` = `PowerSystems.get_max_active_power(device)` +- ``Q^\text{ld,max}`` = `PowerSystems.get_max_reactive_power(device)` **Time Series Parameters:** @@ -89,25 +84,21 @@ mdtable(combo_table, latex = false) **Objective:** -Creates an objective function term based on the [`VariableCost` Options](@ref) where the quantity term is defined as ``Pg``. +Creates an objective function term based on the [`VariableCost` Options](@ref) where the quantity term is defined as ``p^\text{ld}``. **Expressions:** -- Adds ``Pg`` and ``Qg`` terms and to the respective active and reactive power balance expressions created by the selected [Network Formulations](@ref network_formulations) -- Subtracts the time series parameters listed above terms from the respective active and reactive power balance expressions created by the selected [Network Formulations](@ref network_formulations) +- Subtract``p^\text{ld}`` and ``q^\text{ld}`` terms and to the respective active and reactive power balance expressions created by the selected [Network Formulations](@ref network_formulations) **Constraints:** -``Pg`` and ``Qg`` represent the "unserved" active and reactive power loads - ```math \begin{aligned} -& Pg_t \le ActivePowerTimeSeriesParameter_t\\ -& Pg_t - u_t ActivePowerTimeSeriesParameter_t \le 0 \\ -& Qg_t \le ReactivePowerTimeSeriesParameter_t\\ -& Qg_t - u_t ReactivePowerTimeSeriesParameter_t\le 0 +& p_t^\text{ld} \le u_t^\text{ld} \cdot \text{ActivePowerTimeSeriesParameter}_t, \quad \forall t \in \{1,\dots, T\} \\ +& q_t^\text{re} = \text{pf} \cdot p_t^\text{re}, \quad \forall t \in \{1,\dots, T\} \end{aligned} ``` +on which ``\text{pf} = \sin(\arctan(Q^\text{ld,max}/P^\text{ld,max}))``. --- @@ -122,9 +113,15 @@ PowerLoadDispatch - [`ActivePowerVariable`](@ref): - Bounds: [0.0, ] - Default initial value: `PowerSystems.get_active_power(device)` + - Symbol: ``p^\text{ld}`` - [`ReactivePowerVariable`](@ref): - Bounds: [0.0, ] - Default initial value: `PowerSystems.get_reactive_power(device)` + - Symbol: ``q^\text{ld}`` + +**Static Parameters:** +- ``P^\text{ld,max}`` = `PowerSystems.get_max_active_power(device)` +- ``Q^\text{ld,max}`` = `PowerSystems.get_max_reactive_power(device)` **Time Series Parameters:** @@ -143,20 +140,37 @@ mdtable(combo_table, latex = false) **Objective:** -Creates an objective function term based on the [`VariableCost` Options](@ref) where the quantity term is defined as ``Pg``. +Creates an objective function term based on the [`VariableCost` Options](@ref) where the quantity term is defined as ``p^\text{re}``. **Expressions:** -- Adds ``Pg`` and ``Qg`` terms and to the respective active and reactive power balance expressions created by the selected [Network Formulations](@ref network_formulations) -- Subtracts the time series parameters listed above terms from the respective active and reactive power balance expressions created by the selected [Network Formulations](@ref network_formulations) +- Subtract``p^\text{ld}`` and ``q^\text{ld}`` terms and to the respective active and reactive power balance expressions created by the selected [Network Formulations](@ref network_formulations) **Constraints:** -``Pg`` and ``Qg`` represent the "unserved" active and reactive power loads - ```math \begin{aligned} -& Pg_t \le ActivePowerTimeSeriesParameter_t\\ -& Qg_t \le ReactivePowerTimeSeriesParameter_t\\ +& p_t^\text{ld} \le \text{ActivePowerTimeSeriesParameter}_t, \quad \forall t \in \{1,\dots, T\}\\ +& q_t^\text{ld} = \text{pf} \cdot p_t^\text{ld}, \quad \forall t \in \{1,\dots, T\}\\ \end{aligned} ``` +on which ``\text{pf} = \sin(\arctan(Q^\text{ld,max}/P^\text{ld,max}))``. + +## Valid configurations + +Valid `DeviceModel`s for subtypes of `ElectricLoad` include the following: + +```@eval +using PowerSimulations +using PowerSystems +using DataFrames +using Latexify +combos = PowerSimulations.generate_device_formulation_combinations() +filter!(x -> x["device_type"] <: ElectricLoad, combos) +combo_table = DataFrame( + "Valid DeviceModel" => ["`DeviceModel($(c["device_type"]), $(c["formulation"]))`" for c in combos], + "Device Type" => ["[$(c["device_type"])](https://nrel-Sienna.github.io/PowerSystems.jl/stable/model_library/generated_$(c["device_type"])/)" for c in combos], + "Formulation" => ["[$(c["formulation"])](@ref)" for c in combos], + ) +mdtable(combo_table, latex = false) +``` From 2b4a6bcf44cc939bf06e63af3f63becd606373d3 Mon Sep 17 00:00:00 2001 From: rodrigomha Date: Mon, 29 Apr 2024 12:16:58 -0700 Subject: [PATCH 19/69] update branch docs --- docs/src/formulation_library/Branch.md | 137 +++++++++++++++++++++---- 1 file changed, 117 insertions(+), 20 deletions(-) diff --git a/docs/src/formulation_library/Branch.md b/docs/src/formulation_library/Branch.md index f55d6c37e7..79c6d04766 100644 --- a/docs/src/formulation_library/Branch.md +++ b/docs/src/formulation_library/Branch.md @@ -1,47 +1,123 @@ # `PowerSystems.Branch` Formulations +!!! note + The usage of reactive power variables and constraints will depend on the network model used, i.e. if it uses (or not) reactive power. If the network model is purely active power based, then no variables and constraints related to reactive power are created. For the sake of completion, if the formulation allows the usage of reactive power it will be included. -Valid `DeviceModel`s for subtypes of `Branch` include the following: +## `StaticBranch` for `PTDFPowerModel` Network model -```@eval -using PowerSimulations -using PowerSystems -using DataFrames -using Latexify -combos = PowerSimulations.generate_device_formulation_combinations() -filter!(x -> x["device_type"] <: Branch, combos) -combo_table = DataFrame( - "Valid DeviceModel" => ["`DeviceModel($(c["device_type"]), $(c["formulation"]))`" for c in combos], - "Device Type" => ["[$(c["device_type"])](https://nrel-Sienna.github.io/PowerSystems.jl/stable/model_library/generated_$(c["device_type"])/)" for c in combos], - "Formulation" => ["[$(c["formulation"])](@ref)" for c in combos], - ) -mdtable(combo_table, latex = false) +```@docs +StaticBranch ``` ---- +**Variables:** -## `StaticBranch` +- [`FlowActivePowerVariable`](@ref): + - Bounds: ``(-\infty,\infty)`` + - Symbol: ``f`` +If Slack variables are enabled: +- [`FlowActivePowerSlackUpperBound`](@ref): + - Bounds: [0.0, ] + - Default proportional cost: 2e5 + - Symbol: ``f^\text{sl,up}`` +- [`FlowActivePowerSlackLowerBound`](@ref): + - Bounds: [0.0, ] + - Default proportional cost: 2e5 + - Symbol: ``f^\text{sl,lo}`` -```@docs -StaticBranch +**Static Parameters** + +- ``R^\text{max}`` = `PowerSystems.get_rate(branch)` + +**Objective:** + +Add a large proportional cost to the objective function if rate constraint slack variables are used ``+ (f^\text{sl,up} + f^\text{sl,lo}) \cdot 2 \cdot 10^5`` + +**Expressions:** + +No expressions are used. + +**Constraints:** + +For each branch ``b \in \{1,\dots, B\}`` (in a system with ``N`` buses) the constraints are given by: + +```math +\begin{aligned} +& f_t = \sum_{i=1}^N \text{PTDF}_{i,b} \cdot \text{Bal}_{i,t}, \quad \forall t \in \{1,\dots, T\}\\ +& f_t - f_t^\text{sl,up} \le R^\text{max} \\ +& f_t + f_t^\text{sl,lo} \le -R^\text{max} \\ +\end{aligned} ``` +on which ``\text{PTDF}`` is the ``N \times B`` system Power Transfer Distribution Factors (PTDF) matrix, and ``\text{Bal}_{i,t}`` is the active power bus balance expression (i.e. ``\text{Generation}_{i,t} - \text{Demand}_{i,t}``) at bus ``i`` at time-step ``t``. --- -## `StaticBranchBounds` +## `StaticBranchBounds` for `PTDFPowerModel` Network model ```@docs StaticBranchBounds ``` +**Variables:** + +- [`FlowActivePowerVariable`](@ref): + - Bounds: ``\left[-R^\text{max},R^\text{max}\right]`` + - Symbol: ``f`` + +**Static Parameters** + +- ``R^\text{max}`` = `PowerSystems.get_rate(branch)` + +**Objective:** + +No cost is added to the objective function. + +**Expressions:** + +No expressions are used. + +**Constraints:** + +For each branch ``b \in \{1,\dots, B\}`` (in a system with ``N`` buses) the constraints are given by: + +```math +\begin{aligned} +& f_t = \sum_{i=1}^N \text{PTDF}_{i,b} \cdot \text{Bal}_{i,t}, \quad \forall t \in \{1,\dots, T\} +\end{aligned} +``` +on which ``\text{PTDF}`` is the ``N \times B`` system Power Transfer Distribution Factors (PTDF) matrix, and ``\text{Bal}_{i,t}`` is the active power bus balance expression (i.e. ``\text{Generation}_{i,t} - \text{Demand}_{i,t}``) at bus ``i`` at time-step ``t``. + --- -## `StaticBranchUnbounded` +## `StaticBranchUnbounded` `PTDFPowerModel` Network model ```@docs StaticBranchUnbounded ``` +- [`FlowActivePowerVariable`](@ref): + - Bounds: ``(-\infty,\infty)`` + - Symbol: ``f`` + + +**Objective:** + +No cost is added to the objective function. + +**Expressions:** + +No expressions are used. + +**Constraints:** + +For each branch ``b \in \{1,\dots, B\}`` (in a system with ``N`` buses) the constraints are given by: + +```math +\begin{aligned} +& f_t = \sum_{i=1}^N \text{PTDF}_{i,b} \cdot \text{Bal}_{i,t}, \quad \forall t \in \{1,\dots, T\} +\end{aligned} +``` +on which ``\text{PTDF}`` is the ``N \times B`` system Power Transfer Distribution Factors (PTDF) matrix, and ``\text{Bal}_{i,t}`` is the active power bus balance expression (i.e. ``\text{Generation}_{i,t} - \text{Demand}_{i,t}``) at bus ``i`` at time-step ``t``. + --- ## `HVDCTwoTerminalLossless` @@ -65,3 +141,24 @@ HVDCTwoTerminalDispatch ```@docs HVDCTwoTerminalUnbounded ``` + +--- + +## Valid configurations + +Valid `DeviceModel`s for subtypes of `Branch` include the following: + +```@eval +using PowerSimulations +using PowerSystems +using DataFrames +using Latexify +combos = PowerSimulations.generate_device_formulation_combinations() +filter!(x -> x["device_type"] <: Branch, combos) +combo_table = DataFrame( + "Valid DeviceModel" => ["`DeviceModel($(c["device_type"]), $(c["formulation"]))`" for c in combos], + "Device Type" => ["[$(c["device_type"])](https://nrel-Sienna.github.io/PowerSystems.jl/stable/model_library/generated_$(c["device_type"])/)" for c in combos], + "Formulation" => ["[$(c["formulation"])](@ref)" for c in combos], + ) +mdtable(combo_table, latex = false) +``` \ No newline at end of file From 21f8ab87519d4b7d724e701e582ad91d519818fa Mon Sep 17 00:00:00 2001 From: rodrigomha Date: Mon, 29 Apr 2024 12:17:14 -0700 Subject: [PATCH 20/69] export slack flow variables --- docs/src/api/PowerSimulations.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/src/api/PowerSimulations.md b/docs/src/api/PowerSimulations.md index c038a7d6c1..b83fbebd8d 100644 --- a/docs/src/api/PowerSimulations.md +++ b/docs/src/api/PowerSimulations.md @@ -134,6 +134,8 @@ ReservationVariable ```@docs FlowActivePowerVariable +FlowActivePowerSlackUpperBound +FlowActivePowerSlackLowerBound FlowActivePowerFromToVariable FlowActivePowerToFromVariable FlowReactivePowerFromToVariable From 7a071a0330d0056adcee9376400a953c6faa5f09 Mon Sep 17 00:00:00 2001 From: rodrigomha Date: Mon, 29 Apr 2024 17:53:03 -0700 Subject: [PATCH 21/69] fix typo --- docs/src/formulation_library/Introduction.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/formulation_library/Introduction.md b/docs/src/formulation_library/Introduction.md index 3ddfa205e6..cbbf66be13 100644 --- a/docs/src/formulation_library/Introduction.md +++ b/docs/src/formulation_library/Introduction.md @@ -52,7 +52,7 @@ Each `DeviceModel` formulation is described in specific in their respective page \end{align*} ``` -Note that the `StaticPowerLoad` does not impose any cost to the objective function or any constraint, but add its power demand to the supply-balance demand of the `CopperPlatePowerModel` used. Since we are using the `ThermalDispatchNoMin` formulation for the thermal generation, the lower bound for the power is 0, instead of ``P^\text{th,min}``. In addition, we are assuming a linear cost ``c^\text{th}``. Finally, the `RenewableFullDispatch` formulation allows the dispatch of the renewable unit to be between 0 and its maximum injection time series ``p_t^\text{re,param}``. +Note that the `StaticPowerLoad` does not impose any cost to the objective function or any constraint, but add its power demand to the supply-balance demand of the `CopperPlatePowerModel` used. Since we are using the `ThermalDispatchNoMin` formulation for the thermal generation, the lower bound for the power is 0, instead of ``P^\text{th,min}``. In addition, we are assuming a linear cost ``C^\text{th}``. Finally, the `RenewableFullDispatch` formulation allows the dispatch of the renewable unit to be between 0 and its maximum injection time series ``p_t^\text{re,param}``. # Nomenclature From 35dbe3a6c269b7126da60bfb4e490826cf732715 Mon Sep 17 00:00:00 2001 From: rodrigomha Date: Mon, 29 Apr 2024 17:54:10 -0700 Subject: [PATCH 22/69] update docstrings of branch variables --- src/core/variables.jl | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/core/variables.jl b/src/core/variables.jl index 6dca0be96a..be09f62445 100644 --- a/src/core/variables.jl +++ b/src/core/variables.jl @@ -106,14 +106,14 @@ struct ReactivePowerVariable <: VariableType end """ Struct to dispatch the creation of binary storage charge reservation variable -Docs abbreviation: ``r`` +Docs abbreviation: ``u^\\text{st}`` """ struct ReservationVariable <: VariableType end """ Struct to dispatch the creation of Active Power Reserve Variables -Docs abbreviation: ``Pr`` +Docs abbreviation: ``r`` """ struct ActivePowerReserveVariable <: VariableType end @@ -204,28 +204,28 @@ struct FlowActivePowerVariable <: VariableType end """ Struct to dispatch the creation of unidirectional Active Power Flow Variables -Docs abbreviation: ``\\overrightarrow{P}`` +Docs abbreviation: ``f^\\text{from-to}`` """ struct FlowActivePowerFromToVariable <: VariableType end """ Struct to dispatch the creation of unidirectional Active Power Flow Variables -Docs abbreviation: ``\\overleftarrow{P}`` +Docs abbreviation: ``f^\\text{to-from}`` """ struct FlowActivePowerToFromVariable <: VariableType end """ Struct to dispatch the creation of unidirectional Reactive Power Flow Variables -Docs abbreviation: ``\\overrightarrow{Q}`` +Docs abbreviation: ``f^\\text{q,from-to}`` """ struct FlowReactivePowerFromToVariable <: VariableType end """ Struct to dispatch the creation of unidirectional Reactive Power Flow Variables -Docs abbreviation: ``\\overleftarrow{Q}`` +Docs abbreviation: ``f^\\text{q,to-from}`` """ struct FlowReactivePowerToFromVariable <: VariableType end @@ -240,14 +240,14 @@ struct PhaseShifterAngle <: VariableType end """ Struct to dispatch the creation of HVDC Losses Auxiliary Variables -Docs abbreviation: TODO +Docs abbreviation: ``\\ell`` """ struct HVDCLosses <: VariableType end """ Struct to dispatch the creation of HVDC Flow Direction Auxiliary Variables -Docs abbreviation: TODO +Docs abbreviation: ``u^\\text{dir}`` """ struct HVDCFlowDirectionVariable <: VariableType end From 008067759f7b12e66470665e216af8369d442dfb Mon Sep 17 00:00:00 2001 From: rodrigomha Date: Mon, 29 Apr 2024 17:54:18 -0700 Subject: [PATCH 23/69] update export of branch variables --- src/PowerSimulations.jl | 1 + 1 file changed, 1 insertion(+) diff --git a/src/PowerSimulations.jl b/src/PowerSimulations.jl index c62b830d9b..b1273831cc 100644 --- a/src/PowerSimulations.jl +++ b/src/PowerSimulations.jl @@ -271,6 +271,7 @@ export FlowReactivePowerToFromConstraint export FrequencyResponseConstraint export HVDCPowerBalance export HVDCLosses +export HVDCFlowDirectionVariable export InputActivePowerVariableLimitsConstraint export NetworkFlowConstraint export NodalBalanceActiveConstraint From a54f7b536c5eba40e8743062656569ddf2dbf662 Mon Sep 17 00:00:00 2001 From: rodrigomha Date: Mon, 29 Apr 2024 17:54:23 -0700 Subject: [PATCH 24/69] finish branch docs --- docs/src/formulation_library/Branch.md | 143 +++++++++++++++++++++++-- 1 file changed, 134 insertions(+), 9 deletions(-) diff --git a/docs/src/formulation_library/Branch.md b/docs/src/formulation_library/Branch.md index 79c6d04766..2ef17527de 100644 --- a/docs/src/formulation_library/Branch.md +++ b/docs/src/formulation_library/Branch.md @@ -43,8 +43,8 @@ For each branch ``b \in \{1,\dots, B\}`` (in a system with ``N`` buses) the cons ```math \begin{aligned} & f_t = \sum_{i=1}^N \text{PTDF}_{i,b} \cdot \text{Bal}_{i,t}, \quad \forall t \in \{1,\dots, T\}\\ -& f_t - f_t^\text{sl,up} \le R^\text{max} \\ -& f_t + f_t^\text{sl,lo} \le -R^\text{max} \\ +& f_t - f_t^\text{sl,up} \le R^\text{max},\quad \forall t \in \{1,\dots, T\} \\ +& f_t + f_t^\text{sl,lo} \le -R^\text{max},\quad \forall t \in \{1,\dots, T\} \\ \end{aligned} ``` on which ``\text{PTDF}`` is the ``N \times B`` system Power Transfer Distribution Factors (PTDF) matrix, and ``\text{Bal}_{i,t}`` is the active power bus balance expression (i.e. ``\text{Generation}_{i,t} - \text{Demand}_{i,t}``) at bus ``i`` at time-step ``t``. @@ -88,7 +88,7 @@ on which ``\text{PTDF}`` is the ``N \times B`` system Power Transfer Distributio --- -## `StaticBranchUnbounded` `PTDFPowerModel` Network model +## `StaticBranchUnbounded` for `PTDFPowerModel` Network model ```@docs StaticBranchUnbounded @@ -120,28 +120,153 @@ on which ``\text{PTDF}`` is the ``N \times B`` system Power Transfer Distributio --- -## `HVDCTwoTerminalLossless` +## `HVDCTwoTerminalUnbounded` for `PTDFPowerModel` Network model ```@docs -HVDCTwoTerminalLossless +HVDCTwoTerminalUnbounded ``` +This model assumes that it can transfer power from two AC buses without losses and no limits. + +**Variables:** + +- [`FlowActivePowerVariable`](@ref): + - Bounds: ``\left(-\infty,\infty\right)`` + - Symbol: ``f`` + + +**Objective:** + +No cost is added to the objective function. + +**Expressions:** + +The variable `FlowActivePowerVariable` ``f`` is added to the nodal balance expression `ActivePowerBalance`, by adding the flow ``f`` in the receiving bus and subtracting it from the sending bus. This is used then to compute the AC flows using the PTDF equation. + +**Constraints:** + +No constraints are added. + + --- -## `HVDCTwoTerminalDispatch` +## `HVDCTwoTerminalLossless` for `PTDFPowerModel` Network model ```@docs -HVDCTwoTerminalDispatch +HVDCTwoTerminalLossless +``` + +This model assumes that it can transfer power from two AC buses without losses. + +**Variables:** + +- [`FlowActivePowerVariable`](@ref): + - Bounds: ``\left(-\infty,\infty\right)`` + - Symbol: ``f`` + + +**Static Parameters** + +- ``R^\text{from,min}`` = `PowerSystems.get_active_power_limits_from(branch).min` +- ``R^\text{from,max}`` = `PowerSystems.get_active_power_limits_from(branch).max` +- ``R^\text{to,min}`` = `PowerSystems.get_active_power_limits_to(branch).min` +- ``R^\text{to,max}`` = `PowerSystems.get_active_power_limits_to(branch).max` + +**Objective:** + +No cost is added to the objective function. + +**Expressions:** + +The variable `FlowActivePowerVariable` ``f`` is added to the nodal balance expression `ActivePowerBalance`, by adding the flow ``f`` in the receiving bus and subtracting it from the sending bus. This is used then to compute the AC flows using the PTDF equation. + +**Constraints:** + +```math +\begin{align*} +& R^\text{min} \le f_t \le R^\text{max},\quad \forall t \in \{1,\dots, T\} \\ +\end{align*} +``` +where: +```math +\begin{align*} +& R^\text{min} = \begin{cases} + \min\left(R^\text{from,min}, R^\text{to,min}\right), & \text{if } R^\text{from,min} \ge 0 \text{ and } R^\text{to,min} \ge 0 \\ + \max\left(R^\text{from,min}, R^\text{to,min}\right), & \text{if } R^\text{from,min} \le 0 \text{ and } R^\text{to,min} \le 0 \\ + R^\text{from,min},& \text{if } R^\text{from,min} \le 0 \text{ and } R^\text{to,min} \ge 0 \\ + R^\text{to,min},& \text{if } R^\text{from,min} \ge 0 \text{ and } R^\text{to,min} \le 0 + \end{cases} +\end{align*} +``` +and +```math +\begin{align*} +& R^\text{max} = \begin{cases} + \min\left(R^\text{from,max}, R^\text{to,max}\right), & \text{if } R^\text{from,max} \ge 0 \text{ and } R^\text{to,max} \ge 0 \\ + \max\left(R^\text{from,max}, R^\text{to,max}\right), & \text{if } R^\text{from,max} \le 0 \text{ and } R^\text{to,max} \le 0 \\ + R^\text{from,max},& \text{if } R^\text{from,max} \le 0 \text{ and } R^\text{to,max} \ge 0 \\ + R^\text{to,max},& \text{if } R^\text{from,max} \ge 0 \text{ and } R^\text{to,max} \le 0 + \end{cases} +\end{align*} ``` --- -## `HVDCTwoTerminalUnbounded` + +## `HVDCTwoTerminalDispatch` for `PTDFPowerModel` Network model + ```@docs -HVDCTwoTerminalUnbounded +HVDCTwoTerminalDispatch ``` +**Variables** + +- [`FlowActivePowerToFromVariable`](@ref): + - Symbol: ``f^\text{to-from}`` +- [`FlowActivePowerFromToVariable`](@ref): + - Symbol: ``f^\text{from-to}`` +- [`HVDCLosses`](@ref): + - Symbol: ``\ell`` +- [`HVDCFlowDirectionVariable`](@ref) + - Bounds: ``\{0,1\}`` + - Symbol: ``u^\text{dir}`` + +**Static Parameters** + +- ``R^\text{from,min}`` = `PowerSystems.get_active_power_limits_from(branch).min` +- ``R^\text{from,max}`` = `PowerSystems.get_active_power_limits_from(branch).max` +- ``R^\text{to,min}`` = `PowerSystems.get_active_power_limits_to(branch).min` +- ``R^\text{to,max}`` = `PowerSystems.get_active_power_limits_to(branch).max` +- ``L_0`` = `PowerSystems.get_loss(branch).l0` +- ``L_1`` = `PowerSystems.get_loss(branch).l1` + +**Objective:** + +No cost is added to the objective function. + +**Expressions:** + +Each `FlowActivePowerToFromVariable` ``f^\text{to-from}`` and `FlowActivePowerFromToVariable` ``f^\text{from-to}`` is added to the nodal balance expression `ActivePowerBalance`, by adding the respective flow in the receiving bus and subtracting it from the sending bus. That is, ``f^\text{to-from}`` adds the flow to the `from` bus, and subtracts the flow from the `to` bus, while ``f^\text{from-to}`` adds the flow to the `to` bus, and subtracts the flow from the `from` bus This is used then to compute the AC flows using the PTDF equation. + +In addition, the `HVDCLosses` are subtracted to the `from` bus in the `ActivePowerBalance` expression. + +**Constraints:** + +```math +\begin{align*} +& R^\text{from,min} \le f_t^\text{from-to} \le R^\text{from,max}, \forall t \in \{1,\dots, T\} \\ +& R^\text{to,min} \le f_t^\text{to-from} \le R^\text{to,max},\quad \forall t \in \{1,\dots, T\} \\ +& f_t^\text{to-from} - f_t^\text{from-to} \le L_1 \cdot f_t^\text{to-from} - L_0,\quad \forall t \in \{1,\dots, T\} \\ +& f_t^\text{from-to} - f_t^\text{to-from} \ge L_1 \cdot f_t^\text{from-to} + L_0,\quad \forall t \in \{1,\dots, T\} \\ +& f_t^\text{from-to} - f_t^\text{to-from} \ge - M^\text{big} (1 - u^\text{dir}_t),\quad \forall t \in \{1,\dots, T\} \\ +& f_t^\text{to-from} - f_t^\text{from-to} \ge - M^\text{big} u^\text{dir}_t,\quad \forall t \in \{1,\dots, T\} \\ +& f_t^\text{to-from} - f_t^\text{from-to} \le \ell_t,\quad \forall t \in \{1,\dots, T\} \\ +& f_t^\text{from-to} - f_t^\text{to-from} \le \ell_t,\quad \forall t \in \{1,\dots, T\} +\end{align*} +``` + + --- ## Valid configurations From 2676003f2d11170bb34bd4223f6f09e7fd9dd7b5 Mon Sep 17 00:00:00 2001 From: rodrigomha Date: Tue, 30 Apr 2024 12:35:10 -0700 Subject: [PATCH 25/69] update pwl docstring --- src/core/variables.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/variables.jl b/src/core/variables.jl index be09f62445..aeeafe6304 100644 --- a/src/core/variables.jl +++ b/src/core/variables.jl @@ -254,7 +254,7 @@ struct HVDCFlowDirectionVariable <: VariableType end """ Struct to dispatch the creation of piecewise linear cost variables for objective function -Docs abbreviation: TODO +Docs abbreviation: ``\\delta`` """ struct PieceWiseLinearCostVariable <: VariableType end From 28514e71124daee030586cf550c904894958cc18 Mon Sep 17 00:00:00 2001 From: rodrigomha Date: Tue, 30 Apr 2024 12:35:22 -0700 Subject: [PATCH 26/69] update old docs --- docs/src/formulation_library/General.md | 45 ++++------------------ docs/src/formulation_library/ThermalGen.md | 2 +- 2 files changed, 9 insertions(+), 38 deletions(-) diff --git a/docs/src/formulation_library/General.md b/docs/src/formulation_library/General.md index 71e3e552e6..ae594bddb6 100644 --- a/docs/src/formulation_library/General.md +++ b/docs/src/formulation_library/General.md @@ -15,11 +15,11 @@ No variables are created for `DeviceModel(<:DeviceType, FixedOutput)` **Static Parameters:** - ThermalGen: - - ``Pg^\text{max}`` = `PowerSystems.get_max_active_power(device)` - - ``Qg^\text{max}`` = `PowerSystems.get_max_reactive_power(device)` + - ``P^\text{th,max}`` = `PowerSystems.get_max_active_power(device)` + - ``Q^\text{th,max}`` = `PowerSystems.get_max_reactive_power(device)` - Storage: - - ``Pg^\text{max}`` = `PowerSystems.get_max_active_power(device)` - - ``Qg^\text{max}`` = `PowerSystems.get_max_reactive_power(device)` + - ``P^\text{st,max}`` = `PowerSystems.get_max_active_power(device)` + - ``Q^\text{st,max}`` = `PowerSystems.get_max_reactive_power(device)` **Time Series Parameters:** @@ -48,7 +48,7 @@ No objective terms are created for `DeviceModel(<:DeviceType, FixedOutput)` **Expressions:** -Adds the active and reactive parameters listed for specific device types above to the respective active and reactive power balance expressions created by the selected [Network Formulations](@ref network_formulations) +Adds the active and reactive parameters listed for specific device types above to the respective active and reactive power balance expressions created by the selected [Network Formulations](@ref network_formulations). **Constraints:** @@ -56,42 +56,13 @@ No constraints are created for `DeviceModel(<:DeviceType, FixedOutput)` --- -## `VariableCost` Options +## `Cost` Options -PowerSimulations can represent variable costs using a variety of different methods depending on the data available in each device. The following describes the objective function terms that are populated for each variable cost option. - -### Scalar `VariableCost` - -`variable_cost <: Float64`: creates a fixed marginal cost term in the objective function - -```math -\begin{aligned} -& \text{min} \sum_{t} C * G_t -\end{aligned} -``` - -### Polynomial `VariableCost` - -`variable_cost <: Tuple{Float64, Float64}`: creates a polynomial cost term in the objective function where - -- ``C_g``=`variable_cost[1]` -- ``C_g^\prime``=`variable_cost[2]` - -```math -\begin{aligned} -& \text{min} \sum_{t} C * G_t + C^\prime * G_t^2 -\end{aligned} -``` - -### Piecewise Linear `VariableCost` - -`variable_cost <: Vector{Tuple{Float64, Float64}}`: creates a piecewise linear cost term in the objective function - -TODO: add formulation +TODO ___ -### `StorageManagementCost` +## `StorageManagementCost` Adds an objective function cost term according to: diff --git a/docs/src/formulation_library/ThermalGen.md b/docs/src/formulation_library/ThermalGen.md index e6625fdcb6..5d158ab317 100644 --- a/docs/src/formulation_library/ThermalGen.md +++ b/docs/src/formulation_library/ThermalGen.md @@ -4,7 +4,7 @@ Thermal generation formulations define the optimization models that describe the !!! note - Thermal units can include multiple terms added to the objective function, such as no-load cost, turn-on/off cost, fixed cost and variable cost. In addition, variable costs can be linear, quadratic or piecewise-linear formulations. These methods are properly described in the cost function document: TODO. + Thermal units can include multiple terms added to the objective function, such as no-load cost, turn-on/off cost, fixed cost and variable cost. In addition, variable costs can be linear, quadratic or piecewise-linear formulations. These methods are properly described in the [cost function page](@ref pwl_cost). !!! note From edb65c578399a51c600f37fe82046ec328afeec5 Mon Sep 17 00:00:00 2001 From: rodrigomha Date: Tue, 30 Apr 2024 12:35:29 -0700 Subject: [PATCH 27/69] add pwl docs --- docs/make.jl | 1 + docs/src/formulation_library/Piecewise.md | 44 +++++++++++++++++++++++ 2 files changed, 45 insertions(+) create mode 100644 docs/src/formulation_library/Piecewise.md diff --git a/docs/make.jl b/docs/make.jl index 1537f13100..f90d00bf00 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -39,6 +39,7 @@ pages = OrderedDict( "Load" => "formulation_library/Load.md", "Branch" => "formulation_library/Branch.md", "Services" => "formulation_library/Service.md", + "Piecewise Linear Cost" => "formulation_library/Piecewise.md", ], "API Reference" => "api/PowerSimulations.md", ) diff --git a/docs/src/formulation_library/Piecewise.md b/docs/src/formulation_library/Piecewise.md new file mode 100644 index 0000000000..387580db0a --- /dev/null +++ b/docs/src/formulation_library/Piecewise.md @@ -0,0 +1,44 @@ +# [Piecewise linear cost functions](@id pwl_cost) + +The choice for piecewise-linear (PWL) cost representation in `PowerSimulations.jl` is equivalent to the so-called λ-model from the paper [_The Impacts of Convex Piecewise Linear Cost Formulations on AC Optimal Power Flow_](https://www.sciencedirect.com/science/article/pii/S0378779621001723). The SOS constraints in each model are only implemented if the data for PWL is not convex. + +## Special Ordered Set (SOS) Constraints + +A special ordered set (SOS) is an ordered set of variables used as an additional way to specify integrality conditions in an optimization model. + +- Special Ordered Sets of type 1 (SOS1) are a set of variables, at most one of which can take a non-zero value, all others being at 0. They most frequently applications is in a a set of variables that are actually binary variables: in other words, we have to choose at most one from a set of possibilities. +- Special Ordered Sets of type 2 (SOS2) are an ordered set of non-negative variables, of which at most two can be non-zero, and if two are non-zero these must be consecutive in their ordering. Special Ordered Sets of type 2 are typically used to model non-linear functions of a variable in a linear model, such as non-convex quadratic functions using PWL functions. + +## Standard representation of PWL costs + +Piecewise-linear costs are defined by a sequence of points representing the line segments for each generator: ``(P_k^\text{max}, C_k)`` on which we assume ``C_k`` is the cost of generating ``P_k^\text{max}`` power, and ``k \in \{1,\dots, K\}`` are the number of segments each generator cost function has. + +### Commitment formulation + + With this the standard representation of PWL costs for a thermal unit commitment is given by: + +```math +\begin{align*} + \min_{\substack{p_{t}, \delta_{k,t}}} + & \sum_{t \in \mathcal{T}} \left(\sum_{k \in \mathcal{K}} C_{k,t} \delta_{k,t} \right) \Delta t\\ + & \sum_{k \in \mathcal{K}} P_{k}^{\text{max}} \delta_{k,t} = p_{t} & \forall t \in \mathcal{T}\\ + & \sum_{k \in \mathcal{K}} \delta_{k,t} = u_{t} & \forall t \in \mathcal{T}\\ + & P^{\text{min}} u_{t} \leq p_{t} \leq P^{\text{max}} u_{t} & \forall t \in \mathcal{T}\\ + &\left \{\delta_{1,t}, \dots, \delta_{K,t} \right \} \in \text{SOS}_{2} & \forall t \in \mathcal{T} +\end{align*} +``` +on which ``\delta_{k,t} \in [0,1]`` is the interpolation variable, ``p`` is the active power of the generator and ``u \in \{0,1\}`` is the commitment variable of the generator. In the case of a PWL convex costs, i.e. increasing slopes, the SOS constraint is omitted. + +### Dispatch formulation + +```math +\begin{align*} + \min_{\substack{p_{t}, \delta_{k,t}}} + & \sum_{t \in \mathcal{T}} \left(\sum_{k \in \mathcal{K}} C_{k,t} \delta_{k,t} \right) \Delta t\\ + & \sum_{k \in \mathcal{K}} P_{k}^{\text{max}} \delta_{k,t} = p_{t} & \forall t \in \mathcal{T}\\ + & \sum_{k \in \mathcal{K}} \delta_{k,t} = \text{on}_{t} & \forall t \in \mathcal{T}\\ + & P^{\text{min}} \text{on}_{t} \leq p_{t} \leq P^{\text{max}} \text{on}_{t} & \forall t \in \mathcal{T}\\ + &\left \{\delta_{i,t}, \dots, \delta_{k,t} \right \} \in \text{SOS}_{2} & \forall t \in \mathcal{T} +\end{align*} +``` +on which ``\delta_{k,t} \in [0,1]`` is the interpolation variable, ``p`` is the active power of the generator and ``\text{on} \in \{0,1\}`` is the parameter that decides if the generator is available or not. In the case of a PWL convex costs, i.e. increasing slopes, the SOS constraint is omitted. \ No newline at end of file From 9898bc29ee211c426389d05fd46f413c5c180890 Mon Sep 17 00:00:00 2001 From: rodrigomha Date: Tue, 30 Apr 2024 16:29:50 -0700 Subject: [PATCH 28/69] add note to PWL docs --- docs/src/formulation_library/Piecewise.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/src/formulation_library/Piecewise.md b/docs/src/formulation_library/Piecewise.md index 387580db0a..d8511ad7ff 100644 --- a/docs/src/formulation_library/Piecewise.md +++ b/docs/src/formulation_library/Piecewise.md @@ -13,6 +13,9 @@ A special ordered set (SOS) is an ordered set of variables used as an additional Piecewise-linear costs are defined by a sequence of points representing the line segments for each generator: ``(P_k^\text{max}, C_k)`` on which we assume ``C_k`` is the cost of generating ``P_k^\text{max}`` power, and ``k \in \{1,\dots, K\}`` are the number of segments each generator cost function has. +!!!note + `PowerSystems` has more options to specify cost functions for each thermal unit. Independent of which form of the cost data is provided, `PowerSimulations.jl` will internally transform the data to use the λ-model formulation. See TODO: ADD PSY COST DOCS for more information. + ### Commitment formulation With this the standard representation of PWL costs for a thermal unit commitment is given by: From 63f58e9f66a00e4ead85668d2d2b337d0f86faa3 Mon Sep 17 00:00:00 2001 From: rodrigomha Date: Tue, 30 Apr 2024 16:30:00 -0700 Subject: [PATCH 29/69] add ref to formulation intro --- docs/src/formulation_library/Introduction.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/formulation_library/Introduction.md b/docs/src/formulation_library/Introduction.md index cbbf66be13..ef867b8297 100644 --- a/docs/src/formulation_library/Introduction.md +++ b/docs/src/formulation_library/Introduction.md @@ -1,4 +1,4 @@ -# Formulations Introduction +# [Formulations Introduction](@id formulation_intro) PowerSimulations.jl enables modularity in its formulations by assigning a `DeviceModel` to each `PowerSystems.jl` component type existing in a defined system. From 13132c1313a9ac8b0be5844bc44f30e6218c7a5c Mon Sep 17 00:00:00 2001 From: rodrigomha Date: Tue, 30 Apr 2024 16:30:13 -0700 Subject: [PATCH 30/69] add debugging models docs --- .../debugging_infeasible_models.md | 164 ++++++++++++++++++ 1 file changed, 164 insertions(+) diff --git a/docs/src/modeler_guide/debugging_infeasible_models.md b/docs/src/modeler_guide/debugging_infeasible_models.md index c96c7ff78b..711818eb72 100644 --- a/docs/src/modeler_guide/debugging_infeasible_models.md +++ b/docs/src/modeler_guide/debugging_infeasible_models.md @@ -5,4 +5,168 @@ Getting infeasible solutions to models is a common occurrence in operations simu ## Adding slacks to the model +One of the most common infeasibility issues observed is due to not enough generation to supply demand, or conversely, excessive fixed (non-curtailable) generation in a low demand scenario. + +The recommended solution for any of these cases is adding slack variables to the network model, for example: + +```julia +template_uc = ProblemTemplate( + NetworkModel( + CopperPlatePowerModel, + use_slacks=true, + ), + ) +``` +will add slack variables to the `ActivePowerBalance` expression. + +In this case, if the problem is now feasible, the user can check the solution of the variables `SystemBalanceSlackUp` and `SystemBalanceSlackDown`, and if one value is greater than zero, it represents that not enough generation (for Slack Up) or not enough demand (for Slack Down) in the optimization problem. + +### Services cases + +In many scenarios, certain units are also required to provide reserve requirements, e.g. thermal units mandated to provide up-regulation. In such scenarios, it is also possible to add slack variables, by specifying the service model (`RangeReserve`) for the specific service type (`VariableReserve{ReserveUp}`) as: +```julia +set_service_model!( + template_uc, + ServiceModel( + VariableReserve{ReserveUp}, + RangeReserve; + use_slacks=true + ), +) +``` +Again, if the problem is now feasible, check the solution of `ReserveRequirementSlack` variable, and if it is larger than zero in a specific time-step, then it is evidence that there is not enough reserve available to satisfy the requirement. + ## Getting the infeasibility conflict + +Some solvers allows to identify which constraints and variables are producing the infeasibility, by finding the irreducible infeasible set (IIS), that is the subset of constraints and variable bounds that will become feasible if any single constraint or variable bound is removed. + +To enable this feature in `PowerSimulations` the keyword argument `calculate_conflict` must be set to `true`, when creating the `DecisionModel`. Note that not all solvers allow the computation of the IIS, but most commercial solvers have this capability. It is also recommended to enable the keyword argument `store_variable_names=true` to help understanding which variables are with infeasibility issues. + +The following code creates a decision model with the `Xpress` optimizer, and enabling the `calculate_conflict=true` keyword argument. + +```julia +DecisionModel( + template_ed, + sys_rts_rt; + name="ED", + optimizer=optimizer_with_attributes(Xpress.Optimizer, "MIPRELSTOP" => 1e-2), + optimizer_solve_log_print=true, + calculate_conflict=true, + store_variable_names=true, +) +``` + +Here is an example on how the IIS will be displayed as: + +```raw +Error: Constraints participating in conflict basis (IIS) +│ +│ ┌──────────────────────────────────────┐ +│ │ CopperPlateBalanceConstraint__System │ +│ ├──────────────────────────────────────┤ +│ │ (113, 26) │ +│ └──────────────────────────────────────┘ +│ ┌──────────────────────────────────┐ +│ │ EnergyAssetBalance__HybridSystem │ +│ ├──────────────────────────────────┤ +│ │ ("317_Hybrid", 26) │ +│ └──────────────────────────────────┘ +│ ┌─────────────────────────────────────────────┐ +│ │ PieceWiseLinearCostConstraint__HybridSystem │ +│ ├─────────────────────────────────────────────┤ +│ │ ("317_Hybrid", 26) │ +│ └─────────────────────────────────────────────┘ +│ ┌────────────────────────────────────────────────┐ +│ │ PieceWiseLinearCostConstraint__ThermalStandard │ +│ ├────────────────────────────────────────────────┤ +│ │ ("202_STEAM_3", 26) │ +│ │ ("101_STEAM_3", 26) │ +│ │ ("118_CC_1", 26) │ +│ │ ("202_STEAM_4", 26) │ +│ │ ("315_CT_6", 26) │ +│ │ ("201_STEAM_3", 26) │ +│ │ ("102_STEAM_4", 26) │ +│ └────────────────────────────────────────────────┘ +│ ┌──────────────────────────────────────────────────────────────────────┐ +│ │ ActivePowerVariableTimeSeriesLimitsConstraint__RenewableDispatch__ub │ +│ ├──────────────────────────────────────────────────────────────────────┤ +│ │ ("122_WIND_1", 26) │ +│ │ ("324_PV_3", 26) │ +│ │ ("312_PV_1", 26) │ +│ │ ("102_PV_1", 26) │ +│ │ ("101_PV_1", 26) │ +│ │ ("324_PV_2", 26) │ +│ │ ("313_PV_2", 26) │ +│ │ ("104_PV_1", 26) │ +│ │ ("101_PV_2", 26) │ +│ │ ("309_WIND_1", 26) │ +│ │ ("310_PV_2", 26) │ +│ │ ("113_PV_1", 26) │ +│ │ ("314_PV_1", 26) │ +│ │ ("324_PV_1", 26) │ +│ │ ("103_PV_1", 26) │ +│ │ ("303_WIND_1", 26) │ +│ │ ("314_PV_2", 26) │ +│ │ ("102_PV_2", 26) │ +│ │ ("314_PV_3", 26) │ +│ │ ("320_PV_1", 26) │ +│ │ ("101_PV_3", 26) │ +│ │ ("319_PV_1", 26) │ +│ │ ("314_PV_4", 26) │ +│ │ ("310_PV_1", 26) │ +│ │ ("215_PV_1", 26) │ +│ │ ("313_PV_1", 26) │ +│ │ ("101_PV_4", 26) │ +│ │ ("119_PV_1", 26) │ +│ └──────────────────────────────────────────────────────────────────────┘ +│ ┌─────────────────────────────────────────────────────────────────────────────┐ +│ │ FeedforwardSemiContinousConstraint__ThermalStandard__ActivePowerVariable_ub │ +│ ├─────────────────────────────────────────────────────────────────────────────┤ +│ │ ("322_CT_6", 26) │ +│ │ ("321_CC_1", 26) │ +│ │ ("223_CT_4", 26) │ +│ │ ("213_CT_1", 26) │ +│ │ ("223_CT_6", 26) │ +│ │ ("123_CT_1", 26) │ +│ │ ("113_CT_3", 26) │ +│ │ ("302_CT_3", 26) │ +│ │ ("215_CT_4", 26) │ +│ │ ("301_CT_4", 26) │ +│ │ ("113_CT_2", 26) │ +│ │ ("221_CC_1", 26) │ +│ │ ("223_CT_5", 26) │ +│ │ ("315_CT_7", 26) │ +│ │ ("215_CT_5", 26) │ +│ │ ("113_CT_1", 26) │ +│ │ ("307_CT_2", 26) │ +│ │ ("213_CT_2", 26) │ +│ │ ("113_CT_4", 26) │ +│ │ ("218_CC_1", 26) │ +│ │ ("213_CC_3", 26) │ +│ │ ("323_CC_2", 26) │ +│ │ ("322_CT_5", 26) │ +│ │ ("207_CT_2", 26) │ +│ │ ("123_CT_5", 26) │ +│ │ ("123_CT_4", 26) │ +│ │ ("207_CT_1", 26) │ +│ │ ("301_CT_3", 26) │ +│ │ ("302_CT_4", 26) │ +│ │ ("307_CT_1", 26) │ +│ └─────────────────────────────────────────────────────────────────────────────┘ +│ ┌───────────────────────────────────────────────────────┐ +│ │ RenewableActivePowerLimitConstraint__HybridSystem__ub │ +│ ├───────────────────────────────────────────────────────┤ +│ │ ("317_Hybrid", 26) │ +│ └───────────────────────────────────────────────────────┘ +│ ┌───────────────────────────────────────┐ +│ │ ThermalOnVariableUb__HybridSystem__ub │ +│ ├───────────────────────────────────────┤ +│ │ ("317_Hybrid", 26) │ +│ └───────────────────────────────────────┘ + + Error: Serializing Infeasible Problem at /var/folders/1v/t69qyl0n5059n6c1nn7sp8zm7g8s6z/T/jl_jNSREb/compact_sim/problems/ED/infeasible_ED_2020-10-06T15:00:00.json +``` + +Note that the IIS clearly identify that the issue is happening at time step 26, and constraints are related with the `CopperPlateBalanceConstraint__System`, with multiple upper bound constraints, for the hybrid system, renewable units and thermal units. This highlights that there may not be enough generation in the system. Indeed, by enabling system slacks, the problem become feasible. + +Finally, the infeasible model is exported in a `json` file that can be loaded directly in `JuMP` to be explored. More information about this is [available here](https://jump.dev/JuMP.jl/stable/moi/submodules/FileFormats/overview/#Read-from-file). \ No newline at end of file From 8d7c6eec3569533d2b70d334d66fb2e5ec08edfb Mon Sep 17 00:00:00 2001 From: rodrigomha Date: Tue, 30 Apr 2024 16:30:28 -0700 Subject: [PATCH 31/69] update ref of templates --- docs/src/modeler_guide/problem_templates.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/modeler_guide/problem_templates.md b/docs/src/modeler_guide/problem_templates.md index 52eff0a56a..aea9e98c44 100644 --- a/docs/src/modeler_guide/problem_templates.md +++ b/docs/src/modeler_guide/problem_templates.md @@ -3,7 +3,7 @@ Templates are used to specify the modeling properties of the devices and network that are going to he used to specify a problem. A `ProblemTemplate` is just a collection of `DeviceModel`s that allows the user to specify the formulations of each set of devices (by device type) independently so that the modeler can adjust the level of detail according to the question of interest and the available data. -For more information about valid `DeviceModel`s and their mathematical representations, check out the [Formulation Library](@ref formulation_library). +For more information about valid `DeviceModel`s and their mathematical representations, check out the [Formulation Library](@ref formulation_intro). ## Building a `ProblemTemplate` From b237ffe1b63e41b1301f19f0c2e7aecde577c7d6 Mon Sep 17 00:00:00 2001 From: rodrigomha Date: Tue, 30 Apr 2024 16:30:41 -0700 Subject: [PATCH 32/69] complete finish a simulation docs --- .../src/modeler_guide/running_a_simulation.md | 103 +++++++++++++++++- 1 file changed, 102 insertions(+), 1 deletion(-) diff --git a/docs/src/modeler_guide/running_a_simulation.md b/docs/src/modeler_guide/running_a_simulation.md index 906bff25a2..e30ca026ce 100644 --- a/docs/src/modeler_guide/running_a_simulation.md +++ b/docs/src/modeler_guide/running_a_simulation.md @@ -7,10 +7,111 @@ Check out the [Operations Problem Tutorial](@ref op_problem_tutorial) ## Feedforward -TODO +The definition of exactly what information is passed using the defined chronologies is accomplished using FeedForwards. + +Specifically, a FeedForward is used to define what to do with information being passed with an inter-stage chronology in a Simulation. The most common FeedForward is the `SemiContinuousFeedForward` that affects the semi-continuous range constraints of thermal generators in the economic dispatch problems based on the value of the (already solved) unit-commitment variables. + +The creation of a FeedForward requires at least to specify the `component_type` on which the FeedForward will be applied. The `source` variable specify which variable will be taken from the problem solved, for example the commitment variable of the thermal unit in the unit commitment problem. Finally, the `affected_values` specify which variables will be affected in the problem to be solved, for example the next economic dispatch problem. + +The following code specify the creation of semi-continuous range constraints on the `ActivePowerVariable` based on the solution of the commitment variable `OnVariable` for all `ThermalStandard` units. + +```julia +SemiContinuousFeedforward( + component_type=ThermalStandard, + source=OnVariable, + affected_values=[ActivePowerVariable], +) +``` + +## Chronologies + +In PowerSimulations, chronologies define where information is flowing. There are two types +of chronologies. + +- inter-stage chronologies: Define how information flows between stages. e.g. day-ahead solutions are used to inform economic dispatch problems +- intra-stage chronologies: Define how information flows between multiple executions of a single stage. e.g. the dispatch setpoints of the first period of an economic dispatch problem are constrained by the ramping limits from setpoints in the final period of the previous problem. ## Sequencing In a typical simulation pipeline, we want to connect daily (24-hours) day-ahead unit commitment problems, with multiple economic dispatch problems. Usually, our day-ahead unit commitment problem will have an hourly (1-hour) resolution, while the economic dispatch will have a 5-minute resolution. Depending on your problem, it is common to use a 2-day look-ahead for unit commitment problems, so in this case, the Day-Ahead problem will have: resolution = Hour(1) with interval = Hour(24) and horizon = 48. In the case of the economic dispatch problem, it is common to use a look-ahead of two hours. Thus, the Real-Time problem will have: resolution = Minute(5), with interval = Minute(5) (we only store the first operating point) and horizon = 24 (24 time steps of 5 minutes are 120 minutes, that is 2 hours). + +## Simulation Setup + +The following code creates the entire simulation pipeline: + +```julia +# We assume that the templates for UC and ED are ready +# sys_da has the resolution of 1 hour: +# with the 24 hours interval and horizon of 48 hours. +# sys_rt has the resolution of 5 minutes: +# with a 5-minute interval and horizon of 2 hours (24 time steps) + +# Create the UC Decision Model +decision_model_uc = DecisionModel( + template_uc, + sys_da; + name="UC", + optimizer=optimizer_with_attributes( + Xpress.Optimizer, + "MIPRELSTOP" => 1e-1, + ), +) + +# Create the ED Decision Model +decision_model_ed = DecisionModel( + template_ed, + sys_rt; + name="ED", + optimizer=optimizer_with_attributes(Xpress.Optimizer), +) + +# Specify the SimulationModels using a Vector of decision_models: UC, ED +sim_models = SimulationModels( + decision_models=[ + decision_model_uc, + decision_model_ed, + ], +) + +# Create the FeedForwards: +semi_ff = SemiContinuousFeedforward( + component_type=ThermalStandard, + source=OnVariable, + affected_values=[ActivePowerVariable], +) + +# Specify the sequencing: +sim_sequence = SimulationSequence( + # Specify the vector of decision models: sim_models + models=sim_models, + # Specify a Dict of feedforwards on which the FF applies + # based on the DecisionModel name, in this case "ED" + feedforwards=Dict( + "ED" => [semi_ff], + ), + # Specify the chronology, in this case inter-stage + ini_cond_chronology=InterProblemChronology(), +) + +# Construct the simulation: +sim = Simulation( + name="compact_sim", + steps=10, # 10 days + models=sim_models, + sequence=sim_sequence, + # Specify the start_time as a DateTime: e.g. DateTime("2020-10-01T00:00:00") + initial_time=start_time, + # Specify a temporary folder to avoid storing logs if not needed + simulation_folder=mktempdir(cleanup=true), +) + +# Build the decision models and simulation setup +build!(sim) + +# Execute the simulation using the Optimizer specified in each DecisionModel +execute!(sim, enable_progress_bar=true) +``` + +Check the [PCM tutorial](@ref pcm_tutorial) for a more detailed tutorial on executing a simulation in a production cost modeling (PCM) environment. \ No newline at end of file From e336a1ef81ee5ff2cada2d4a5dc1b12268011324 Mon Sep 17 00:00:00 2001 From: rodrigomha Date: Tue, 30 Apr 2024 16:30:47 -0700 Subject: [PATCH 33/69] update definitions --- docs/src/modeler_guide/definitions.md | 43 +++++++++++++++++++++++++-- 1 file changed, 41 insertions(+), 2 deletions(-) diff --git a/docs/src/modeler_guide/definitions.md b/docs/src/modeler_guide/definitions.md index 8a4946f85f..6333424c4b 100644 --- a/docs/src/modeler_guide/definitions.md +++ b/docs/src/modeler_guide/definitions.md @@ -1,15 +1,44 @@ # Definitions +## A + +* *Attributes*: Certain device formulations can be customized by specifying attributes that will include/remove certain variables, expressions and/or constraints. For example, in `StorageSystemsSimulations.jl`, the device formulation of `StorageDispatchWithReserves` can be specified with the following dictionary of attributes: +```julia +set_device_model!( + template, + DeviceModel( + GenericBattery, + StorageDispatchWithReserves; + attributes=Dict{String, Any}( + "reservation" => false, + "cycling_limits" => false, + "energy_target" => false, + "complete_coverage" => false, + "regularization" => false, + ), + ), +) +``` +Changing the attributes between `true` or `false` can enable/disable multiple aspects of the formulation. + +## C + +* *Chronologies:* In `PowerSimulations.jl`, chronologies define where information is flowing. There are two types of chronologies. 1) **inter-stage chronologies** (`InterProblemChronology`) that define how information flows between stages. e.g. day-ahead solutions are used to inform economic dispatch problems; and 2) **intra-stage chronologies** (`IntraProblemChronology`) that define how information flows between multiple executions of a single stage. e.g. the dispatch setpoints of the first period of an economic dispatch problem are constrained by the ramping limits from setpoints in the final period of the previous problem. + ## D -* *Decision Problem*: A decision problem calculates the desired system operation based on forecasts of uncertain inputs and information about the state of the system. The output of a decision problem represents the policies used to drive the set-points of the system's devices, like generators or switches, and depends on the purpose of the problem. See the [Decision Model Tutorial](op_problem_tutorial) to learn more about solving individual problems. +* *Decision Problem*: A decision problem calculates the desired system operation based on forecasts of uncertain inputs and information about the state of the system. The output of a decision problem represents the policies used to drive the set-points of the system's devices, like generators or switches, and depends on the purpose of the problem. See the [Decision Model Tutorial](@ref op_problem_tutorial) to learn more about solving individual problems. -* *Device Formulation*: The model of a device that is incorporated into a large system optimization models. For instance, the storage device model used inside of a Unit Commitment (UC) problem. A device model needs to follow some requirements to be integrated into operation problems. +* *Device Formulation*: The model of a device that is incorporated into a large system optimization models. For instance, the storage device model used inside of a Unit Commitment (UC) problem. A device model needs to follow some requirements to be integrated into operation problems. For more information about valid `DeviceModel`s and their mathematical representations, check out the [Formulation Library](@ref formulation_intro). ## E * *Emulation Problem*: An emulation problem is used to mimic the system's behavior subject to an incoming decision and the realization of a forecasted inputs. The solution of the emulator produces outputs representative of the system performance when operating subject the policies resulting from the decision models. +## F + +* *FeedForward*: The definition of exactly what information is passed using the defined chronologies is accomplished using FeedForwards. Specifically, a FeedForward is used to define what to do with information being passed with an inter-stage chronology in a Simulation. The most common FeedForward is the `SemiContinuousFeedForward` that affects the semi-continuous range constraints of thermal generators in the economic dispatch problems based on the value of the (already solved) unit-commitment variables. + ## H * *Horizon*: The number of steps in the look-ahead of a decision problem. For instance, a Day-Ahead problem usually has a 48 step horizon. Check the time [Time Series Data Section in PowerSystems.jl](https://nrel-sienna.github.io/PowerSystems.jl/stable/modeler_guide/time_series/) @@ -21,3 +50,13 @@ ## R * *Resolution*: The amount of time between timesteps in a simulation. For instance 1-hour or 5-minutes. In Julia these are defined using the syntax `Hour(1)` and `Minute(5)`. Check the time [Time Series Data Section in PowerSystems.jl](https://nrel-sienna.github.io/PowerSystems.jl/stable/modeler_guide/time_series/) + +## S + +* *Simulation*: A simulation is a pre-determined sequence of decision problems in a way that solving it, resembles the solution procedures commonly used by operators. The most common simulation model is the solution of a Unit Commitment and Economic Dispatch sequence of problems. + +* *Solver*: A solver is a software package that incorporates algorithms for finding solutions to one or more classes of optimization problem. For example, FICO Xpress is a commercial optimization solver for linear programming (LP), convex quadratic programming (QP) problems, convex quadratically constrained quadratic programming (QCQP), second-order cone programming (SOCP) and their mixed integer counterparts. **A solver is required to be specified** in order to solve any computer optimization problem. + +## T + +* *Template*: A `ProblemTemplate` is just a collection of `DeviceModel`s that allows the user to specify the formulations of each set of devices (by device type) independently so that the modeler can adjust the level of detail according to the question of interest and the available data. For more information about valid `DeviceModel`s and their mathematical representations, check out the [Formulation Library](@ref formulation_intro). \ No newline at end of file From ea9be22f77e47e85de4dec63709cc3f6bb4f838e Mon Sep 17 00:00:00 2001 From: rodrigomha Date: Tue, 30 Apr 2024 16:31:47 -0700 Subject: [PATCH 34/69] remove tips and tricks --- docs/make.jl | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/make.jl b/docs/make.jl index f90d00bf00..8249d974e0 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -17,7 +17,6 @@ pages = OrderedDict( "modeler_guide/running_a_simulation.md", "modeler_guide/simulation_recorder.md", "modeler_guide/logging.md", - "modeler_guide/tips_and_tricks.md", "modeler_guide/debugging_infeasible_models.md", "modeler_guide/parallel_simulations.md", "modeler_guide/modeling_faq.md", From 7fe072d489021cf970054660f6be427787dcf833 Mon Sep 17 00:00:00 2001 From: rodrigomha Date: Wed, 1 May 2024 17:06:40 -0700 Subject: [PATCH 35/69] reorder definitions --- docs/src/modeler_guide/definitions.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/src/modeler_guide/definitions.md b/docs/src/modeler_guide/definitions.md index 6333424c4b..53f1b47152 100644 --- a/docs/src/modeler_guide/definitions.md +++ b/docs/src/modeler_guide/definitions.md @@ -49,7 +49,9 @@ Changing the attributes between `true` or `false` can enable/disable multiple as ## R -* *Resolution*: The amount of time between timesteps in a simulation. For instance 1-hour or 5-minutes. In Julia these are defined using the syntax `Hour(1)` and `Minute(5)`. Check the time [Time Series Data Section in PowerSystems.jl](https://nrel-sienna.github.io/PowerSystems.jl/stable/modeler_guide/time_series/) +* *Resolution*: The amount of time between time steps in a simulation. For instance 1-hour or 5-minutes. In Julia these are defined using the syntax `Hour(1)` and `Minute(5)`. Check the time [Time Series Data Section in PowerSystems.jl](https://nrel-sienna.github.io/PowerSystems.jl/stable/modeler_guide/time_series/) + +* *Results vs Realized Results*: In `PowerSimulations.jl` the term *results* is used to refer to the solution of all optimization problems in a *Simulation*. When using `read_variable(results, Variable)` in a `DecisionModel` of a simulation, the output is a dictionary with the values of such variable for every optimization problem solved, while `read_realized_variable(results, Variable)` will return the values of the specified interval and number of steps in the simulation. See the [Read Results page](@ref read_results) for more details. ## S From 178f9f9650cd901fd3c0d68545678ca9560f8455 Mon Sep 17 00:00:00 2001 From: rodrigomha Date: Wed, 1 May 2024 17:06:45 -0700 Subject: [PATCH 36/69] add read results --- docs/make.jl | 1 + docs/src/modeler_guide/read_results.md | 201 +++++++++++++++++++++++++ 2 files changed, 202 insertions(+) create mode 100644 docs/src/modeler_guide/read_results.md diff --git a/docs/make.jl b/docs/make.jl index 8249d974e0..f948e0453f 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -15,6 +15,7 @@ pages = OrderedDict( "modeler_guide/psi_structure.md", "modeler_guide/problem_templates.md", "modeler_guide/running_a_simulation.md", + "modeler_guide/read_results.md", "modeler_guide/simulation_recorder.md", "modeler_guide/logging.md", "modeler_guide/debugging_infeasible_models.md", diff --git a/docs/src/modeler_guide/read_results.md b/docs/src/modeler_guide/read_results.md new file mode 100644 index 0000000000..08ff92d975 --- /dev/null +++ b/docs/src/modeler_guide/read_results.md @@ -0,0 +1,201 @@ +# [Read results](@id read_results) + +Once a `DecisionModel` is solved via `solve!(model)` or a Simulation is executed (and solved) via `execute!(simulation)`, the results are stored and can be accessed directly in the REPL for result exploration and plotting. + +## Read results of a Decision Problem + +Once a `DecisionModel` is solved, results are accessed using `ProblemResults(model)` as follows: + +```julia +# The DecisionModel is already constructed +build!(model, output_dir = mktempdir()) +solve!(model) + +results = ProblemResults(model) +``` + +The output will showcase the available expressions, parameters and variables to read. For example it will look like: + +```raw +Start: 2020-01-01T00:00:00 +End: 2020-01-03T23:00:00 +Resolution: 60 minutes + +PowerSimulations Problem Auxiliary variables Results +┌──────────────────────────────────────────┐ +│ CumulativeCyclingCharge__HybridSystem │ +│ CumulativeCyclingDischarge__HybridSystem │ +└──────────────────────────────────────────┘ + +PowerSimulations Problem Expressions Results +┌─────────────────────────────────────────────┐ +│ ProductionCostExpression__RenewableDispatch │ +│ ProductionCostExpression__ThermalStandard │ +└─────────────────────────────────────────────┘ + +PowerSimulations Problem Duals Results +┌──────────────────────────────────────┐ +│ CopperPlateBalanceConstraint__System │ +└──────────────────────────────────────┘ + +PowerSimulations Problem Parameters Results +┌────────────────────────────────────────────────────────────────────────┐ +│ ActivePowerTimeSeriesParameter__RenewableFix │ +│ RenewablePowerTimeSeries__HybridSystem │ +│ RequirementTimeSeriesParameter__VariableReserve__ReserveUp__Spin_Up_R3 │ +│ RequirementTimeSeriesParameter__VariableReserve__ReserveUp__Reg_Up │ +│ ActivePowerTimeSeriesParameter__PowerLoad │ +│ ActivePowerTimeSeriesParameter__RenewableDispatch │ +│ RequirementTimeSeriesParameter__VariableReserve__ReserveDown__Reg_Down │ +│ ActivePowerTimeSeriesParameter__HydroDispatch │ +│ RequirementTimeSeriesParameter__VariableReserve__ReserveUp__Spin_Up_R1 │ +│ RequirementTimeSeriesParameter__VariableReserve__ReserveUp__Spin_Up_R2 │ +└────────────────────────────────────────────────────────────────────────┘ + +PowerSimulations Problem Variables Results +┌────────────────────────────────────────────────────────────────────┐ +│ ActivePowerOutVariable__HybridSystem │ +│ ReservationVariable__HybridSystem │ +│ RenewablePower__HybridSystem │ +│ ActivePowerReserveVariable__VariableReserve__ReserveUp__Spin_Up_R1 │ +│ SystemBalanceSlackUp__System │ +│ BatteryEnergyShortageVariable__HybridSystem │ +│ ActivePowerReserveVariable__VariableReserve__ReserveUp__Reg_Up │ +│ StopVariable__ThermalStandard │ +│ BatteryStatus__HybridSystem │ +│ BatteryDischarge__HybridSystem │ +│ ActivePowerInVariable__HybridSystem │ +│ DischargeRegularizationVariable__HybridSystem │ +│ BatteryCharge__HybridSystem │ +│ ActivePowerVariable__RenewableDispatch │ +│ ActivePowerReserveVariable__VariableReserve__ReserveDown__Reg_Down │ +│ EnergyVariable__HybridSystem │ +│ OnVariable__HybridSystem │ +│ BatteryEnergySurplusVariable__HybridSystem │ +│ SystemBalanceSlackDown__System │ +│ ActivePowerReserveVariable__VariableReserve__ReserveUp__Spin_Up_R2 │ +│ ThermalPower__HybridSystem │ +│ ActivePowerVariable__ThermalStandard │ +│ StartVariable__ThermalStandard │ +│ ActivePowerReserveVariable__VariableReserve__ReserveUp__Spin_Up_R3 │ +│ OnVariable__ThermalStandard │ +│ ChargeRegularizationVariable__HybridSystem │ +└────────────────────────────────────────────────────────────────────┘ +``` + +Then the following code can be used to read results: + +```julia +# Read active power of Thermal Standard +thermal_active_power = read_variable(results, "ActivePowerVariable__ThermalStandard") + +# Read max active power parameter of RenewableDispatch +renewable_param = read_parameter(results, "ActivePowerTimeSeriesParameter__RenewableDispatch") + +# Read cost expressions of ThermalStandard units +cost_thermal = read_expression(results, "ProductionCostExpression__ThermalStandard") + +# Read dual variables +dual_balance_constraint = read_dual(results, "CopperPlateBalanceConstraint__System") + +# Read auxiliary variables +aux_var_result = read_aux_variable(results, "CumulativeCyclingCharge__HybridSystem") +``` + +Results will be in the form of DataFrames that can be easily explored. + +## Read results of a Simulation + +```julia +# The Simulation is already constructed +build!(sim) +execute!(sim; enable_progress_bar=true) + +results_sim = SimulationResults(sim) +``` + +As an example, the `SimulationResults` printing will look like: + +```raw +Decision Problem Results +┌──────────────┬─────────────────────┬──────────────┬─────────────────────────┐ +│ Problem Name │ Initial Time │ Resolution │ Last Solution Timestamp │ +├──────────────┼─────────────────────┼──────────────┼─────────────────────────┤ +│ ED │ 2020-10-02T00:00:00 │ 60 minutes │ 2020-10-09T23:00:00 │ +│ UC │ 2020-10-02T00:00:00 │ 1440 minutes │ 2020-10-09T00:00:00 │ +└──────────────┴─────────────────────┴──────────────┴─────────────────────────┘ + +Emulator Results +┌─────────────────┬───────────┐ +│ Name │ Emulator │ +│ Resolution │ 5 minutes │ +│ Number of steps │ 2304 │ +└─────────────────┴───────────┘ +``` + +With this, it is possible to obtain results of each `DecisionModel` and `EmulationModel` as follows: + +```julia +# Use the Problem Name for Decision Problems +results_uc = get_decision_problem_results(results_sim, "UC") +results_ed = get_decision_problem_results(results_sim, "ED") +results_emulator = get_emulation_problem_results(results_dcp) +``` + +Once we have each decision (or emulation) problem results, we can explore directly using the approach for Decision Models, mentioned in the previous section. + +### Reading solutions for all simulation steps + +In this case, using `read_variable` (or read expression, parameter or dual), will return a dictionary of all steps (of that Decision Problem). For example, the following code: + +```julia +thermal_active_power = read_variable(results_uc, "ActivePowerVariable__ThermalStandard") +``` +will return: +``` +DataStructures.SortedDict{Any, Any, Base.Order.ForwardOrdering} with 8 entries: + DateTime("2020-10-02T00:00:00") => 72×54 DataFrame… + DateTime("2020-10-03T00:00:00") => 72×54 DataFrame… + DateTime("2020-10-04T00:00:00") => 72×54 DataFrame… + DateTime("2020-10-05T00:00:00") => 72×54 DataFrame… + DateTime("2020-10-06T00:00:00") => 72×54 DataFrame… + DateTime("2020-10-07T00:00:00") => 72×54 DataFrame… + DateTime("2020-10-08T00:00:00") => 72×54 DataFrame… + DateTime("2020-10-09T00:00:00") => 72×54 DataFrame… +``` +That is, a sorted dictionary for each simulation step, using as a key the initial timestamp for that specific simulation step. + +Note that in this case, each DataFrame, has a dimension of ``72 \times 54``, since the horizon is 72 hours (number of rows), but the interval is only 24 hours. Indeed, note the initial timestamp of each simulation step is the beginning of each day, i.e. 24 hours. Finally, there 54 columns, since this example system has 53 `ThermalStandard` units (plus 1 column for the timestamps). The user is free to explore the solution of any simulation step as needed. + +### Reading the "realized" solution (i.e. the interval) + +Using `read_realized_variable` (or read realized expression, parameter or dual), will return the DataFrame of the realized solution of any specific variable. That is, it will concatenate the corresponding simulation step with the specified interval of that step, to construct a single DataFrame with the "realized solution" of the entire simulation. + +For example, the code: +```julia +th_realized_power = read_realized_variable(results_uc, "ActivePowerVariable__ThermalStandard") +``` +will return: +```raw +92×54 DataFrame + Row │ DateTime 322_CT_6 321_CC_1 202_STEAM_3 223_CT_4 123_STEAM_2 213_CT_1 223_CT_6 313_CC_1 101_STEAM_3 123_C ⋯ + │ DateTime Float64 Float64 Float64 Float64 Float64 Float64 Float64 Float64 Float64 Float ⋯ +─────┼─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── + 1 │ 2020-10-02T00:00:00 0.0 293.333 0.0 0.0 0.0 0.0 0.0 231.667 76.0 0.0 ⋯ + 2 │ 2020-10-02T01:00:00 0.0 267.552 0.0 0.0 0.0 0.0 0.0 231.667 76.0 0.0 + 3 │ 2020-10-02T02:00:00 0.0 234.255 0.0 0.0 -4.97544e-11 0.0 0.0 231.667 76.0 0.0 + 4 │ 2020-10-02T03:00:00 0.0 249.099 0.0 0.0 -4.97544e-11 0.0 0.0 231.667 76.0 0.0 + 5 │ 2020-10-02T04:00:00 0.0 293.333 0.0 0.0 -4.97544e-11 0.0 0.0 231.667 76.0 0.0 ⋯ + 6 │ 2020-10-02T05:00:00 0.0 293.333 1.27578e-11 0.0 -4.97544e-11 0.0 0.0 293.333 76.0 0.0 + ⋮ │ ⋮ ⋮ ⋮ ⋮ ⋮ ⋮ ⋮ ⋮ ⋮ ⋮ ⋱ + 187 │ 2020-10-09T18:00:00 0.0 293.333 76.0 0.0 155.0 0.0 0.0 318.843 76.0 0.0 + 188 │ 2020-10-09T19:00:00 0.0 293.333 76.0 0.0 124.0 0.0 0.0 293.333 76.0 0.0 + 189 │ 2020-10-09T20:00:00 0.0 293.333 60.6667 0.0 124.0 0.0 0.0 0.0 76.0 0.0 ⋯ + 190 │ 2020-10-09T21:00:00 -7.65965e-12 293.333 60.6667 0.0 124.0 0.0 0.0 0.0 76.0 0.0 + 191 │ 2020-10-09T22:00:00 0.0 0.0 60.6667 0.0 124.0 0.0 0.0 0.0 76.0 7.156 + 192 │ 2020-10-09T23:00:00 0.0 0.0 60.6667 0.0 117.81 0.0 0.0 0.0 76.0 0.0 + 44 columns and 180 rows omitted +``` +In this case, the 8 simulation steps of 24 hours (192 hours), in a single DataFrame, to enable easy exploration of the realized results for the user. + + From 7847032581c2dd2f67734c6ef95a575d4cd155bb Mon Sep 17 00:00:00 2001 From: rodrigomha Date: Thu, 2 May 2024 17:16:25 -0700 Subject: [PATCH 37/69] update docstrings --- src/core/formulations.jl | 4 +++- src/core/variables.jl | 5 +++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/core/formulations.jl b/src/core/formulations.jl index e88a7a2c3c..3f0fcb7823 100644 --- a/src/core/formulations.jl +++ b/src/core/formulations.jl @@ -145,7 +145,9 @@ LossLess InterconnectingConverter Model """ struct LossLessConverter <: AbstractConverterFormulation end -# TODO: Think if this an ok abstraction for future use cases +""" +LossLess Line Abstract Model +""" struct LossLessLine <: AbstractBranchFormulation end ############################## Network Model Formulations ################################## diff --git a/src/core/variables.jl b/src/core/variables.jl index aeeafe6304..0c86286074 100644 --- a/src/core/variables.jl +++ b/src/core/variables.jl @@ -161,6 +161,11 @@ Docs abbreviation: ``p^\\text{sl,dn}`` """ struct SystemBalanceSlackDown <: VariableType end +""" +Struct to dispatch the creation of Reserve requirement slack variables. Used when there is not reserves in the system to satisfy the requirement. + +Docs abbreviation: ``r^\\text{sl}`` +""" struct ReserveRequirementSlack <: VariableType end """ From f58e28d0999e6a3267b6cc323ce1124fa74f4c9f Mon Sep 17 00:00:00 2001 From: rodrigomha Date: Thu, 2 May 2024 17:16:40 -0700 Subject: [PATCH 38/69] minor docs updates --- docs/src/api/PowerSimulations.md | 1 + docs/src/formulation_library/Branch.md | 25 ++++++++++++++------ docs/src/formulation_library/Load.md | 4 ++-- docs/src/formulation_library/RenewableGen.md | 6 ++--- docs/src/formulation_library/ThermalGen.md | 2 +- 5 files changed, 25 insertions(+), 13 deletions(-) diff --git a/docs/src/api/PowerSimulations.md b/docs/src/api/PowerSimulations.md index b83fbebd8d..86ed344935 100644 --- a/docs/src/api/PowerSimulations.md +++ b/docs/src/api/PowerSimulations.md @@ -122,6 +122,7 @@ HotStartVariable WarmStartVariable ColdStartVariable PowerAboveMinimumVariable +PowerOutput ``` ### Storage Unit Variables diff --git a/docs/src/formulation_library/Branch.md b/docs/src/formulation_library/Branch.md index 2ef17527de..0509680227 100644 --- a/docs/src/formulation_library/Branch.md +++ b/docs/src/formulation_library/Branch.md @@ -3,7 +3,9 @@ !!! note The usage of reactive power variables and constraints will depend on the network model used, i.e. if it uses (or not) reactive power. If the network model is purely active power based, then no variables and constraints related to reactive power are created. For the sake of completion, if the formulation allows the usage of reactive power it will be included. -## `StaticBranch` for `PTDFPowerModel` Network model +## `StaticBranch` + +Formulation valid for `PTDFPowerModel` Network model ```@docs StaticBranch @@ -51,7 +53,9 @@ on which ``\text{PTDF}`` is the ``N \times B`` system Power Transfer Distributio --- -## `StaticBranchBounds` for `PTDFPowerModel` Network model +## `StaticBranchBounds` + +Formulation valid for `PTDFPowerModel` Network model ```@docs StaticBranchBounds @@ -88,7 +92,9 @@ on which ``\text{PTDF}`` is the ``N \times B`` system Power Transfer Distributio --- -## `StaticBranchUnbounded` for `PTDFPowerModel` Network model +## `StaticBranchUnbounded` + +Formulation valid for `PTDFPowerModel` Network model ```@docs StaticBranchUnbounded @@ -120,7 +126,9 @@ on which ``\text{PTDF}`` is the ``N \times B`` system Power Transfer Distributio --- -## `HVDCTwoTerminalUnbounded` for `PTDFPowerModel` Network model +## `HVDCTwoTerminalUnbounded` + +Formulation valid for `PTDFPowerModel` Network model ```@docs HVDCTwoTerminalUnbounded @@ -150,7 +158,9 @@ No constraints are added. --- -## `HVDCTwoTerminalLossless` for `PTDFPowerModel` Network model +## `HVDCTwoTerminalLossless` + +Formulation valid for `PTDFPowerModel` Network model ```@docs HVDCTwoTerminalLossless @@ -213,8 +223,9 @@ and --- -## `HVDCTwoTerminalDispatch` for `PTDFPowerModel` Network model +## `HVDCTwoTerminalDispatch` +Formulation valid for `PTDFPowerModel` Network model ```@docs HVDCTwoTerminalDispatch @@ -279,7 +290,7 @@ using PowerSystems using DataFrames using Latexify combos = PowerSimulations.generate_device_formulation_combinations() -filter!(x -> x["device_type"] <: Branch, combos) +filter!(x -> (x["device_type"] <: Branch) && (x["device_type"] != TModelHVDCLine), combos) combo_table = DataFrame( "Valid DeviceModel" => ["`DeviceModel($(c["device_type"]), $(c["formulation"]))`" for c in combos], "Device Type" => ["[$(c["device_type"])](https://nrel-Sienna.github.io/PowerSystems.jl/stable/model_library/generated_$(c["device_type"])/)" for c in combos], diff --git a/docs/src/formulation_library/Load.md b/docs/src/formulation_library/Load.md index a1d70064a6..6433b447f1 100644 --- a/docs/src/formulation_library/Load.md +++ b/docs/src/formulation_library/Load.md @@ -84,7 +84,7 @@ mdtable(combo_table, latex = false) **Objective:** -Creates an objective function term based on the [`VariableCost` Options](@ref) where the quantity term is defined as ``p^\text{ld}``. +Creates an objective function term based on the cost options, where the quantity term is defined as ``p^\text{ld}``. **Expressions:** @@ -140,7 +140,7 @@ mdtable(combo_table, latex = false) **Objective:** -Creates an objective function term based on the [`VariableCost` Options](@ref) where the quantity term is defined as ``p^\text{re}``. +Creates an objective function term based on the cost options, where the quantity term is defined as ``p^\text{re}``. **Expressions:** diff --git a/docs/src/formulation_library/RenewableGen.md b/docs/src/formulation_library/RenewableGen.md index 89de8e0b36..d81bc7dff8 100644 --- a/docs/src/formulation_library/RenewableGen.md +++ b/docs/src/formulation_library/RenewableGen.md @@ -6,7 +6,7 @@ Renewable generation formulations define the optimization models that describe r The usage of reactive power variables and constraints will depend on the network model used, i.e. if it uses (or not) reactive power. If the network model is purely active power based, then no variables and constraints related to reactive power are created. For the sake of completion, if the formulation allows the usage of reactive power it will be included. !!! note - Reserve variables for services are not included in the formulation, albeit their inclusion change the variables, expressions, constraints and objective functions created. A detailed description of the implications in the optimization models is described in the [Service formulation](@ref service_formulation) section. + Reserve variables for services are not included in the formulation, albeit their inclusion change the variables, expressions, constraints and objective functions created. A detailed description of the implications in the optimization models is described in the [Service formulation](@ref service_formulations) section. ## `RenewableFullDispatch` @@ -48,7 +48,7 @@ mdtable(combo_table, latex = false) **Objective:** -Creates an objective function term based on the [`VariableCost` Options](@ref) where the quantity term is defined as ``- p^\text{re}`` to incentivize generation from `RenewableGen` devices. +Creates an objective function term based on the cost options, where the quantity term is defined as ``- p^\text{re}`` to incentivize generation from `RenewableGen` devices. **Expressions:** @@ -106,7 +106,7 @@ mdtable(combo_table, latex = false) **Objective:** -Creates an objective function term based on the [`VariableCost` Options](@ref) where the quantity term is defined as ``- p_t^\text{re}`` to incentivize generation from `RenewableGen` devices. +Creates an objective function term based on the cost options, where the quantity term is defined as ``- p_t^\text{re}`` to incentivize generation from `RenewableGen` devices. **Expressions:** diff --git a/docs/src/formulation_library/ThermalGen.md b/docs/src/formulation_library/ThermalGen.md index 5d158ab317..403874b971 100644 --- a/docs/src/formulation_library/ThermalGen.md +++ b/docs/src/formulation_library/ThermalGen.md @@ -11,7 +11,7 @@ Thermal generation formulations define the optimization models that describe the The usage of reactive power variables and constraints will depend on the network model used, i.e. if it uses (or not) reactive power. If the network model is purely active power based, then no variables and constraints related to reactive power are created. For the sake of completion, if the formulation allows the usage of reactive power it will be included. !!! note - Reserve variables for services are not included in the formulation, albeit their inclusion change the variables, expressions, constraints and objective functions created. A detailed description of the implications in the optimization models is described in the [Service formulation](@ref service_formulation) section. + Reserve variables for services are not included in the formulation, albeit their inclusion change the variables, expressions, constraints and objective functions created. A detailed description of the implications in the optimization models is described in the [Service formulation](@ref service_formulations) section. --- From 45fc122d79fb21e71c78627758dd3c29e9f55368 Mon Sep 17 00:00:00 2001 From: rodrigomha Date: Thu, 2 May 2024 17:16:49 -0700 Subject: [PATCH 39/69] add range reserve service --- docs/src/formulation_library/Service.md | 84 ++++++++++++++++++++++++- 1 file changed, 83 insertions(+), 1 deletion(-) diff --git a/docs/src/formulation_library/Service.md b/docs/src/formulation_library/Service.md index 4b713ebd4a..097398fbb8 100644 --- a/docs/src/formulation_library/Service.md +++ b/docs/src/formulation_library/Service.md @@ -1,3 +1,85 @@ # [`PowerSystems.Service` Formulations](@id service_formulations) -TODO +`Services` (or ancillary services) are models used to ensure that there is necessary support to the power grid from generators to consumers, in order to ensure reliable operation of the system. + +The most common application for ancillary services are reserves, i.e., generation (or load) that is not currently being used, but can be quickly made available in case of unexpected changes of grid conditions, for example a sudden loss of load or generation. + +A key challenge of adding services to a system, from a mathematical perspective, is specifying which units contribute to the specified requirement of a service, that implies the creation of new variables (such as reserve variables) and modification of constraints. + +In this documentation, we first specify the available `Services` in the grid, and what requirements impose in the system, and later we discuss the implication on device formulations for specific units. + +## `RangeReserve` + +For each service ``s`` of the model type `RangeReserve` the following variables are created: + +**Variables**: + +- [`ActivePowerReserveVariable`](@ref): + - Bounds: [0.0, ] + - Symbol: ``r_{d}`` for ``d`` in contributing devices to the service ``s`` +If slacks are enabled: +- [`ReserveRequirementSlack`](@ref): + - Bounds: [0.0, ] + - Symbol: ``r^\text{sl}`` + +Depending on the `PowerSystems.jl` type associated to the `RangeReserve` formulation model, the parameters are: + +**Static Parameters** + +- ``\text{PF}`` = `PowerSystems.get_max_participation_factor(service)` + +For a `StaticReserve` `PowerSystems` type: +- ``\text{Req}`` = `PowerSystems.get_requirement(service)` + +**Time Series Parameters** + +For a `VariableReserve` `PowerSystems` type: +```@eval +using PowerSimulations +using PowerSystems +using DataFrames +using Latexify +combos = PowerSimulations.get_default_time_series_names(VariableReserve, RangeReserve) +combo_table = DataFrame( + "Parameter" => map(x -> "[`$x`](@ref)", collect(keys(combos))), + "Default Time Series Name" => map(x -> "`$x`", collect(values(combos))), + ) +mdtable(combo_table, latex = false) +``` + +**Relevant Methods:** + +``\mathcal{D}_s`` = `PowerSystems.get_contributing_devices(system, service)`: Set (vector) of all contributing devices to the service ``s`` in the system. + +**Expressions:** + +Adds the `ActivePowerReserveVariable` for upper/lower bound expressions of contributing devices. + +For `ReserveUp` types, the variable is added to `ActivePowerRangeExpressionUB`, such that this expression considers both the `ActivePowerVariable` and its reserve variable. Similarly, For `ReserveDown` types, the variable is added to `ActivePowerRangeExpressionLB`, such that this expression considers both the `ActivePowerVariable` and its reserve variable + + +*Example*: for a thermal unit ``d`` contributing to two different `ReserveUp` ``s_1, s_2`` services (e.g. Reg-Up and Spin): +```math +\text{ActivePowerRangeExpressionUB}_{t} = p_t^\text{th} + r_{s_1,t} + r_{s_2, t} \le P^\text{th,max} +``` +similarly if ``s_3`` is a `ReserveDown` service (e.g. Reg-Down): +```math +\text{ActivePowerRangeExpressionLB}_{t} = p_t^\text{th} - r_{s_3,t} \ge P^\text{th,min} +``` + + +**Constraints:** + +A RangeReserve implements two fundamental constraints. The first is that the sum of all reserves of contributing devices must be larger than the `RangeReserve` requirement. Thus, for a service ``s``: + +```math +\sum_{d\in\mathcal{D}_s} r_{d,t} + r_t^\text{sl} \ge \text{Req},\quad \forall t\in \{1,\dots, T\} \quad \text{(for a StaticReserve)} \\ +\sum_{d\in\mathcal{D}_s} r_{d,t} + r_t^\text{sl} \ge \text{RequirementTimeSeriesParameter}_{t},\quad \forall t\in \{1,\dots, T\} \quad \text{(for a VariableReserve)} +``` + +In addition, there is a restriction on how much each contributing device ``d`` can contribute to the requirement, based on the max participation factor allowed. + +```math +r_{d,t} \le \text{Req} \cdot \text{PF} ,\quad \forall d\in \mathcal{D}_s, \forall t\in \{1,\dots, T\} \quad \text{(for a StaticReserve)} \\ +r_{d,t} \le \text{RequirementTimeSeriesParameter}_{t} \cdot \text{PF}\quad \forall d\in \mathcal{D}_s, \forall t\in \{1,\dots, T\}, \quad \text{(for a VariableReserve)} +``` From 05ca9c7eec3f22cfddb1b560434f6cae9ae14364 Mon Sep 17 00:00:00 2001 From: rodrigomha Date: Fri, 3 May 2024 17:23:10 -0700 Subject: [PATCH 40/69] add docstrings --- src/core/formulations.jl | 7 +++++++ src/core/variables.jl | 5 +++++ 2 files changed, 12 insertions(+) diff --git a/src/core/formulations.jl b/src/core/formulations.jl index 3f0fcb7823..b520a902dd 100644 --- a/src/core/formulations.jl +++ b/src/core/formulations.jl @@ -230,7 +230,14 @@ abstract type AbstractAGCFormulation <: AbstractServiceFormulation end struct PIDSmoothACE <: AbstractAGCFormulation end struct GroupReserve <: AbstractReservesFormulation end + +""" +Struct for to add reserves to be larger than a specified requirement +""" struct RangeReserve <: AbstractReservesFormulation end +""" +Struct for to add reserves to be larger than a variable requirement depending of costs +""" struct StepwiseCostReserve <: AbstractReservesFormulation end struct RampReserve <: AbstractReservesFormulation end struct NonSpinningReserve <: AbstractReservesFormulation end diff --git a/src/core/variables.jl b/src/core/variables.jl index 0c86286074..d91d3aff89 100644 --- a/src/core/variables.jl +++ b/src/core/variables.jl @@ -117,6 +117,11 @@ Docs abbreviation: ``r`` """ struct ActivePowerReserveVariable <: VariableType end +""" +Struct to dispatch the creation of Service Requirement Variables + +Docs abbreviation: ``\\text{req}`` +""" struct ServiceRequirementVariable <: VariableType end """ From 5327f77c09625e2eaf739415d370bf409f15ec57 Mon Sep 17 00:00:00 2001 From: rodrigomha Date: Fri, 3 May 2024 17:23:16 -0700 Subject: [PATCH 41/69] add new service docs --- docs/src/api/PowerSimulations.md | 6 +-- docs/src/formulation_library/Service.md | 67 ++++++++++++++++++++++++- 2 files changed, 69 insertions(+), 4 deletions(-) diff --git a/docs/src/api/PowerSimulations.md b/docs/src/api/PowerSimulations.md index 86ed344935..c06b7b2640 100644 --- a/docs/src/api/PowerSimulations.md +++ b/docs/src/api/PowerSimulations.md @@ -14,9 +14,9 @@ end 3. [Emulation Models](#emulation-models) 4. [Service Models](#service-models) 5. [Simulation Models](#simulation-models) -6. [Variables](#variables) -7. [Constraints](#constraints) -8. [Parameters](#parameters) +6. [Variables](#Variables) +7. [Constraints](#Constraints) +8. [Parameters](#Parameters) # Device Models diff --git a/docs/src/formulation_library/Service.md b/docs/src/formulation_library/Service.md index 097398fbb8..9a2f29cc6b 100644 --- a/docs/src/formulation_library/Service.md +++ b/docs/src/formulation_library/Service.md @@ -10,6 +10,10 @@ In this documentation, we first specify the available `Services` in the grid, an ## `RangeReserve` +```@docs +RangeReserve +``` + For each service ``s`` of the model type `RangeReserve` the following variables are created: **Variables**: @@ -20,6 +24,7 @@ For each service ``s`` of the model type `RangeReserve` the following variables If slacks are enabled: - [`ReserveRequirementSlack`](@ref): - Bounds: [0.0, ] + - Default proportional cost: 1e5 - Symbol: ``r^\text{sl}`` Depending on the `PowerSystems.jl` type associated to the `RangeReserve` formulation model, the parameters are: @@ -51,6 +56,10 @@ mdtable(combo_table, latex = false) ``\mathcal{D}_s`` = `PowerSystems.get_contributing_devices(system, service)`: Set (vector) of all contributing devices to the service ``s`` in the system. +**Objective:** + +Add a large proportional cost to the objective function if slack variables are used ``+ r^\text{sl} \cdot 10^5`` + **Expressions:** Adds the `ActivePowerReserveVariable` for upper/lower bound expressions of contributing devices. @@ -67,7 +76,6 @@ similarly if ``s_3`` is a `ReserveDown` service (e.g. Reg-Down): \text{ActivePowerRangeExpressionLB}_{t} = p_t^\text{th} - r_{s_3,t} \ge P^\text{th,min} ``` - **Constraints:** A RangeReserve implements two fundamental constraints. The first is that the sum of all reserves of contributing devices must be larger than the `RangeReserve` requirement. Thus, for a service ``s``: @@ -83,3 +91,60 @@ In addition, there is a restriction on how much each contributing device ``d`` c r_{d,t} \le \text{Req} \cdot \text{PF} ,\quad \forall d\in \mathcal{D}_s, \forall t\in \{1,\dots, T\} \quad \text{(for a StaticReserve)} \\ r_{d,t} \le \text{RequirementTimeSeriesParameter}_{t} \cdot \text{PF}\quad \forall d\in \mathcal{D}_s, \forall t\in \{1,\dots, T\}, \quad \text{(for a VariableReserve)} ``` + +## `StepwiseCostReserve` + +```@docs +StepwiseCostReserve +``` + +For each service ``s`` of the model type `RangeReserve` the following variables are created: + +**Variables**: + +- [`ActivePowerReserveVariable`](@ref): + - Bounds: [0.0, ] + - Symbol: ``r_{d}`` for ``d`` in contributing devices to the service ``s`` +- [`ServiceRequirementVariable`](@ref): + - Bounds: [0.0, ] + - Symbol: ``\text{req}`` + +**Static Parameters** + +TODO + +**Time Series Parameters** + +TODO + +**Relevant Methods:** + +``\mathcal{D}_s`` = `PowerSystems.get_contributing_devices(system, service)`: Set (vector) of all contributing devices to the service ``s`` in the system. + +**Objective:** + +TODO + +**Expressions:** + +Adds the `ActivePowerReserveVariable` for upper/lower bound expressions of contributing devices. + +For `ReserveUp` types, the variable is added to `ActivePowerRangeExpressionUB`, such that this expression considers both the `ActivePowerVariable` and its reserve variable. Similarly, For `ReserveDown` types, the variable is added to `ActivePowerRangeExpressionLB`, such that this expression considers both the `ActivePowerVariable` and its reserve variable + + +*Example*: for a thermal unit ``d`` contributing to two different `ReserveUp` ``s_1, s_2`` services (e.g. Reg-Up and Spin): +```math +\text{ActivePowerRangeExpressionUB}_{t} = p_t^\text{th} + r_{s_1,t} + r_{s_2, t} \le P^\text{th,max} +``` +similarly if ``s_3`` is a `ReserveDown` service (e.g. Reg-Down): +```math +\text{ActivePowerRangeExpressionLB}_{t} = p_t^\text{th} - r_{s_3,t} \ge P^\text{th,min} +``` + +**Constraints:** + +A `StepwiseCostReserve` implements a single constraint, such that the sum of all reserves of contributing devices must be larger than the `ServiceRequirementVariable` variable. Thus, for a service ``s``: + +```math +\sum_{d\in\mathcal{D}_s} r_{d,t} \ge \text{req}_t,\quad \forall t\in \{1,\dots, T\} +``` From 5e7f88e383a9f83f8509437fd2cb8f21179e14dc Mon Sep 17 00:00:00 2001 From: rodrigomha Date: Fri, 3 May 2024 18:10:02 -0700 Subject: [PATCH 42/69] remove AGC variables --- docs/src/api/PowerSimulations.md | 7 ------- 1 file changed, 7 deletions(-) diff --git a/docs/src/api/PowerSimulations.md b/docs/src/api/PowerSimulations.md index c06b7b2640..050066b68f 100644 --- a/docs/src/api/PowerSimulations.md +++ b/docs/src/api/PowerSimulations.md @@ -153,13 +153,6 @@ VoltageAngle ```@docs ActivePowerReserveVariable ServiceRequirementVariable -DeltaActivePowerUpVariable -DeltaActivePowerDownVariable -AdditionalDeltaActivePowerUpVariable -AdditionalDeltaActivePowerDownVariable -AreaMismatchVariable -SteadyStateFrequencyDeviation -SmoothACE SystemBalanceSlackUp SystemBalanceSlackDown ReserveRequirementSlack From 6c609f52afd8ddb44324d026541b76bb1f1f1d6f Mon Sep 17 00:00:00 2001 From: rodrigomha Date: Mon, 6 May 2024 17:19:09 -0700 Subject: [PATCH 43/69] add toc to existing docs --- docs/src/formulation_library/Branch.md | 10 ++++++++++ docs/src/formulation_library/Load.md | 9 +++++++++ docs/src/formulation_library/Network.md | 5 +++++ docs/src/formulation_library/Piecewise.md | 6 +++++- docs/src/formulation_library/RenewableGen.md | 8 ++++++++ docs/src/formulation_library/ThermalGen.md | 12 ++++++++++++ 6 files changed, 49 insertions(+), 1 deletion(-) diff --git a/docs/src/formulation_library/Branch.md b/docs/src/formulation_library/Branch.md index 0509680227..8123d7ab7d 100644 --- a/docs/src/formulation_library/Branch.md +++ b/docs/src/formulation_library/Branch.md @@ -3,6 +3,16 @@ !!! note The usage of reactive power variables and constraints will depend on the network model used, i.e. if it uses (or not) reactive power. If the network model is purely active power based, then no variables and constraints related to reactive power are created. For the sake of completion, if the formulation allows the usage of reactive power it will be included. +### Table of contents + +1. [`StaticBranch`](#StaticBranch) +2. [`StaticBranchBounds`](#StaticBranchBounds) +3. [`StaticBranchUnbounded`](#StaticBranchUnbounded) +4. [`HVDCTwoTerminalUnbounded`](#HVDCTwoTerminalUnbounded) +5. [`HVDCTwoTerminalLossless`](#HVDCTwoTerminalLossless) +6. [`HVDCTwoTerminalDispatch`](#HVDCTwoTerminalDispatch) +7. [Valid configurations](#Valid-configurations) + ## `StaticBranch` Formulation valid for `PTDFPowerModel` Network model diff --git a/docs/src/formulation_library/Load.md b/docs/src/formulation_library/Load.md index 6433b447f1..9df4f559e6 100644 --- a/docs/src/formulation_library/Load.md +++ b/docs/src/formulation_library/Load.md @@ -5,6 +5,15 @@ Electric load formulations define the optimization models that describe load uni !!! note The usage of reactive power variables and constraints will depend on the network model used, i.e. if it uses (or not) reactive power. If the network model is purely active power based, then no variables and constraints related to reactive power are created. For the sake of completion, if the formulation allows the usage of reactive power it will be included. +### Table of contents + +1. [`StaticPowerLoad`](#StaticPowerLoad) +2. [`PowerLoadInterruption`](#PowerLoadInterruption) +3. [`PowerLoadDispatch`](#PowerLoadDispatch) +4. [Valid configurations](#Valid-configurations) + +--- + ## `StaticPowerLoad` ```@docs diff --git a/docs/src/formulation_library/Network.md b/docs/src/formulation_library/Network.md index eae0aff52a..1a4b01d67f 100644 --- a/docs/src/formulation_library/Network.md +++ b/docs/src/formulation_library/Network.md @@ -17,6 +17,7 @@ Network formulations are used to describe how the network and buses are handled All of these formulations are described in the [PowerModels.jl documentation](https://lanl-ansi.github.io/PowerModels.jl/stable/formulation-details/) and will not be described here. +--- ## `CopperPlatePowerModel` @@ -56,6 +57,8 @@ Adds the `CopperPlateBalanceConstraint` to balance the active power of all compo \end{align} ``` +--- + ## `AreaBalancePowerModel` ```@docs @@ -84,6 +87,8 @@ Adds the `AreaDispatchBalanceConstraint` to balance the active power of all comp \end{align} ``` +--- + ## `PTDFPowerModel` ```@docs diff --git a/docs/src/formulation_library/Piecewise.md b/docs/src/formulation_library/Piecewise.md index d8511ad7ff..d175ed7270 100644 --- a/docs/src/formulation_library/Piecewise.md +++ b/docs/src/formulation_library/Piecewise.md @@ -44,4 +44,8 @@ on which ``\delta_{k,t} \in [0,1]`` is the interpolation variable, ``p`` is the &\left \{\delta_{i,t}, \dots, \delta_{k,t} \right \} \in \text{SOS}_{2} & \forall t \in \mathcal{T} \end{align*} ``` -on which ``\delta_{k,t} \in [0,1]`` is the interpolation variable, ``p`` is the active power of the generator and ``\text{on} \in \{0,1\}`` is the parameter that decides if the generator is available or not. In the case of a PWL convex costs, i.e. increasing slopes, the SOS constraint is omitted. \ No newline at end of file +on which ``\delta_{k,t} \in [0,1]`` is the interpolation variable, ``p`` is the active power of the generator and ``\text{on} \in \{0,1\}`` is the parameter that decides if the generator is available or not. In the case of a PWL convex costs, i.e. increasing slopes, the SOS constraint is omitted. + +## Compact representation of PWL costs + +TODO \ No newline at end of file diff --git a/docs/src/formulation_library/RenewableGen.md b/docs/src/formulation_library/RenewableGen.md index d81bc7dff8..fed1f2889b 100644 --- a/docs/src/formulation_library/RenewableGen.md +++ b/docs/src/formulation_library/RenewableGen.md @@ -8,6 +8,14 @@ Renewable generation formulations define the optimization models that describe r !!! note Reserve variables for services are not included in the formulation, albeit their inclusion change the variables, expressions, constraints and objective functions created. A detailed description of the implications in the optimization models is described in the [Service formulation](@ref service_formulations) section. +### Table of contents + +1. [`RenewableFullDispatch`](#RenewableFullDispatch) +2. [`RenewableConstantPowerFactor`](#RenewableConstantPowerFactor) +3. [Valid configurations](#Valid-configurations) + +--- + ## `RenewableFullDispatch` ```@docs diff --git a/docs/src/formulation_library/ThermalGen.md b/docs/src/formulation_library/ThermalGen.md index 403874b971..c9afd60bfa 100644 --- a/docs/src/formulation_library/ThermalGen.md +++ b/docs/src/formulation_library/ThermalGen.md @@ -13,6 +13,18 @@ Thermal generation formulations define the optimization models that describe the !!! note Reserve variables for services are not included in the formulation, albeit their inclusion change the variables, expressions, constraints and objective functions created. A detailed description of the implications in the optimization models is described in the [Service formulation](@ref service_formulations) section. +### Table of Contents + +1. [`ThermalBasicDispatch`](#ThermalBasicDispatch) +2. [`ThermalDispatchNoMin`](#ThermalDispatchNoMin) +3. [`ThermalCompactDispatch`](#ThermalCompactDispatch) +4. [`ThermalStandardDispatch`](#ThermalStandardDispatch) +5. [`ThermalBasicUnitCommitment`](#ThermalBasicUnitCommitment) +6. [`ThermalBasicCompactUnitCommitment`](#ThermalBasicCompactUnitCommitment) +7. [`ThermalStandardUnitCommitment`](#ThermalStandardUnitCommitment) +8. [`ThermalMultiStartUnitCommitment`](#ThermalMultiStartUnitCommitment) +9. [Valid configurations](#Valid-configurations) + --- ## `ThermalBasicDispatch` From a81fb127ff45154aa2006c363e91e469cf2e35b4 Mon Sep 17 00:00:00 2001 From: rodrigomha Date: Mon, 6 May 2024 17:19:15 -0700 Subject: [PATCH 44/69] finish service docs --- docs/src/formulation_library/Service.md | 318 +++++++++++++++++++++++- 1 file changed, 309 insertions(+), 9 deletions(-) diff --git a/docs/src/formulation_library/Service.md b/docs/src/formulation_library/Service.md index 9a2f29cc6b..6509bd53c5 100644 --- a/docs/src/formulation_library/Service.md +++ b/docs/src/formulation_library/Service.md @@ -8,6 +8,17 @@ A key challenge of adding services to a system, from a mathematical perspective, In this documentation, we first specify the available `Services` in the grid, and what requirements impose in the system, and later we discuss the implication on device formulations for specific units. +### Table of contents + +1. [`RangeReserve`](#RangeReserve) +2. [`StepwiseCostReserve`](#StepwiseCostReserve) +3. [`GroupReserve`](#GroupReserve) +4. [`RampReserve`](#RampReserve) +5. [`NonSpinningReserve`](#NonSpinningReserve) +6. [`ConstantMaxInterfaceFlow`](#ConstantMaxInterfaceFlow) + +--- + ## `RangeReserve` ```@docs @@ -20,6 +31,7 @@ For each service ``s`` of the model type `RangeReserve` the following variables - [`ActivePowerReserveVariable`](@ref): - Bounds: [0.0, ] + - Default proportional cost: ``1.0 / \text{SystemBasePower}`` - Symbol: ``r_{d}`` for ``d`` in contributing devices to the service ``s`` If slacks are enabled: - [`ReserveRequirementSlack`](@ref): @@ -54,11 +66,11 @@ mdtable(combo_table, latex = false) **Relevant Methods:** -``\mathcal{D}_s`` = `PowerSystems.get_contributing_devices(system, service)`: Set (vector) of all contributing devices to the service ``s`` in the system. +- ``\mathcal{D}_s`` = `PowerSystems.get_contributing_devices(system, service)`: Set (vector) of all contributing devices to the service ``s`` in the system. **Objective:** -Add a large proportional cost to the objective function if slack variables are used ``+ r^\text{sl} \cdot 10^5`` +Add a large proportional cost to the objective function if slack variables are used ``+ r^\text{sl} \cdot 10^5``. In addition adds the default cost for `ActivePowerReserveVariables` as a proportional cost. **Expressions:** @@ -92,13 +104,17 @@ r_{d,t} \le \text{Req} \cdot \text{PF} ,\quad \forall d\in \mathcal{D}_s, \foral r_{d,t} \le \text{RequirementTimeSeriesParameter}_{t} \cdot \text{PF}\quad \forall d\in \mathcal{D}_s, \forall t\in \{1,\dots, T\}, \quad \text{(for a VariableReserve)} ``` +--- + ## `StepwiseCostReserve` +Service must be used with `ReserveDemandCurve` `PowerSystems.jl` type. This service model is used to model ORDC (Operating Reserve Demand Curve) in ERCOT. + ```@docs StepwiseCostReserve ``` -For each service ``s`` of the model type `RangeReserve` the following variables are created: +For each service ``s`` of the model type `ReserveDemandCurve` the following variables are created: **Variables**: @@ -109,21 +125,147 @@ For each service ``s`` of the model type `RangeReserve` the following variables - Bounds: [0.0, ] - Symbol: ``\text{req}`` +**Time Series Parameters** + +For a `ReserveDemandCurve` `PowerSystems` type: +```@eval +using PowerSimulations +using PowerSystems +using DataFrames +using Latexify +combos = PowerSimulations.get_default_time_series_names(ReserveDemandCurve, StepwiseCostReserve) +combo_table = DataFrame( + "Parameter" => map(x -> "[`$x`](@ref)", collect(keys(combos))), + "Default Time Series Name" => map(x -> "`$x`", collect(values(combos))), + ) +mdtable(combo_table, latex = false) +``` + +**Relevant Methods:** + +- ``\mathcal{D}_s`` = `PowerSystems.get_contributing_devices(system, service)`: Set (vector) of all contributing devices to the service ``s`` in the system. + +**Objective:** + +The `ServiceRequirementVariable` is added as a piecewise linear cost based on the decreasing offers listed in the `variable_cost` time series. These decreasing cost represent the scarcity prices of not having sufficient reserves. For example, if the variable ``\text{req} = 0``, then a really high cost is paid for not having enough reserves, and if ``\text{req}`` is larger, then a lower cost (or even zero) is paid. TODO: actual implementation. + +**Expressions:** + +Adds the `ActivePowerReserveVariable` for upper/lower bound expressions of contributing devices. + +For `ReserveUp` types, the variable is added to `ActivePowerRangeExpressionUB`, such that this expression considers both the `ActivePowerVariable` and its reserve variable. Similarly, For `ReserveDown` types, the variable is added to `ActivePowerRangeExpressionLB`, such that this expression considers both the `ActivePowerVariable` and its reserve variable + + +*Example*: for a thermal unit ``d`` contributing to two different `ReserveUp` ``s_1, s_2`` services (e.g. Reg-Up and Spin): +```math +\text{ActivePowerRangeExpressionUB}_{t} = p_t^\text{th} + r_{s_1,t} + r_{s_2, t} \le P^\text{th,max} +``` +similarly if ``s_3`` is a `ReserveDown` service (e.g. Reg-Down): +```math +\text{ActivePowerRangeExpressionLB}_{t} = p_t^\text{th} - r_{s_3,t} \ge P^\text{th,min} +``` + +**Constraints:** + +A `StepwiseCostReserve` implements a single constraint, such that the sum of all reserves of contributing devices must be larger than the `ServiceRequirementVariable` variable. Thus, for a service ``s``: + +```math +\sum_{d\in\mathcal{D}_s} r_{d,t} \ge \text{req}_t,\quad \forall t\in \{1,\dots, T\} +``` + +## `GroupReserve` + +Service must be used with `StaticReserveGroup` `PowerSystems.jl` type. This service model is used to model an aggregation of services. + +```@docs +GroupReserve +``` + +For each service ``s`` of the model type `GroupReserve` the following variables are created: + +**Variables**: + +No variables are created, but the services associated with the `GroupReserve` must have created variables. + +**Static Parameters** + +- ``\text{Req}`` = `PowerSystems.get_requirement(service)` + +**Relevant Methods:** + +- ``\mathcal{S}_s`` = `PowerSystems.get_contributing_services(system, service)`: Set (vector) of all contributing services to the group service ``s`` in the system. +- ``\mathcal{D}_{s_i}`` = `PowerSystems.get_contributing_devices(system, service_aux)`: Set (vector) of all contributing devices to the service ``s_i`` in the system. + +**Objective:** + +Does not modify the objective function, besides the changes to the objective function due to the other services associated to the group service. + +**Expressions:** + +No changes, besides the changes to the expressions due to the other services associated to the group service. + +**Constraints:** + +A GroupReserve implements that the sum of all reserves of contributing devices, of all contributing services, must be larger than the `GroupReserve` requirement. Thus, for a `GroupReserve` service ``s``: + +```math +\sum_{d\in\mathcal{D}_{s_i}} \sum_{i \in \mathcal{S}_s} r_{d,t} \ge \text{Req},\quad \forall t\in \{1,\dots, T\} +``` + +--- + +## `RampReserve` + +```@docs +RampReserve +``` + +For each service ``s`` of the model type `RampReserve` the following variables are created: + +**Variables**: + +- [`ActivePowerReserveVariable`](@ref): + - Bounds: [0.0, ] + - Default proportional cost: ``1.0 / \text{SystemBasePower}`` + - Symbol: ``r_{d}`` for ``d`` in contributing devices to the service ``s`` +If slacks are enabled: +- [`ReserveRequirementSlack`](@ref): + - Bounds: [0.0, ] + - Default proportional cost: 1e5 + - Symbol: ``r^\text{sl}`` + +`RampReserve` only accepts `VariableReserve` `PowerSystems.jl` type. With that, the parameters are: + **Static Parameters** -TODO +- ``\text{TF}`` = `PowerSystems.get_time_frame(service)` +- ``R^\text{th,up}`` = `PowerSystems.get_ramp_limits(device).up` for thermal contributing devices +- ``R^\text{th,dn}`` = `PowerSystems.get_ramp_limits(device).down` for thermal contributing devices + **Time Series Parameters** -TODO +For a `VariableReserve` `PowerSystems` type: +```@eval +using PowerSimulations +using PowerSystems +using DataFrames +using Latexify +combos = PowerSimulations.get_default_time_series_names(VariableReserve, RampReserve) +combo_table = DataFrame( + "Parameter" => map(x -> "[`$x`](@ref)", collect(keys(combos))), + "Default Time Series Name" => map(x -> "`$x`", collect(values(combos))), + ) +mdtable(combo_table, latex = false) +``` **Relevant Methods:** -``\mathcal{D}_s`` = `PowerSystems.get_contributing_devices(system, service)`: Set (vector) of all contributing devices to the service ``s`` in the system. +- ``\mathcal{D}_s`` = `PowerSystems.get_contributing_devices(system, service)`: Set (vector) of all contributing devices to the service ``s`` in the system. **Objective:** -TODO +Add a large proportional cost to the objective function if slack variables are used ``+ r^\text{sl} \cdot 10^5``. In addition adds the default cost for `ActivePowerReserveVariables` as a proportional cost. **Expressions:** @@ -143,8 +285,166 @@ similarly if ``s_3`` is a `ReserveDown` service (e.g. Reg-Down): **Constraints:** -A `StepwiseCostReserve` implements a single constraint, such that the sum of all reserves of contributing devices must be larger than the `ServiceRequirementVariable` variable. Thus, for a service ``s``: +A RampReserve implements three fundamental constraints. The first is that the sum of all reserves of contributing devices must be larger than the `RampReserve` requirement. Thus, for a service ``s``: ```math -\sum_{d\in\mathcal{D}_s} r_{d,t} \ge \text{req}_t,\quad \forall t\in \{1,\dots, T\} +\sum_{d\in\mathcal{D}_s} r_{d,t} + r_t^\text{sl} \ge \text{RequirementTimeSeriesParameter}_{t},\quad \forall t\in \{1,\dots, T\} +``` + +Finally, there is a restriction based on the ramp limits of the contributing devices: + +```math +r_{d,t} \le R^\text{th,up} \cdot \text{TF}\quad \forall d\in \mathcal{D}_s, \forall t\in \{1,\dots, T\}, \quad \text{(for ReserveUp)} \\ +r_{d,t} \le R^\text{th,dn} \cdot \text{TF}\quad \forall d\in \mathcal{D}_s, \forall t\in \{1,\dots, T\}, \quad \text{(for ReserveDown)} +``` + +--- + +## `NonSpinningReserve` + +```@docs +NonSpinningReserve ``` + +For each service ``s`` of the model type `NonSpinningReserve`, the following variables are created: + +**Variables**: + +- [`ActivePowerReserveVariable`](@ref): + - Bounds: [0.0, ] + - Default proportional cost: ``1.0 / \text{SystemBasePower}`` + - Symbol: ``r_{d}`` for ``d`` in contributing devices to the service ``s`` +If slacks are enabled: +- [`ReserveRequirementSlack`](@ref): + - Bounds: [0.0, ] + - Default proportional cost: 1e5 + - Symbol: ``r^\text{sl}`` + +`NonSpinningReserve` only accepts `VariableReserve` `PowerSystems.jl` type. With that, the parameters are: + +**Static Parameters** + +- ``\text{PF}`` = `PowerSystems.get_max_participation_factor(service)` +- ``\text{TF}`` = `PowerSystems.get_time_frame(service)` +- ``P^\text{th,min}`` = `PowerSystems.get_active_power_limits(device).min` for thermal contributing devices +- ``T^\text{st,up}`` = `PowerSystems.get_time_limits(d).up` for thermal contributing devices +- ``R^\text{th,up}`` = `PowerSystems.get_ramp_limits(device).down` for thermal contributing devices + +Other parameters: + +- ``\Delta T``: Resolution of the problem in minutes. + +**Time Series Parameters** + +For a `VariableReserve` `PowerSystems` type: +```@eval +using PowerSimulations +using PowerSystems +using DataFrames +using Latexify +combos = PowerSimulations.get_default_time_series_names(VariableReserve, NonSpinningReserve) +combo_table = DataFrame( + "Parameter" => map(x -> "[`$x`](@ref)", collect(keys(combos))), + "Default Time Series Name" => map(x -> "`$x`", collect(values(combos))), + ) +mdtable(combo_table, latex = false) +``` + +**Relevant Methods:** + +- ``\mathcal{D}_s`` = `PowerSystems.get_contributing_devices(system, service)`: Set (vector) of all contributing devices to the service ``s`` in the system. + +**Objective:** + +Add a large proportional cost to the objective function if slack variables are used ``+ r^\text{sl} \cdot 10^5``. In addition adds the default cost for `ActivePowerReserveVariables` as a proportional cost. + +**Expressions:** + +Adds the `ActivePowerReserveVariable` for upper/lower bound expressions of contributing devices. + +For `ReserveUp` types, the variable is added to `ActivePowerRangeExpressionUB`, such that this expression considers both the `ActivePowerVariable` and its reserve variable. Similarly, For `ReserveDown` types, the variable is added to `ActivePowerRangeExpressionLB`, such that this expression considers both the `ActivePowerVariable` and its reserve variable + + +*Example*: for a thermal unit ``d`` contributing to two different `ReserveUp` ``s_1, s_2`` services (e.g. Reg-Up and Spin): +```math +\text{ActivePowerRangeExpressionUB}_{t} = p_t^\text{th} + r_{s_1,t} + r_{s_2, t} \le P^\text{th,max} +``` +similarly if ``s_3`` is a `ReserveDown` service (e.g. Reg-Down): +```math +\text{ActivePowerRangeExpressionLB}_{t} = p_t^\text{th} - r_{s_3,t} \ge P^\text{th,min} +``` + +**Constraints:** + +A NonSpinningReserve implements three fundamental constraints. The first is that the sum of all reserves of contributing devices must be larger than the `RampReserve` requirement. Thus, for a service ``s``: + +```math +\sum_{d\in\mathcal{D}_s} r_{d,t} + r_t^\text{sl} \ge \text{RequirementTimeSeriesParameter}_{t},\quad \forall t\in \{1,\dots, T\} +``` + +In addition, there is a restriction on how much each contributing device ``d`` can contribute to the requirement, based on the max participation factor allowed. + +```math +r_{d,t} \le \text{RequirementTimeSeriesParameter}_{t} \cdot \text{PF}\quad \forall d\in \mathcal{D}_s, \forall t\in \{1,\dots, T\}, +``` + +Finally, there is a restriction based on the reserve response time for the non-spinning reserve if the unit is off. To do so, compute ``R^\text{limit}_d`` as the reserve response limit as: +```math +R^\text{limit}_d = \begin{cases} +0 & \text{ if TF } \le T^\text{st,up}_d \\ +P^\text{th,min}_d + (\text{TF}_s - T^\text{st,up}_d) \cdot R^\text{th,up}_d \Delta T \cdot R^\text{th,up}_d & \text{ if TF } > T^\text{st,up}_d +\end{cases}, \quad \forall d\in \mathcal{D}_s +``` + +Then, the constraint depends on the commitment variable ``u_t^\text{th}`` as: + +```math +r_{d,t} \le (1 - u_{d,t}^\text{th}) \cdot R^\text{limit}_d +``` + +--- + +## `ConstantMaxInterfaceFlow` + +This Service model only accepts the `PowerSystems.jl` `TransmissionInterface` type to properly function. It is used to model a collection of branches that make up an interface or corridor with a maximum transfer of power. + +```@docs +ConstantMaxInterfaceFlow +``` + +**Variables** + +If slacks are used: +- [`InterfaceFlowSlackUp`](@ref): + - Bounds: [0.0, ] + - Symbol: ``f^\text{sl,up}`` +- [`InterfaceFlowSlackDown`](@ref): + - Bounds: [0.0, ] + - Symbol: ``f^\text{sl,dn}`` + +**Static Parameters** + +- ``F^\text{max}`` = `PowerSystems.get_active_power_flow_limits(service).max` +- ``F^\text{min}`` = `PowerSystems.get_active_power_flow_limits(service).min` +- ``C^\text{flow}`` = `PowerSystems.get_violation_penalty(service)` +- ``\mathcal{M}_s`` = `PowerSystems.get_direction_mapping(service)`. Dictionary of contributing branches with its specified direction (``\text{Dir}_d = 1`` or ``\text{Dir}_d = -1``) with respect to the interface. + +**Relevant Methods** + +- ``\mathcal{D}_s`` = `PowerSystems.get_contributing_devices(system, service)`: Set (vector) of all contributing branches to the service ``s`` in the system. + +**Objective:** + +Add the violation penalty proportional cost to the objective function if slack variables are used ``+ (f^\text{sl,up} + f^\text{sl,dn}) \cdot C^\text{flow}``. + +**Expressions:** + +Creates the expression `InterfaceTotalFlow` to keep track of all `FlowActivePowerVariable` of contributing branches to the transmission interface. + +**Constraints:** + +It adds the constraint to limit the `InterfaceTotalFlow` by the specified bounds of the service ``s``: + +```math +F^\text{min} \le f^\text{sl,up}_t - f^\text{sl,dn}_t + \sum_{d\in\mathcal{D}_s} \text{Dir}_d f_{d,t} \le F^\text{max}, \quad \forall t \in \{1,\dots,T\} +``` \ No newline at end of file From 2663eac5718ce7adc98dbf35cfd3abb70d536eb2 Mon Sep 17 00:00:00 2001 From: rodrigomha Date: Mon, 6 May 2024 17:19:22 -0700 Subject: [PATCH 45/69] update refs for API --- docs/src/api/PowerSimulations.md | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/docs/src/api/PowerSimulations.md b/docs/src/api/PowerSimulations.md index 050066b68f..713fb935e9 100644 --- a/docs/src/api/PowerSimulations.md +++ b/docs/src/api/PowerSimulations.md @@ -9,11 +9,11 @@ end ### Table of Contents -1. [Device Models](#device-models) -2. [Decision Models](#decision-models) -3. [Emulation Models](#emulation-models) -4. [Service Models](#service-models) -5. [Simulation Models](#simulation-models) +1. [Device Models](#Device-Models) +2. [Decision Models](#Decision-Models) +3. [Emulation Models](#Emulation-Models) +4. [Service Models](#Service-Models) +5. [Simulation Models](#Simulation-Models) 6. [Variables](#Variables) 7. [Constraints](#Constraints) 8. [Parameters](#Parameters) @@ -43,14 +43,6 @@ Refer to the [Problem Templates Page](https://nrel-siip.github.io/PowerSimulatio   ``` -# Service Models - -List of structures and methods for Service models - -```@docs -ServiceModel -``` - # Decision Models ```@docs @@ -81,6 +73,19 @@ run!(::EmulationModel)   ``` +# Service Models + +List of structures and methods for Service models + +```@docs +ServiceModel +``` + +```@raw html +  +  +``` + # Simulation Models Refer to the [Simulations Page](@ref running_a_simulation) to explanations on how to setup a Simulation, with Sequencing and Feedforwards. From c4f4bf5cef657de5cc4372043aa871a6c90231e9 Mon Sep 17 00:00:00 2001 From: rodrigomha Date: Mon, 6 May 2024 17:19:30 -0700 Subject: [PATCH 46/69] update definitions for services --- docs/src/modeler_guide/definitions.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/src/modeler_guide/definitions.md b/docs/src/modeler_guide/definitions.md index 53f1b47152..a8effa9ab5 100644 --- a/docs/src/modeler_guide/definitions.md +++ b/docs/src/modeler_guide/definitions.md @@ -55,6 +55,8 @@ Changing the attributes between `true` or `false` can enable/disable multiple as ## S +* *Service Formulation*: The model of a service that is incorporated into a large system optimization models. `Services` (or ancillary services) are models used to ensure that there is necessary support to the power grid from generators to consumers, in order to ensure reliable operation of the system. The most common application for ancillary services are reserves, i.e., generation (or load) that is not currently being used, but can be quickly made available in case of unexpected changes of grid conditions, for example a sudden loss of load or generation. A service model needs to follow some requirements to be integrated into operation problems. For more information about valid `ServiceModel`s and their mathematical representations, check out the [Formulation Library](@ref service_formulations). + * *Simulation*: A simulation is a pre-determined sequence of decision problems in a way that solving it, resembles the solution procedures commonly used by operators. The most common simulation model is the solution of a Unit Commitment and Economic Dispatch sequence of problems. * *Solver*: A solver is a software package that incorporates algorithms for finding solutions to one or more classes of optimization problem. For example, FICO Xpress is a commercial optimization solver for linear programming (LP), convex quadratic programming (QP) problems, convex quadratically constrained quadratic programming (QCQP), second-order cone programming (SOCP) and their mixed integer counterparts. **A solver is required to be specified** in order to solve any computer optimization problem. From 4cb7a7de6dc50477a323a5dbaf9939eb686eda3b Mon Sep 17 00:00:00 2001 From: rodrigomha Date: Mon, 6 May 2024 17:19:42 -0700 Subject: [PATCH 47/69] update docstrings --- src/core/constraints.jl | 5 +++++ src/core/formulations.jl | 14 +++++++++++++- src/core/variables.jl | 14 ++++++++++++-- 3 files changed, 30 insertions(+), 3 deletions(-) diff --git a/src/core/constraints.jl b/src/core/constraints.jl index ac0f75957e..e37e05d000 100644 --- a/src/core/constraints.jl +++ b/src/core/constraints.jl @@ -59,6 +59,11 @@ struct NodalBalanceActiveConstraint <: ConstraintType end struct NodalBalanceReactiveConstraint <: ConstraintType end struct ParticipationAssignmentConstraint <: ConstraintType end struct ParticipationFractionConstraint <: ConstraintType end +""" +Struct to create the PieceWiseLinearCostConstraint associated with a specified variable. + +See [Piecewise linear cost functions](@id pwl_cost) for more information. +""" struct PieceWiseLinearCostConstraint <: ConstraintType end struct RampConstraint <: ConstraintType end struct RampLimitConstraint <: ConstraintType end diff --git a/src/core/formulations.jl b/src/core/formulations.jl index b520a902dd..7486cd0327 100644 --- a/src/core/formulations.jl +++ b/src/core/formulations.jl @@ -229,6 +229,10 @@ abstract type AbstractAGCFormulation <: AbstractServiceFormulation end struct PIDSmoothACE <: AbstractAGCFormulation end + +""" +Struct to add reserves to be larger than a specified requirement for an aggregated collection of services +""" struct GroupReserve <: AbstractReservesFormulation end """ @@ -239,7 +243,15 @@ struct RangeReserve <: AbstractReservesFormulation end Struct for to add reserves to be larger than a variable requirement depending of costs """ struct StepwiseCostReserve <: AbstractReservesFormulation end +""" +Struct to add reserves to be larger than a specified requirement, with ramp constraints +""" struct RampReserve <: AbstractReservesFormulation end +""" +Struct to add non spinning reserve requirements larger than specified requirement +""" struct NonSpinningReserve <: AbstractReservesFormulation end - +""" +Struct to add a constant maximum transmission flow for specified interface +""" struct ConstantMaxInterfaceFlow <: AbstractServiceFormulation end diff --git a/src/core/variables.jl b/src/core/variables.jl index d91d3aff89..56b09933d5 100644 --- a/src/core/variables.jl +++ b/src/core/variables.jl @@ -190,14 +190,14 @@ struct FlowActivePowerSlackLowerBound <: VariableType end """ Struct to dispatch the creation of Voltage Magnitude Variables for AC formulations -Docs abbreviation: TODO +Docs abbreviation: v """ struct VoltageMagnitude <: VariableType end """ Struct to dispatch the creation of Voltage Angle Variables for AC/DC formulations -Docs abbreviation: TODO +Docs abbreviation: ``\\theta`` """ struct VoltageAngle <: VariableType end @@ -268,8 +268,18 @@ Docs abbreviation: ``\\delta`` """ struct PieceWiseLinearCostVariable <: VariableType end + +""" +Struct to dispatch the creation of Interface Flow Slack Up variables + +Docs abbreviation: ``f^\\text{sl,up}`` +""" struct InterfaceFlowSlackUp <: VariableType end +""" +Struct to dispatch the creation of Interface Flow Slack Down variables +Docs abbreviation: ``f^\\text{sl,dn}`` +""" struct InterfaceFlowSlackDown <: VariableType end struct UpperBoundFeedForwardSlack <: VariableType end From c0eac04b9e1adc06c02cb4c8e2786571f44df244 Mon Sep 17 00:00:00 2001 From: rodrigomha Date: Mon, 6 May 2024 17:21:14 -0700 Subject: [PATCH 48/69] formatter --- src/core/formulations.jl | 1 - src/core/variables.jl | 1 - 2 files changed, 2 deletions(-) diff --git a/src/core/formulations.jl b/src/core/formulations.jl index 7486cd0327..0bdffc9ad4 100644 --- a/src/core/formulations.jl +++ b/src/core/formulations.jl @@ -229,7 +229,6 @@ abstract type AbstractAGCFormulation <: AbstractServiceFormulation end struct PIDSmoothACE <: AbstractAGCFormulation end - """ Struct to add reserves to be larger than a specified requirement for an aggregated collection of services """ diff --git a/src/core/variables.jl b/src/core/variables.jl index 56b09933d5..1846a0cfc3 100644 --- a/src/core/variables.jl +++ b/src/core/variables.jl @@ -268,7 +268,6 @@ Docs abbreviation: ``\\delta`` """ struct PieceWiseLinearCostVariable <: VariableType end - """ Struct to dispatch the creation of Interface Flow Slack Up variables From b62f2b8a2b4e224efbbc7ca5277fcbb6f3592402 Mon Sep 17 00:00:00 2001 From: rodrigomha Date: Mon, 6 May 2024 17:41:05 -0700 Subject: [PATCH 49/69] add AreaDispatchBalance constraint --- docs/src/api/PowerSimulations.md | 1 - src/core/constraints.jl | 12 +++++++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/docs/src/api/PowerSimulations.md b/docs/src/api/PowerSimulations.md index 713fb935e9..d29acca055 100644 --- a/docs/src/api/PowerSimulations.md +++ b/docs/src/api/PowerSimulations.md @@ -181,7 +181,6 @@ PieceWiseLinearCostConstraint ```@docs AreaDispatchBalanceConstraint -AreaParticipationAssignmentConstraint BalanceAuxConstraint CopperPlateBalanceConstraint FrequencyResponseConstraint diff --git a/src/core/constraints.jl b/src/core/constraints.jl index e37e05d000..feecd3adaa 100644 --- a/src/core/constraints.jl +++ b/src/core/constraints.jl @@ -27,6 +27,16 @@ Base.convert(::Type{ConstraintKey}, name::Symbol) = ConstraintKey(decode_symbol( struct AbsoluteValueConstraint <: ConstraintType end struct ActiveRangeICConstraint <: ConstraintType end +""" +Struct to create the constraint to balance power across specified areas. +For more information check [Network Formulations](@ref network_formulations). + +The specified constraint is generally formulated as: + +```math +\\sum_{c \\in \\text{components}_a} p_t^c = 0, \\quad \\forall a\\in \\{1,\\dots, A\\}, t \\in \\{1, \\dots, T\\} +``` +""" struct AreaDispatchBalanceConstraint <: ConstraintType end struct AreaParticipationAssignmentConstraint <: ConstraintType end struct BalanceAuxConstraint <: ConstraintType end @@ -62,7 +72,7 @@ struct ParticipationFractionConstraint <: ConstraintType end """ Struct to create the PieceWiseLinearCostConstraint associated with a specified variable. -See [Piecewise linear cost functions](@id pwl_cost) for more information. +See [Piecewise linear cost functions](@ref pwl_cost) for more information. """ struct PieceWiseLinearCostConstraint <: ConstraintType end struct RampConstraint <: ConstraintType end From a10fc2d194a962b8fea505677d9eb3e231593190 Mon Sep 17 00:00:00 2001 From: rodrigomha Date: Mon, 6 May 2024 17:45:42 -0700 Subject: [PATCH 50/69] update network constraint docstrings --- src/core/constraints.jl | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/core/constraints.jl b/src/core/constraints.jl index feecd3adaa..9a4b90c4d9 100644 --- a/src/core/constraints.jl +++ b/src/core/constraints.jl @@ -41,6 +41,16 @@ struct AreaDispatchBalanceConstraint <: ConstraintType end struct AreaParticipationAssignmentConstraint <: ConstraintType end struct BalanceAuxConstraint <: ConstraintType end struct CommitmentConstraint <: ConstraintType end +""" +Struct to create the constraint to balance power in the copperplate model. +For more information check [Network Formulations](@ref network_formulations). + +The specified constraint is generally formulated as: + +```math +\\sum_{c \\in \\text{components}} p_t^c = 0, \\quad \\forall t \\in \\{1, \\dots, T\\} +``` +""" struct CopperPlateBalanceConstraint <: ConstraintType end struct DurationConstraint <: ConstraintType end struct EnergyBalanceConstraint <: ConstraintType end @@ -65,7 +75,19 @@ struct FlowReactivePowerToFromConstraint <: ConstraintType end #not being used struct HVDCPowerBalance <: ConstraintType end struct FrequencyResponseConstraint <: ConstraintType end struct NetworkFlowConstraint <: ConstraintType end +""" +Struct to create the constraint to balance active power in nodal formulation. +For more information check [Network Formulations](@ref network_formulations). + +The specified constraint depends on the network model choosen. +""" struct NodalBalanceActiveConstraint <: ConstraintType end +""" +Struct to create the constraint to balance reactive power in nodal formulation. +For more information check [Network Formulations](@ref network_formulations). + +The specified constraint depends on the network model choosen. +""" struct NodalBalanceReactiveConstraint <: ConstraintType end struct ParticipationAssignmentConstraint <: ConstraintType end struct ParticipationFractionConstraint <: ConstraintType end From c9ebafbf764d80eefb5b7db4218757c49dc34ada Mon Sep 17 00:00:00 2001 From: rodrigomha Date: Mon, 6 May 2024 17:52:09 -0700 Subject: [PATCH 51/69] update power variable limit constraints --- docs/src/api/PowerSimulations.md | 2 -- src/core/constraints.jl | 33 ++++++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/docs/src/api/PowerSimulations.md b/docs/src/api/PowerSimulations.md index d29acca055..532bae5487 100644 --- a/docs/src/api/PowerSimulations.md +++ b/docs/src/api/PowerSimulations.md @@ -181,9 +181,7 @@ PieceWiseLinearCostConstraint ```@docs AreaDispatchBalanceConstraint -BalanceAuxConstraint CopperPlateBalanceConstraint -FrequencyResponseConstraint NodalBalanceActiveConstraint NodalBalanceReactiveConstraint ``` diff --git a/src/core/constraints.jl b/src/core/constraints.jl index 9a4b90c4d9..83b6634fca 100644 --- a/src/core/constraints.jl +++ b/src/core/constraints.jl @@ -119,8 +119,41 @@ struct InterfaceFlowLimit <: ConstraintType end abstract type PowerVariableLimitsConstraint <: ConstraintType end struct InputActivePowerVariableLimitsConstraint <: PowerVariableLimitsConstraint end struct OutputActivePowerVariableLimitsConstraint <: PowerVariableLimitsConstraint end +""" +Struct to create the constraint to limit active power expressions. +For more information check [Device Formulations](@ref formulation_intro). + +The specified constraint depends on the UpperBound and LowerBound expressions, but +in its most basic formulation is of the form: + +```math +P^\\text{min} \\le p_t \\le P^\\text{max}, \\quad \\forall t \\in \\{1,\\dots,T\\} +``` +""" struct ActivePowerVariableLimitsConstraint <: PowerVariableLimitsConstraint end +""" +Struct to create the constraint to limit reactive power expressions. +For more information check [Device Formulations](@ref formulation_intro). + +The specified constraint depends on the UpperBound and LowerBound expressions, but +in its most basic formulation is of the form: + +```math +Q^\\text{min} \\le q_t \\le Q^\\text{max}, \\quad \\forall t \\in \\{1,\\dots,T\\} +``` +""" struct ReactivePowerVariableLimitsConstraint <: PowerVariableLimitsConstraint end +""" +Struct to create the constraint to limit active power expressions by a time series parameter. +For more information check [Device Formulations](@ref formulation_intro). + +The specified constraint depends on the UpperBound expressions, but +in its most basic formulation is of the form: + +```math +p_t \\le \\text{ActivePowerTimeSeriesParameter}_t, \\quad \\forall t \\in \\{1,\\dots,T\\} +``` +""" struct ActivePowerVariableTimeSeriesLimitsConstraint <: PowerVariableLimitsConstraint end # These apply to the processing of constraint duals From fc7cf847ee2bb814c5977ca1e034d44b940acb27 Mon Sep 17 00:00:00 2001 From: rodrigomha Date: Mon, 6 May 2024 20:47:51 -0700 Subject: [PATCH 52/69] add service docstrings --- docs/src/api/PowerSimulations.md | 6 +- docs/src/formulation_library/Service.md | 2 +- src/core/constraints.jl | 73 ++++++++++++++++++++++++- 3 files changed, 74 insertions(+), 7 deletions(-) diff --git a/docs/src/api/PowerSimulations.md b/docs/src/api/PowerSimulations.md index 532bae5487..98da886c02 100644 --- a/docs/src/api/PowerSimulations.md +++ b/docs/src/api/PowerSimulations.md @@ -196,13 +196,11 @@ InputActivePowerVariableLimitsConstraint OutputActivePowerVariableLimitsConstraint ``` -### Regulation and Services Constraints +### Services Constraints ```@docs -ParticipationAssignmentConstraint -RegulationLimitsConstraint RequirementConstraint -ReserveEnergyCoverageConstraint +ParticipationFractionConstraint ReservePowerConstraint ``` diff --git a/docs/src/formulation_library/Service.md b/docs/src/formulation_library/Service.md index 6509bd53c5..0951e1b859 100644 --- a/docs/src/formulation_library/Service.md +++ b/docs/src/formulation_library/Service.md @@ -399,7 +399,7 @@ P^\text{th,min}_d + (\text{TF}_s - T^\text{st,up}_d) \cdot R^\text{th,up}_d \De Then, the constraint depends on the commitment variable ``u_t^\text{th}`` as: ```math -r_{d,t} \le (1 - u_{d,t}^\text{th}) \cdot R^\text{limit}_d +r_{d,t} \le (1 - u_{d,t}^\text{th}) \cdot R^\text{limit}_d, \quad \forall d \in \mathcal{D}_s, \forall t \in \{1,\dots, T\} ``` --- diff --git a/src/core/constraints.jl b/src/core/constraints.jl index 83b6634fca..dc86dfbf89 100644 --- a/src/core/constraints.jl +++ b/src/core/constraints.jl @@ -79,17 +79,28 @@ struct NetworkFlowConstraint <: ConstraintType end Struct to create the constraint to balance active power in nodal formulation. For more information check [Network Formulations](@ref network_formulations). -The specified constraint depends on the network model choosen. +The specified constraint depends on the network model chosen. """ struct NodalBalanceActiveConstraint <: ConstraintType end """ Struct to create the constraint to balance reactive power in nodal formulation. For more information check [Network Formulations](@ref network_formulations). -The specified constraint depends on the network model choosen. +The specified constraint depends on the network model chosen. """ struct NodalBalanceReactiveConstraint <: ConstraintType end struct ParticipationAssignmentConstraint <: ConstraintType end +""" +Struct to create the constraint to participation assignments limits in the active power reserves. +For more information check [Service Formulations](@ref service_formulations). + +The constraint is as follows: + +```math +r_{d,t} \\le \\text{Req} \\cdot \\text{PF} ,\\quad \\forall d\\in \\mathcal{D}_s, \\forall t\\in \\{1,\\dots, T\\} \\quad \\text{(for a StaticReserve)} \\\\ +r_{d,t} \\le \\text{RequirementTimeSeriesParameter}_{t} \\cdot \\text{PF}\\quad \\forall d\\in \\mathcal{D}_s, \\forall t\\in \\{1,\\dots, T\\}, \\quad \\text{(for a VariableReserve)} +``` +""" struct ParticipationFractionConstraint <: ConstraintType end """ Struct to create the PieceWiseLinearCostConstraint associated with a specified variable. @@ -97,6 +108,21 @@ Struct to create the PieceWiseLinearCostConstraint associated with a specified v See [Piecewise linear cost functions](@ref pwl_cost) for more information. """ struct PieceWiseLinearCostConstraint <: ConstraintType end +""" +Struct to create the RampConstraint associated with a specified thermal device or reserve service. + +For thermal units, see more information in [Thermal Formulations](@ref ThermalGen-Formulations). The constraint is as follows: +```math +-R^\\text{th,dn} \\le p_t^\\text{th} - p_{t-1}^\\text{th} \\le R^\\text{th,up}, \\quad \\forall t\\in \\{1, \\dots, T\\} +``` + +For Ramp Reserve, see more information in [Service Formulations](@ref service_formulations). The constraint is as follows: + +```math +r_{d,t} \\le R^\\text{th,up} \\cdot \\text{TF}\\quad \\forall d\\in \\mathcal{D}_s, \\forall t\\in \\{1,\\dots, T\\}, \\quad \\text{(for ReserveUp)} \\\\ +r_{d,t} \\le R^\\text{th,dn} \\cdot \\text{TF}\\quad \\forall d\\in \\mathcal{D}_s, \\forall t\\in \\{1,\\dots, T\\}, \\quad \\text{(for ReserveDown)} +``` +""" struct RampConstraint <: ConstraintType end struct RampLimitConstraint <: ConstraintType end struct RangeLimitConstraint <: ConstraintType end @@ -104,8 +130,29 @@ struct RateLimitConstraint <: ConstraintType end struct RateLimitConstraintFromTo <: ConstraintType end struct RateLimitConstraintToFrom <: ConstraintType end struct RegulationLimitsConstraint <: ConstraintType end +""" +Struct to create the constraint for satisfying active power reserve requirements. +For more information check [Service Formulations](@ref service_formulations). + +The constraint is as follows: + +```math +\\sum_{d\\in\\mathcal{D}_s} r_{d,t} + r_t^\\text{sl} \\ge \\text{Req},\\quad \\forall t\\in \\{1,\\dots, T\\} \\quad \\text{(for a StaticReserve)} \\\\ +\\sum_{d\\in\\mathcal{D}_s} r_{d,t} + r_t^\\text{sl} \\ge \\text{RequirementTimeSeriesParameter}_{t},\\quad \\forall t\\in \\{1,\\dots, T\\} \\quad \\text{(for a VariableReserve)} +``` +""" struct RequirementConstraint <: ConstraintType end struct ReserveEnergyCoverageConstraint <: ConstraintType end +""" +Struct to create the constraint for ensuring that NonSpinning Reserve can be delivered from turn-off thermal units. +For more information check [Service Formulations](@ref service_formulations) for NonSpinningReserve. + +The constraint is as follows: + +```math +r_{d,t} \\le (1 - u_{d,t}^\\text{th}) \\cdot R^\\text{limit}_d, \\quad \\forall d \\in \\mathcal{D}_s, \\forall t \\in \\{1,\\dots, T\\} +``` +""" struct ReservePowerConstraint <: ConstraintType end struct SACEPIDAreaConstraint <: ConstraintType end struct StartTypeConstraint <: ConstraintType end @@ -117,7 +164,29 @@ struct HVDCDirection <: ConstraintType end struct InterfaceFlowLimit <: ConstraintType end abstract type PowerVariableLimitsConstraint <: ConstraintType end +""" +Struct to create the constraint to limit active power input expressions. +For more information check [Device Formulations](@ref formulation_intro). + +The specified constraint depends on the UpperBound and LowerBound expressions, but +in its most basic formulation is of the form: + +```math +P^\\text{min} \\le p_t^\\text{in} \\le P^\\text{max}, \\quad \\forall t \\in \\{1,\\dots,T\\} +``` +""" struct InputActivePowerVariableLimitsConstraint <: PowerVariableLimitsConstraint end +""" +Struct to create the constraint to limit active power output expressions. +For more information check [Device Formulations](@ref formulation_intro). + +The specified constraint depends on the UpperBound and LowerBound expressions, but +in its most basic formulation is of the form: + +```math +P^\\text{min} \\le p_t^\\text{out} \\le P^\\text{max}, \\quad \\forall t \\in \\{1,\\dots,T\\} +``` +""" struct OutputActivePowerVariableLimitsConstraint <: PowerVariableLimitsConstraint end """ Struct to create the constraint to limit active power expressions. From 621aeb29c46e7e1d7cbfba70cbb656f8ae65d19f Mon Sep 17 00:00:00 2001 From: rodrigomha Date: Mon, 6 May 2024 21:21:49 -0700 Subject: [PATCH 53/69] add more docstrings --- docs/src/api/PowerSimulations.md | 6 +-- docs/src/formulation_library/ThermalGen.md | 5 ++- src/core/constraints.jl | 51 ++++++++++++++++++++++ 3 files changed, 57 insertions(+), 5 deletions(-) diff --git a/docs/src/api/PowerSimulations.md b/docs/src/api/PowerSimulations.md index 98da886c02..879ec52753 100644 --- a/docs/src/api/PowerSimulations.md +++ b/docs/src/api/PowerSimulations.md @@ -161,6 +161,8 @@ ServiceRequirementVariable SystemBalanceSlackUp SystemBalanceSlackDown ReserveRequirementSlack +InterfaceFlowSlackUp +InterfaceFlowSlackDown ``` ```@raw html @@ -211,7 +213,6 @@ ActiveRangeICConstraint CommitmentConstraint DurationConstraint RampConstraint -RampLimitConstraint StartupInitialConditionConstraint StartupTimeLimitTemperatureConstraint ``` @@ -220,7 +221,6 @@ StartupTimeLimitTemperatureConstraint ```@docs EqualityConstraint - ``` ### Branches Constraints @@ -268,9 +268,7 @@ RequirementTimeSeriesParameter UpperBoundValueParameter LowerBoundValueParameter OnStatusParameter -EnergyLimitParameter FixValueParameter -EnergyTargetParameter ``` ### Objective Function Parameters diff --git a/docs/src/formulation_library/ThermalGen.md b/docs/src/formulation_library/ThermalGen.md index c9afd60bfa..86143df4b8 100644 --- a/docs/src/formulation_library/ThermalGen.md +++ b/docs/src/formulation_library/ThermalGen.md @@ -586,6 +586,8 @@ ThermalMultiStartUnitCommitment - ``D^\text{cold}`` = `PowerSystems.get_start_time_limits(device).cold` - ``D^\text{warm}`` = `PowerSystems.get_start_time_limits(device).warm` - ``D^\text{hot}`` = `PowerSystems.get_start_time_limits(device).hot` +- ``P^\text{th,startup}`` = `PowerSystems.get_power_trajectory(device).startup` +- ``P^\text{th, shdown}`` = `PowerSystems.get_power_trajectory(device).shutdown` **Objective:** @@ -608,7 +610,8 @@ For each thermal unit creates the range constraints for its active and reactive & -R^\text{th,dn} \le \Delta p_t^\text{th} - \Delta p_{t-1}^\text{th} \le R^\text{th,up}, \quad \forall t\in \{2, \dots, T\} \\ & u_1^\text{th} = u^\text{th,init} + v_1^\text{th} - w_1^\text{th} \\ & u_t^\text{th} = u_{t-1}^\text{th} + v_t^\text{th} - w_t^\text{th}, \quad \forall t \in \{2,\dots,T\} \\ -& v_t^\text{th} + w_t^\text{th} \le 1, \quad \forall t \in \{1,\dots,T\} +& v_t^\text{th} + w_t^\text{th} \le 1, \quad \forall t \in \{1,\dots,T\} \\ +& \max\{P^\text{th,max} - P^\text{th,shdown}, 0\} \cdot w_1^\text{th} \le u^\text{th,init} (P^\text{th,max} - P^\text{th,min}) - P^\text{th,init} \end{align*} ``` diff --git a/src/core/constraints.jl b/src/core/constraints.jl index dc86dfbf89..bcd08cb881 100644 --- a/src/core/constraints.jl +++ b/src/core/constraints.jl @@ -26,6 +26,16 @@ end Base.convert(::Type{ConstraintKey}, name::Symbol) = ConstraintKey(decode_symbol(name)...) struct AbsoluteValueConstraint <: ConstraintType end +""" +Struct to create the constraint for starting up ThermalMultiStart units. +For more information check [ThermalGen Formulations](@ref ThermalGen-Formulations) for ThermalMultiStartUnitCommitment. + +The specified constraint is formulated as: + +```math +\\max\\{P^\\text{th,max} - P^\\text{th,shdown}, 0\\} \\cdot w_1^\\text{th} \\le u^\\text{th,init} (P^\\text{th,max} - P^\\text{th,min}) - P^\\text{th,init} +``` +""" struct ActiveRangeICConstraint <: ConstraintType end """ Struct to create the constraint to balance power across specified areas. @@ -40,6 +50,18 @@ The specified constraint is generally formulated as: struct AreaDispatchBalanceConstraint <: ConstraintType end struct AreaParticipationAssignmentConstraint <: ConstraintType end struct BalanceAuxConstraint <: ConstraintType end +""" +Struct to create the commitment constraint between the on, start, and stop variables. +For more information check [ThermalGen Formulations](@ref ThermalGen-Formulations). + +The specified constraints are formulated as: + +```math +u_1^\\text{th} = u^\\text{th,init} + v_1^\\text{th} - w_1^\\text{th} \\\\ +u_t^\\text{th} = u_{t-1}^\\text{th} + v_t^\\text{th} - w_t^\\text{th}, \\quad \\forall t \\in \\{2,\\dots,T\\} \\\\ +v_t^\\text{th} + w_t^\\text{th} \\le 1, \\quad \\forall t \\in \\{1,\\dots,T\\} +``` +""" struct CommitmentConstraint <: ConstraintType end """ Struct to create the constraint to balance power in the copperplate model. @@ -52,8 +74,26 @@ The specified constraint is generally formulated as: ``` """ struct CopperPlateBalanceConstraint <: ConstraintType end +""" +Struct to create the duration constraint for commitment formulations, i.e. min-up and min-down. + +For more information check [ThermalGen Formulations](@ref ThermalGen-Formulations). +""" struct DurationConstraint <: ConstraintType end struct EnergyBalanceConstraint <: ConstraintType end + +""" +Struct to create the constraint that sets the reactive power to the power factor +in the RenewableConstantPowerFactor formulation for renewable units. + +For more information check [RenewableGen Formulations](@ref RenewableGen-Formulations). + +The specified constraint is formulated as: + +```math +q_t^\\text{re} = \\text{pf} \\cdot p_t^\\text{re}, \\quad \\forall t \\in \\{1,\\dots, T\\} +``` +""" struct EqualityConstraint <: ConstraintType end struct FeedforwardSemiContinousConstraint <: ConstraintType end struct FeedforwardIntegralLimitConstraint <: ConstraintType end @@ -145,6 +185,7 @@ struct RequirementConstraint <: ConstraintType end struct ReserveEnergyCoverageConstraint <: ConstraintType end """ Struct to create the constraint for ensuring that NonSpinning Reserve can be delivered from turn-off thermal units. + For more information check [Service Formulations](@ref service_formulations) for NonSpinningReserve. The constraint is as follows: @@ -156,7 +197,17 @@ r_{d,t} \\le (1 - u_{d,t}^\\text{th}) \\cdot R^\\text{limit}_d, \\quad \\forall struct ReservePowerConstraint <: ConstraintType end struct SACEPIDAreaConstraint <: ConstraintType end struct StartTypeConstraint <: ConstraintType end +""" +Struct to create the start-up initial condition constraints for ThermalMultiStart. + +For more information check [ThermalGen Formulations](@ref ThermalGen-Formulations) for ThermalMultiStartUnitCommitment. +""" struct StartupInitialConditionConstraint <: ConstraintType end +""" +Struct to create the start-up time limit constraints for ThermalMultiStart. + +For more information check [ThermalGen Formulations](@ref ThermalGen-Formulations) for ThermalMultiStartUnitCommitment. +""" struct StartupTimeLimitTemperatureConstraint <: ConstraintType end struct PhaseAngleControlLimit <: ConstraintType end struct HVDCLossesAbsoluteValue <: ConstraintType end From 8916b5ebf98c0d70ee27ae824de9825ef70a4439 Mon Sep 17 00:00:00 2001 From: rodrigomha Date: Mon, 6 May 2024 21:27:32 -0700 Subject: [PATCH 54/69] update docstrings --- src/PowerSimulations.jl | 2 ++ src/core/constraints.jl | 2 +- src/core/variables.jl | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/PowerSimulations.jl b/src/PowerSimulations.jl index b1273831cc..9990b64d02 100644 --- a/src/PowerSimulations.jl +++ b/src/PowerSimulations.jl @@ -233,6 +233,8 @@ export PowerAboveMinimumVariable export PhaseShifterAngle export UpperBoundFeedForwardSlack export LowerBoundFeedForwardSlack +export InterfaceFlowSlackUp +export InterfaceFlowSlackDown # Auxiliary variables export TimeDurationOn diff --git a/src/core/constraints.jl b/src/core/constraints.jl index bcd08cb881..28307331bf 100644 --- a/src/core/constraints.jl +++ b/src/core/constraints.jl @@ -86,7 +86,7 @@ struct EnergyBalanceConstraint <: ConstraintType end Struct to create the constraint that sets the reactive power to the power factor in the RenewableConstantPowerFactor formulation for renewable units. -For more information check [RenewableGen Formulations](@ref RenewableGen-Formulations). +For more information check [RenewableGen Formulations](@ref PowerSystems.RenewableGen-Formulations). The specified constraint is formulated as: diff --git a/src/core/variables.jl b/src/core/variables.jl index 1846a0cfc3..297dcfbd62 100644 --- a/src/core/variables.jl +++ b/src/core/variables.jl @@ -190,7 +190,7 @@ struct FlowActivePowerSlackLowerBound <: VariableType end """ Struct to dispatch the creation of Voltage Magnitude Variables for AC formulations -Docs abbreviation: v +Docs abbreviation: ``v`` """ struct VoltageMagnitude <: VariableType end From 80140d3d731472b9f70064db94f8fbaa428714a3 Mon Sep 17 00:00:00 2001 From: rodrigomha Date: Fri, 10 May 2024 20:37:04 -0700 Subject: [PATCH 55/69] update ff docstrings and docs --- docs/make.jl | 1 + docs/src/formulation_library/Feedforward.md | 59 +++++++++++++++++++++ src/feedforward/feedforwards.jl | 20 ++++++- 3 files changed, 78 insertions(+), 2 deletions(-) create mode 100644 docs/src/formulation_library/Feedforward.md diff --git a/docs/make.jl b/docs/make.jl index f948e0453f..ce9640a385 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -39,6 +39,7 @@ pages = OrderedDict( "Load" => "formulation_library/Load.md", "Branch" => "formulation_library/Branch.md", "Services" => "formulation_library/Service.md", + "Feedforwards" => "formulation_library/Feedforward.md", "Piecewise Linear Cost" => "formulation_library/Piecewise.md", ], "API Reference" => "api/PowerSimulations.md", diff --git a/docs/src/formulation_library/Feedforward.md b/docs/src/formulation_library/Feedforward.md new file mode 100644 index 0000000000..47c094f260 --- /dev/null +++ b/docs/src/formulation_library/Feedforward.md @@ -0,0 +1,59 @@ +# [FeedForward Formulations](@id ff_formulations) + +In PowerSimulations, chronologies define where information is flowing. There are two types +of chronologies. + +- inter-stage chronologies: Define how information flows between stages. e.g. day-ahead solutions are used to inform economic dispatch problems +- intra-stage chronologies: Define how information flows between multiple executions of a single stage. e.g. the dispatch setpoints of the first period of an economic dispatch problem are constrained by the ramping limits from setpoints in the final period of the previous problem. + +The definition of exactly what information is passed using the defined chronologies is accomplished using **FeedForwards**. + +Specifically, a FeedForward is used to define what to do with information being passed with an inter-stage chronology in a Simulation. The most common FeedForward is the `SemiContinuousFeedForward` that affects the semi-continuous range constraints of thermal generators in the economic dispatch problems based on the value of the (already solved) unit-commitment variables. + +The creation of a FeedForward requires at least to specify the `component_type` on which the FeedForward will be applied. The `source` variable specify which variable will be taken from the problem solved, for example the commitment variable of the thermal unit in the unit commitment problem. Finally, the `affected_values` specify which variables will be affected in the problem to be solved, for example the next economic dispatch problem. + +### Table of contents + +1. [`SemiContinuousFeedforward`](#SemiContinuousFeedForward) +2. [`FixValueFeedforward`](#FixValueFeedforward) + + +--- + +## `SemiContinuousFeedforward` + +```@docs +SemiContinuousFeedforward +``` + +**Variables:** + +No variables are created + +**Parameters:** + +- ``\text{on}^\text{th}`` = `OnStatusParameter` obtained from the source variable, typically the commitment variable of the unit commitment problem ``u^\text{th}``. + +**Objective:** + +No changes to the objective function. + +**Expressions:** + +Adds ``-\text{on}^\text{th}P^\text{th,max}`` to the `ActivePowerRangeExpressionUB` expression and ``-\text{on}^\text{th}P^\text{th,min}`` to the `ActivePowerRangeExpressionLB` expression. + +**Constraints:** + +Limits the `ActivePowerRangeExpressionUB` and `ActivePowerRangeExpressionLB` by zero as: + +```math +\begin{align*} +& \text{ActivePowerRangeExpressionUB}_t := p_t^\text{th} - \text{on}_t^\text{th}P^\text{th,max} \le 0, \quad \forall t\in \{1, \dots, T\} \\ +& \text{ActivePowerRangeExpressionLB}_t := p_t^\text{th} - \text{on}_t^\text{th}P^\text{th,min} \ge 0, \quad \forall t\in \{1, \dots, T\} +\end{align*} +``` + +Thus, if the commitment parameter is zero, the dispatch is limited to zero, forcing to turn off the generator without introducing binary variables in the economic dispatch problem. + +## `FixValueFeedforward` + diff --git a/src/feedforward/feedforwards.jl b/src/feedforward/feedforwards.jl index 5a503a15b0..9d541dae6c 100644 --- a/src/feedforward/feedforwards.jl +++ b/src/feedforward/feedforwards.jl @@ -149,7 +149,16 @@ function attach_feedforward!( end """ -Adds a constraint to make the bounds of a variable 0.0. Effectively allows to "turn off" a value. + SemiContinuousFeedforward( + component_type::Type{<:PSY.Component}, + source::Type{T}, + affected_values::Vector{DataType}, + meta = CONTAINER_KEY_EMPTY_META + ) where {T} + +It allows to enable/disable bounds to 0.0 for a specified variable. Commonly used to limit the +`ActivePowerVariable` in an Economic Dispatch problem by the commitment decision taken in +an another problem (typically a Unit Commitment problem). """ struct SemiContinuousFeedforward <: AbstractAffectFeedforward optimization_container_key::OptimizationContainerKey @@ -203,7 +212,14 @@ function has_semicontinuous_feedforward( end """ -Fixes a Variable or Parameter Value in the model. Is the only Feed Forward that can be used + FixValueFeedforward( + component_type::Type{<:PSY.Component}, + source::Type{T}, + affected_values::Vector{DataType}, + meta = CONTAINER_KEY_EMPTY_META + ) where {T} + +Fixes a Variable or Parameter Value in the model from another problem. Is the only Feed Forward that can be used with a Parameter or a Variable as the affected value. """ struct FixValueFeedforward <: AbstractAffectFeedforward From 55ff09c238a2936dce6d5d1398fd1c5ce90d9ac7 Mon Sep 17 00:00:00 2001 From: rodrigomha Date: Fri, 10 May 2024 20:37:13 -0700 Subject: [PATCH 56/69] remove ref to export results --- src/simulation/simulation.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/simulation/simulation.jl b/src/simulation/simulation.jl index aa0a77754f..7a48d7a867 100644 --- a/src/simulation/simulation.jl +++ b/src/simulation/simulation.jl @@ -968,7 +968,7 @@ Solves the simulation model for sequential Simulations. - `sim::Simulation=sim`: simulation object created by Simulation() The optional keyword argument `exports` controls exporting of results to CSV files as -the simulation runs. Refer to [`export_results`](@ref) for a description of this argument. +the simulation runs. # Example ```julia From 9241aecac4e9cb7fe6fb24849afd00791a841f68 Mon Sep 17 00:00:00 2001 From: rodrigomha Date: Fri, 10 May 2024 20:37:25 -0700 Subject: [PATCH 57/69] remove ref in docs --- docs/src/tutorials/basics_of_developing_models.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/tutorials/basics_of_developing_models.md b/docs/src/tutorials/basics_of_developing_models.md index a027918d6a..654ec25da0 100644 --- a/docs/src/tutorials/basics_of_developing_models.md +++ b/docs/src/tutorials/basics_of_developing_models.md @@ -1,3 +1,3 @@ # Basics of Developing Operation Models -Check the page [PowerSimulations Structure](@ref) for more background on PowerSimulations.jl +Check the page PowerSimulations Structure for more background on PowerSimulations.jl From 9ac53f804b0113642a655b1c287030119798c8c6 Mon Sep 17 00:00:00 2001 From: rodrigomha Date: Fri, 10 May 2024 20:37:40 -0700 Subject: [PATCH 58/69] finish reserve docs --- docs/src/api/PowerSimulations.md | 58 ++++++++++++++++++++----- docs/src/formulation_library/Service.md | 49 ++++++++++++++++++++- 2 files changed, 93 insertions(+), 14 deletions(-) diff --git a/docs/src/api/PowerSimulations.md b/docs/src/api/PowerSimulations.md index 879ec52753..139ab76e3b 100644 --- a/docs/src/api/PowerSimulations.md +++ b/docs/src/api/PowerSimulations.md @@ -10,13 +10,36 @@ end ### Table of Contents 1. [Device Models](#Device-Models) + - [Formulations](#Formulations) + - [Problem Templates](#Problem-Templates) 2. [Decision Models](#Decision-Models) 3. [Emulation Models](#Emulation-Models) 4. [Service Models](#Service-Models) 5. [Simulation Models](#Simulation-Models) 6. [Variables](#Variables) + - [Common Variables](#Common-Variables) + - [Thermal Unit Variables](#Thermal-Unit-Variables) + - [Storage Unit Variables](#Storage-Unit-Variables) + - [Branches and Network Variables](#Branches-and-Network-Variables) + - [Services Variables](#Services-Variables) 7. [Constraints](#Constraints) + - [Common Constraints](#Common-Constraints) + - [Network Constraints](#Network-Constraints) + - [Power Variable Limit Constraints](#Power-Variable-Limit-Constraints) + - [Services Constraints](#Services-Constraints) + - [Thermal Unit Constraints](#Thermal-Unit-Constraints) + - [Renewable Unit Constraints](#Renewable-Unit-Constraints) + - [Branches Constraints](#Branches-Constraints) + - [Feedforward Constraints](#Feedforward-Constraints) 8. [Parameters](#Parameters) + - [Time Series Parameters](#Time-Series-Parameters) + - [Variable Value Parameters](#Variable-Value-Parameters) + - [Objective Function Parameters](#Objective-Function-Parameters) + +```@raw html +  +  +``` # Device Models @@ -34,15 +57,14 @@ Refer to the [Formulations Page](@ref formulation_library) for each Abstract Dev Refer to the [Problem Templates Page](@ref op_problem_template) for available `ProblemTemplate`s. -### Problem Templates - -Refer to the [Problem Templates Page](https://nrel-siip.github.io/PowerSimulations.jl/latest/modeler_guide/problem_templates/) for available `ProblemTemplate`s. ```@raw html     ``` +--- + # Decision Models ```@docs @@ -58,6 +80,8 @@ solve!(::DecisionModel)   ``` +--- + # Emulation Models ```@docs @@ -73,6 +97,8 @@ run!(::EmulationModel)   ``` +--- + # Service Models List of structures and methods for Service models @@ -86,6 +112,8 @@ ServiceModel   ``` +--- + # Simulation Models Refer to the [Simulations Page](@ref running_a_simulation) to explanations on how to setup a Simulation, with Sequencing and Feedforwards. @@ -104,6 +132,8 @@ execute!(::Simulation)   ``` +--- + # Variables For a list of variables for each device refer to its Formulations page. @@ -153,7 +183,7 @@ VoltageMagnitude VoltageAngle ``` -### Regulation and Services Variables +### Services Variables ```@docs ActivePowerReserveVariable @@ -170,6 +200,8 @@ InterfaceFlowSlackDown   ``` +--- + # Constraints ### Common Constraints @@ -226,19 +258,14 @@ EqualityConstraint ### Branches Constraints ```@docs -AbsoluteValueConstraint -FlowLimitFromToConstraint -FlowLimitToFromConstraint +FlowLimitConstraint FlowRateConstraint FlowRateConstraintFromTo FlowRateConstraintToFrom -HVDCDirection HVDCLossesAbsoluteValue HVDCPowerBalance NetworkFlowConstraint RateLimitConstraint -RateLimitConstraintFromTo -RateLimitConstraintToFrom PhaseAngleControlLimit ``` @@ -252,9 +279,16 @@ FeedforwardLowerBoundConstraint FeedforwardEnergyTargetConstraint ``` +```@raw html +  +  +``` + +--- + # Parameters -## Time Series Parameters +### Time Series Parameters ```@docs ActivePowerTimeSeriesParameter @@ -262,7 +296,7 @@ ReactivePowerTimeSeriesParameter RequirementTimeSeriesParameter ``` -## Variable Value Parameters +### Variable Value Parameters ```@docs UpperBoundValueParameter diff --git a/docs/src/formulation_library/Service.md b/docs/src/formulation_library/Service.md index 0951e1b859..5da88ddd41 100644 --- a/docs/src/formulation_library/Service.md +++ b/docs/src/formulation_library/Service.md @@ -16,6 +16,7 @@ In this documentation, we first specify the available `Services` in the grid, an 4. [`RampReserve`](#RampReserve) 5. [`NonSpinningReserve`](#NonSpinningReserve) 6. [`ConstantMaxInterfaceFlow`](#ConstantMaxInterfaceFlow) +7. [Changes on Expressions](#Changes-on-Expressions-due-to-Service-models) --- @@ -376,7 +377,7 @@ similarly if ``s_3`` is a `ReserveDown` service (e.g. Reg-Down): **Constraints:** -A NonSpinningReserve implements three fundamental constraints. The first is that the sum of all reserves of contributing devices must be larger than the `RampReserve` requirement. Thus, for a service ``s``: +A NonSpinningReserve implements three fundamental constraints. The first is that the sum of all reserves of contributing devices must be larger than the `NonSpinningReserve` requirement. Thus, for a service ``s``: ```math \sum_{d\in\mathcal{D}_s} r_{d,t} + r_t^\text{sl} \ge \text{RequirementTimeSeriesParameter}_{t},\quad \forall t\in \{1,\dots, T\} @@ -447,4 +448,48 @@ It adds the constraint to limit the `InterfaceTotalFlow` by the specified bounds ```math F^\text{min} \le f^\text{sl,up}_t - f^\text{sl,dn}_t + \sum_{d\in\mathcal{D}_s} \text{Dir}_d f_{d,t} \le F^\text{max}, \quad \forall t \in \{1,\dots,T\} -``` \ No newline at end of file +``` + +## Changes on Expressions due to Service models + +It is important to note that by adding a service to a Optimization Problem, variables for each contributing device must be created. For example, for every contributing generator ``d \in \mathcal{D}`` that is participating in services ``s_1,s_2,s_3``, it is required to create three set of `ActivePowerReserveVariable` variables: + +```math +r_{s_1,d,t},~ r_{s_2,d,t},~ r_{s_3,d,t},\quad \forall d \in \mathcal{D}, \forall t \in \{1,\dots, T\} +``` + +### Changes on UpperBound (UB) and LowerBound (LB) limits + +Each contributing generator ``d`` has active power limits that the reserve variables affect. In simple terms, the limits are implemented using expressions `ActivePowerRangeExpressionUB` and `ActivePowerRangeExpressionLB` as: + +```math +\text{ActivePowerRangeExpressionUB}_t \le P^\text{max} \\ +\text{ActivePowerRangeExpressionLB}_t \ge P^\text{min} +``` +`ReserveUp` type variables contribute to the upper bound expression, while `ReserveDown` variables contribute to the lower bound expressions. So if ``s_1,s_2`` are `ReserveUp` services, and ``s_3`` is a `ReserveDown` service, then for a thermal generator ``d`` using a `ThermalStandardDispatch`: + +```math +\begin{align*} +& p_{d,t}^\text{th} + r_{s_1,d,t} + r_{s_2,d,t} \le P^\text{th,max},\quad \forall d\in \mathcal{D}^\text{th}, \forall t \in \{1,\dots,T\} \\ +& p_{d,t}^\text{th} - r_{s_3,d,t} \ge P^\text{th,min},\quad \forall d\in \mathcal{D}^\text{th}, \forall t \in \{1,\dots,T\} +\end{align*} +``` + +while for a renewable generator ``d`` using a `RenewableFullDispatch`: + +```math +\begin{align*} +& p_{d,t}^\text{re} + r_{s_1,d,t} + r_{s_2,d,t} \le \text{ActivePowerTimeSeriesParameter}_t,\quad \forall d\in \mathcal{D}^\text{re}, \forall t \in \{1,\dots,T\}\\ +& p_{d,t}^\text{re} - r_{s_3,d,t} \ge 0,\quad \forall d\in \mathcal{D}^\text{re}, \forall t \in \{1,\dots,T\} +\end{align*} +``` + +### Changes in Ramp limits + +For the case of Ramp Limits (of formulation that model these limits), the reserve variables only affect the current time, and not the previous time. Then, for the same example as before: +```math +\begin{align*} +& p_{d,t}^\text{th} + r_{s_1,d,t} + r_{s_2,d,t} - p_{d,t-1}^\text{th}\le R^\text{th,up},\quad \forall d\in \mathcal{D}^\text{th}, \forall t \in \{1,\dots,T\}\\ +& p_{d,t}^\text{th} - r_{s_3,d,t} - p_{d,t-1}^\text{th} \ge -R^\text{th,dn},\quad \forall d\in \mathcal{D}^\text{th}, \forall t \in \{1,\dots,T\} +\end{align*} +``` From fb5e0249f5886355834ab9657442d9961604e22f Mon Sep 17 00:00:00 2001 From: rodrigomha Date: Fri, 10 May 2024 20:37:47 -0700 Subject: [PATCH 59/69] add phaseshifter docs --- docs/src/formulation_library/Branch.md | 50 ++++++++++++++++++++++++-- 1 file changed, 48 insertions(+), 2 deletions(-) diff --git a/docs/src/formulation_library/Branch.md b/docs/src/formulation_library/Branch.md index 8123d7ab7d..6732496105 100644 --- a/docs/src/formulation_library/Branch.md +++ b/docs/src/formulation_library/Branch.md @@ -11,7 +11,8 @@ 4. [`HVDCTwoTerminalUnbounded`](#HVDCTwoTerminalUnbounded) 5. [`HVDCTwoTerminalLossless`](#HVDCTwoTerminalLossless) 6. [`HVDCTwoTerminalDispatch`](#HVDCTwoTerminalDispatch) -7. [Valid configurations](#Valid-configurations) +7. [`PhaseAngleControl`](#PhaseAngleControl) +8. [Valid configurations](#Valid-configurations) ## `StaticBranch` @@ -56,7 +57,7 @@ For each branch ``b \in \{1,\dots, B\}`` (in a system with ``N`` buses) the cons \begin{aligned} & f_t = \sum_{i=1}^N \text{PTDF}_{i,b} \cdot \text{Bal}_{i,t}, \quad \forall t \in \{1,\dots, T\}\\ & f_t - f_t^\text{sl,up} \le R^\text{max},\quad \forall t \in \{1,\dots, T\} \\ -& f_t + f_t^\text{sl,lo} \le -R^\text{max},\quad \forall t \in \{1,\dots, T\} \\ +& f_t + f_t^\text{sl,lo} \ge -R^\text{max},\quad \forall t \in \{1,\dots, T\} \end{aligned} ``` on which ``\text{PTDF}`` is the ``N \times B`` system Power Transfer Distribution Factors (PTDF) matrix, and ``\text{Bal}_{i,t}`` is the active power bus balance expression (i.e. ``\text{Generation}_{i,t} - \text{Demand}_{i,t}``) at bus ``i`` at time-step ``t``. @@ -287,6 +288,51 @@ In addition, the `HVDCLosses` are subtracted to the `from` bus in the `ActivePow \end{align*} ``` +--- + +## `PhaseAngleControl` + +Formulation valid for `PTDFPowerModel` Network model + +```@docs +PhaseAngleControl +``` + +**Variables:** + +- [`FlowActivePowerVariable`](@ref): + - Bounds: ``(-\infty,\infty)`` + - Symbol: ``f`` +- [`PhaseShifterAngle`](@ref): + - Symbol: ``\theta^\text{shift}`` + +**Static Parameters** + +- ``R^\text{max}`` = `PowerSystems.get_rate(branch)` +- ``\Theta^\text{min}`` = `PowerSystems.get_phase_angle_limits(branch).min` +- ``\Theta^\text{max}`` = `PowerSystems.get_phase_angle_limits(branch).max` +- ``X`` = `PowerSystems.get_x(branch)` (series reactance) + +**Objective:** + +No changes to objective function + +**Expressions:** + +Adds to the `ActivePowerBalance` expression the term ``-\theta^\text{shift} /X`` to the `from` bus and ``+\theta^\text{shift} /X`` to the `to` bus, that the `PhaseShiftingTransformer` is connected. + +**Constraints:** + +For each branch ``b \in \{1,\dots, B\}`` (in a system with ``N`` buses) the constraints are given by: + +```math +\begin{aligned} +& f_t = \sum_{i=1}^N \text{PTDF}_{i,b} \cdot \text{Bal}_{i,t} + \frac{\theta^\text{shift}_t}{X}, \quad \forall t \in \{1,\dots, T\}\\ +& -R^\text{max} \le f_t \le R^\text{max},\quad \forall t \in \{1,\dots, T\} +\end{aligned} +``` +on which ``\text{PTDF}`` is the ``N \times B`` system Power Transfer Distribution Factors (PTDF) matrix, and ``\text{Bal}_{i,t}`` is the active power bus balance expression (i.e. ``\text{Generation}_{i,t} - \text{Demand}_{i,t}``) at bus ``i`` at time-step ``t``. + --- From 3c45bb5ec34867b1e4f051dc54aa50365bce02de Mon Sep 17 00:00:00 2001 From: rodrigomha Date: Fri, 10 May 2024 20:37:53 -0700 Subject: [PATCH 60/69] update docstrings --- src/core/constraints.jl | 111 +++++++++++++++++++++++++++++++++++++++- src/core/variables.jl | 2 +- 2 files changed, 111 insertions(+), 2 deletions(-) diff --git a/src/core/constraints.jl b/src/core/constraints.jl index 28307331bf..c3bd5de691 100644 --- a/src/core/constraints.jl +++ b/src/core/constraints.jl @@ -103,17 +103,87 @@ struct FeedforwardEnergyTargetConstraint <: ConstraintType end struct FlowActivePowerConstraint <: ConstraintType end #not being used struct FlowActivePowerFromToConstraint <: ConstraintType end #not being used struct FlowActivePowerToFromConstraint <: ConstraintType end #not being used -struct FlowLimitConstraint <: ConstraintType end #not being used +""" +Struct to create the constraint that set the flow limits through a PhaseShiftingTransformer. + +For more information check [Branch Formulations](@ref PowerSystems.Branch-Formulations). + +The specified constraint is formulated as: + +```math +-R^\\text{max} \\le f_t \\le R^\\text{max}, \\quad \\forall t \\in \\{1,\\dots,T\\} +``` +""" +struct FlowLimitConstraint <: ConstraintType end struct FlowLimitFromToConstraint <: ConstraintType end struct FlowLimitToFromConstraint <: ConstraintType end +""" +Struct to create the constraint that set the flow limits through an HVDC two-terminal branch. + +For more information check [Branch Formulations](@ref PowerSystems.Branch-Formulations). + +The specified constraint is formulated as: + +```math +R^\\text{min} \\le f_t \\le R^\\text{max}, \\quad \\forall t \\in \\{1,\\dots,T\\} +``` +""" struct FlowRateConstraint <: ConstraintType end +""" +Struct to create the constraint that set the flow from-to limits through an HVDC two-terminal branch. + +For more information check [Branch Formulations](@ref PowerSystems.Branch-Formulations). + +The specified constraint is formulated as: + +```math +R^\\text{from,min} \\le f_t^\\text{from-to} \\le R^\\text{from,max}, \\forall t \\in \\{1,\\dots, T\\} +``` +""" struct FlowRateConstraintFromTo <: ConstraintType end +""" +Struct to create the constraint that set the flow to-from limits through an HVDC two-terminal branch. + +For more information check [Branch Formulations](@ref PowerSystems.Branch-Formulations). + +The specified constraint is formulated as: + +```math +R^\\text{to,min} \\le f_t^\\text{to-from} \\le R^\\text{to,max},\\quad \\forall t \\in \\{1,\\dots, T\\} +``` +""" struct FlowRateConstraintToFrom <: ConstraintType end struct FlowReactivePowerConstraint <: ConstraintType end #not being used struct FlowReactivePowerFromToConstraint <: ConstraintType end #not being used struct FlowReactivePowerToFromConstraint <: ConstraintType end #not being used +""" +Struct to create the constraints that set the power balance across a lossy HVDC two-terminal line. + +For more information check [Branch Formulations](@ref PowerSystems.Branch-Formulations). + +The specified constraints are formulated as: + +```math +\\begin{align*} +& f_t^\\text{to-from} - f_t^\\text{from-to} \\le L_1 \\cdot f_t^\\text{to-from} - L_0,\\quad \\forall t \\in \\{1,\\dots, T\\} \\\\ +& f_t^\\text{from-to} - f_t^\\text{to-from} \\ge L_1 \\cdot f_t^\\text{from-to} + L_0,\\quad \\forall t \\in \\{1,\\dots, T\\} \\\\ +& f_t^\\text{from-to} - f_t^\\text{to-from} \\ge - M^\\text{big} (1 - u^\\text{dir}_t),\\quad \\forall t \\in \\{1,\\dots, T\\} \\\\ +& f_t^\\text{to-from} - f_t^\\text{from-to} \\ge - M^\\text{big} u^\\text{dir}_t,\\quad \\forall t \\in \\{1,\\dots, T\\} \\\\ +\\end{align*} +``` +""" struct HVDCPowerBalance <: ConstraintType end struct FrequencyResponseConstraint <: ConstraintType end +""" +Struct to create the constraint the AC branch flows depending on the network model. +For more information check [Branch Formulations](@ref PowerSystems.Branch-Formulations). + +The specified constraint depends on the network model chosen. The most common application is the StaticBranch in a PTDF Network Model: + +```math +f_t = \\sum_{i=1}^N \\text{PTDF}_{i,b} \\cdot \\text{Bal}_{i,t}, \\quad \\forall t \\in \\{1,\\dots, T\\} +``` +""" struct NetworkFlowConstraint <: ConstraintType end """ Struct to create the constraint to balance active power in nodal formulation. @@ -166,6 +236,20 @@ r_{d,t} \\le R^\\text{th,dn} \\cdot \\text{TF}\\quad \\forall d\\in \\mathcal{D struct RampConstraint <: ConstraintType end struct RampLimitConstraint <: ConstraintType end struct RangeLimitConstraint <: ConstraintType end +""" +Struct to create the constraint that set the AC flow limits through branches. + +For more information check [Branch Formulations](@ref PowerSystems.Branch-Formulations). + +The specified constraint is formulated as: + +```math +\\begin{align*} +& f_t - f_t^\\text{sl,up} \\le R^\\text{max},\\quad \\forall t \\in \\{1,\\dots, T\\} \\\\ +& f_t + f_t^\\text{sl,lo} \\ge -R^\\text{max},\\quad \\forall t \\in \\{1,\\dots, T\\} +\\end{align*} +``` +""" struct RateLimitConstraint <: ConstraintType end struct RateLimitConstraintFromTo <: ConstraintType end struct RateLimitConstraintToFrom <: ConstraintType end @@ -209,7 +293,32 @@ Struct to create the start-up time limit constraints for ThermalMultiStart. For more information check [ThermalGen Formulations](@ref ThermalGen-Formulations) for ThermalMultiStartUnitCommitment. """ struct StartupTimeLimitTemperatureConstraint <: ConstraintType end +""" +Struct to create the constraint that set the angle limits through a PhaseShiftingTransformer. + +For more information check [Branch Formulations](@ref PowerSystems.Branch-Formulations). + +The specified constraint is formulated as: + +```math +\\Theta^\\text{min} \\le \\theta^\\text{shift}_t \\le \\Theta^\\text{max}, \\quad \\forall t \\in \\{1,\\dots,T\\} +``` +""" struct PhaseAngleControlLimit <: ConstraintType end +""" +Struct to create the constraints that set the losses through a lossy HVDC two-terminal line. + +For more information check [Branch Formulations](@ref PowerSystems.Branch-Formulations). + +The specified constraints are formulated as: + +```math +\\begin{align*} +& f_t^\\text{to-from} - f_t^\\text{from-to} \\le \\ell_t,\\quad \\forall t \\in \\{1,\\dots, T\\} \\\\ +& f_t^\\text{from-to} - f_t^\\text{to-from} \\le \\ell_t,\\quad \\forall t \\in \\{1,\\dots, T\\} +\\end{align*} +``` +""" struct HVDCLossesAbsoluteValue <: ConstraintType end struct HVDCDirection <: ConstraintType end struct InterfaceFlowLimit <: ConstraintType end diff --git a/src/core/variables.jl b/src/core/variables.jl index 297dcfbd62..39843c34da 100644 --- a/src/core/variables.jl +++ b/src/core/variables.jl @@ -242,7 +242,7 @@ struct FlowReactivePowerToFromVariable <: VariableType end """ Struct to dispatch the creation of Phase Shifters Variables -Docs abbreviation: TODO +Docs abbreviation: ``\theta^\\text{shift}`` """ struct PhaseShifterAngle <: VariableType end From 704559f00e04e8a9b862b27b282ce533958743fd Mon Sep 17 00:00:00 2001 From: rodrigomha Date: Fri, 10 May 2024 21:20:44 -0700 Subject: [PATCH 61/69] fix continous typo to continuous --- docs/src/api/PowerSimulations.md | 12 +++++++++--- .../src/modeler_guide/debugging_infeasible_models.md | 2 +- src/PowerSimulations.jl | 2 +- src/core/constraints.jl | 2 +- src/feedforward/feedforward_constraints.jl | 6 +++--- test/test_simulation_build.jl | 8 ++++---- 6 files changed, 19 insertions(+), 13 deletions(-) diff --git a/docs/src/api/PowerSimulations.md b/docs/src/api/PowerSimulations.md index 139ab76e3b..ecb80f3ce7 100644 --- a/docs/src/api/PowerSimulations.md +++ b/docs/src/api/PowerSimulations.md @@ -22,6 +22,7 @@ end - [Storage Unit Variables](#Storage-Unit-Variables) - [Branches and Network Variables](#Branches-and-Network-Variables) - [Services Variables](#Services-Variables) + - [Feedforward Variables](#Feedforward-Variables) 7. [Constraints](#Constraints) - [Common Constraints](#Common-Constraints) - [Network Constraints](#Network-Constraints) @@ -195,6 +196,13 @@ InterfaceFlowSlackUp InterfaceFlowSlackDown ``` +### Feedforward Variables + +```@docs +UpperBoundFeedForwardSlack +LowerBoundFeedForwardSlack +``` + ```@raw html     @@ -272,11 +280,9 @@ PhaseAngleControlLimit ### Feedforward Constraints ```@docs -FeedforwardSemiContinousConstraint -FeedforwardIntegralLimitConstraint +FeedforwardSemiContinuousConstraint FeedforwardUpperBoundConstraint FeedforwardLowerBoundConstraint -FeedforwardEnergyTargetConstraint ``` ```@raw html diff --git a/docs/src/modeler_guide/debugging_infeasible_models.md b/docs/src/modeler_guide/debugging_infeasible_models.md index 711818eb72..cc52f7a2ec 100644 --- a/docs/src/modeler_guide/debugging_infeasible_models.md +++ b/docs/src/modeler_guide/debugging_infeasible_models.md @@ -120,7 +120,7 @@ Error: Constraints participating in conflict basis (IIS) │ │ ("119_PV_1", 26) │ │ └──────────────────────────────────────────────────────────────────────┘ │ ┌─────────────────────────────────────────────────────────────────────────────┐ -│ │ FeedforwardSemiContinousConstraint__ThermalStandard__ActivePowerVariable_ub │ +│ │ FeedforwardSemiContinuousConstraint__ThermalStandard__ActivePowerVariable_ub │ │ ├─────────────────────────────────────────────────────────────────────────────┤ │ │ ("322_CT_6", 26) │ │ │ ("321_CC_1", 26) │ diff --git a/src/PowerSimulations.jl b/src/PowerSimulations.jl index 9990b64d02..fd56c6de59 100644 --- a/src/PowerSimulations.jl +++ b/src/PowerSimulations.jl @@ -254,7 +254,7 @@ export CopperPlateBalanceConstraint export DurationConstraint export EnergyBalanceConstraint export EqualityConstraint -export FeedforwardSemiContinousConstraint +export FeedforwardSemiContinuousConstraint export FeedforwardUpperBoundConstraint export FeedforwardLowerBoundConstraint export FeedforwardIntegralLimitConstraint diff --git a/src/core/constraints.jl b/src/core/constraints.jl index c3bd5de691..877a219b05 100644 --- a/src/core/constraints.jl +++ b/src/core/constraints.jl @@ -95,7 +95,7 @@ q_t^\\text{re} = \\text{pf} \\cdot p_t^\\text{re}, \\quad \\forall t \\in \\{1,\ ``` """ struct EqualityConstraint <: ConstraintType end -struct FeedforwardSemiContinousConstraint <: ConstraintType end +struct FeedforwardSemiContinuousConstraint <: ConstraintType end struct FeedforwardIntegralLimitConstraint <: ConstraintType end struct FeedforwardUpperBoundConstraint <: ConstraintType end struct FeedforwardLowerBoundConstraint <: ConstraintType end diff --git a/src/feedforward/feedforward_constraints.jl b/src/feedforward/feedforward_constraints.jl index 25292824ce..8554ca1031 100644 --- a/src/feedforward/feedforward_constraints.jl +++ b/src/feedforward/feedforward_constraints.jl @@ -78,7 +78,7 @@ function _add_sc_feedforward_constraints!( devices::IS.FlattenIteratorWrapper{V}, model::DeviceModel{V, W}, ) where { - T <: FeedforwardSemiContinousConstraint, + T <: FeedforwardSemiContinuousConstraint, P <: OnStatusParameter, U <: Union{ActivePowerVariable, PowerAboveMinimumVariable}, V <: PSY.Component, @@ -123,7 +123,7 @@ function _add_sc_feedforward_constraints!( devices::IS.FlattenIteratorWrapper{V}, model::DeviceModel{V, W}, ) where { - T <: FeedforwardSemiContinousConstraint, + T <: FeedforwardSemiContinuousConstraint, P <: ParameterType, U <: VariableType, V <: PSY.Component, @@ -225,7 +225,7 @@ function add_feedforward_constraints!( end _add_sc_feedforward_constraints!( container, - FeedforwardSemiContinousConstraint, + FeedforwardSemiContinuousConstraint, parameter_type(), var, devices, diff --git a/test/test_simulation_build.jl b/test/test_simulation_build.jl index 1d78fc1a92..2f34ab8ec0 100644 --- a/test/test_simulation_build.jl +++ b/test/test_simulation_build.jl @@ -185,28 +185,28 @@ end ac_power_model = PSI.get_simulation_model(PSI.get_models(sim), :ED) c = PSI.get_constraint( PSI.get_optimization_container(ac_power_model), - FeedforwardSemiContinousConstraint(), + FeedforwardSemiContinuousConstraint(), ThermalStandard, "ActivePowerVariable_ub", ) @test !isempty(c) c = PSI.get_constraint( PSI.get_optimization_container(ac_power_model), - FeedforwardSemiContinousConstraint(), + FeedforwardSemiContinuousConstraint(), ThermalStandard, "ActivePowerVariable_lb", ) @test !isempty(c) c = PSI.get_constraint( PSI.get_optimization_container(ac_power_model), - FeedforwardSemiContinousConstraint(), + FeedforwardSemiContinuousConstraint(), ThermalStandard, "ReactivePowerVariable_ub", ) @test !isempty(c) c = PSI.get_constraint( PSI.get_optimization_container(ac_power_model), - FeedforwardSemiContinousConstraint(), + FeedforwardSemiContinuousConstraint(), ThermalStandard, "ReactivePowerVariable_lb", ) From 24b0ba217660781c22b2700491973ffa1f2c2f8a Mon Sep 17 00:00:00 2001 From: rodrigomha Date: Fri, 10 May 2024 21:21:18 -0700 Subject: [PATCH 62/69] add docstring to ff slack variables --- src/core/variables.jl | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/core/variables.jl b/src/core/variables.jl index 39843c34da..5c615d6e92 100644 --- a/src/core/variables.jl +++ b/src/core/variables.jl @@ -281,8 +281,17 @@ Docs abbreviation: ``f^\\text{sl,dn}`` """ struct InterfaceFlowSlackDown <: VariableType end +""" +Struct to dispatch the creation of Slack variables for UpperBoundFeedforward + +Docs abbreviation: ``p^\\text{ff,ubsl}`` +""" struct UpperBoundFeedForwardSlack <: VariableType end +""" +Struct to dispatch the creation of Slack variables for LowerBoundFeedforward +Docs abbreviation: ``p^\\text{ff,lbsl}`` +""" struct LowerBoundFeedForwardSlack <: VariableType end const START_VARIABLES = (HotStartVariable, WarmStartVariable, ColdStartVariable) From c70185b45bde636ee6a493cde70e912d8cb050e1 Mon Sep 17 00:00:00 2001 From: rodrigomha Date: Fri, 10 May 2024 21:21:31 -0700 Subject: [PATCH 63/69] add docstrings to ff structs --- src/feedforward/feedforwards.jl | 48 ++++++++++++++++++++++++++++++--- 1 file changed, 44 insertions(+), 4 deletions(-) diff --git a/src/feedforward/feedforwards.jl b/src/feedforward/feedforwards.jl index 9d541dae6c..284cbd887d 100644 --- a/src/feedforward/feedforwards.jl +++ b/src/feedforward/feedforwards.jl @@ -50,7 +50,22 @@ function get_feedforward_meta(ff::AbstractAffectFeedforward) end """ -Adds an upper bound constraint to a variable. + UpperBoundFeedforward( + component_type::Type{<:PSY.Component}, + source::Type{T}, + affected_values::Vector{DataType}, + add_slacks::Bool = false, + meta = CONTAINER_KEY_EMPTY_META + ) where {T} + +Constructs a parameterized upper bound constraint to implement feedforward from other models. + +# Arguments: +* component_type::Type{<:PSY.Component} : Specify the type of component on which the Feedforward will be applied +* source::Type{T} : Specify the VariableType, ParameterType or AuxVariableType as the source of values for the Feedforward +* affected_values::Vector{DataType} : Specify the variable on which the upper bound will be applied using the source values +* add_slacks::Bool = false : Add slacks variables to relax the upper bound constraint. + """ struct UpperBoundFeedforward <: AbstractAffectFeedforward optimization_container_key::OptimizationContainerKey @@ -87,7 +102,22 @@ get_optimization_container_key(ff::UpperBoundFeedforward) = ff.optimization_cont get_slacks(ff::UpperBoundFeedforward) = ff.add_slacks """ -Adds a lower bound constraint to a variable. + LowerBoundFeedforward( + component_type::Type{<:PSY.Component}, + source::Type{T}, + affected_values::Vector{DataType}, + add_slacks::Bool = false, + meta = CONTAINER_KEY_EMPTY_META + ) where {T} + +Constructs a parameterized lower bound constraint to implement feedforward from other models. + +# Arguments: +* component_type::Type{<:PSY.Component} : Specify the type of component on which the Feedforward will be applied +* source::Type{T} : Specify the VariableType, ParameterType or AuxVariableType as the source of values for the Feedforward +* affected_values::Vector{DataType} : Specify the variable on which the lower bound will be applied using the source values +* add_slacks::Bool = false : Add slacks variables to relax the lower bound constraint. + """ struct LowerBoundFeedforward <: AbstractAffectFeedforward optimization_container_key::OptimizationContainerKey @@ -154,11 +184,16 @@ end source::Type{T}, affected_values::Vector{DataType}, meta = CONTAINER_KEY_EMPTY_META - ) where {T} + ) where {T} It allows to enable/disable bounds to 0.0 for a specified variable. Commonly used to limit the `ActivePowerVariable` in an Economic Dispatch problem by the commitment decision taken in an another problem (typically a Unit Commitment problem). + +# Arguments: +* component_type::Type{<:PSY.Component} : Specify the type of component on which the Feedforward will be applied +* source::Type{T} : Specify the VariableType, ParameterType or AuxVariableType as the source of values for the Feedforward +* affected_values::Vector{DataType} : Specify the variable on which the semicontinuous limit will be applied using the source values """ struct SemiContinuousFeedforward <: AbstractAffectFeedforward optimization_container_key::OptimizationContainerKey @@ -217,10 +252,15 @@ end source::Type{T}, affected_values::Vector{DataType}, meta = CONTAINER_KEY_EMPTY_META - ) where {T} + ) where {T} Fixes a Variable or Parameter Value in the model from another problem. Is the only Feed Forward that can be used with a Parameter or a Variable as the affected value. + +# Arguments: +* component_type::Type{<:PSY.Component} : Specify the type of component on which the Feedforward will be applied +* source::Type{T} : Specify the VariableType, ParameterType or AuxVariableType as the source of values for the Feedforward +* affected_values::Vector{DataType} : Specify the variable on which the fix value will be applied using the source values """ struct FixValueFeedforward <: AbstractAffectFeedforward optimization_container_key::OptimizationContainerKey From 160365507702e00d7d96c9b20a542484687c2ac5 Mon Sep 17 00:00:00 2001 From: rodrigomha Date: Fri, 10 May 2024 21:59:31 -0700 Subject: [PATCH 64/69] update ff docstrings --- src/core/constraints.jl | 40 ++++++++++++++++++++++++++++++++++++++++ src/core/variables.jl | 2 +- 2 files changed, 41 insertions(+), 1 deletion(-) diff --git a/src/core/constraints.jl b/src/core/constraints.jl index 877a219b05..497373fc7b 100644 --- a/src/core/constraints.jl +++ b/src/core/constraints.jl @@ -95,9 +95,49 @@ q_t^\\text{re} = \\text{pf} \\cdot p_t^\\text{re}, \\quad \\forall t \\in \\{1,\ ``` """ struct EqualityConstraint <: ConstraintType end +""" +Struct to create the constraint for semicontinuous feedforward limits. + +For more information check [Feedforward Formulations](@ref ff_formulations). + +The specified constraint is formulated as: + +```math +\\begin{align*} +& \\text{ActivePowerRangeExpressionUB}_t := p_t^\\text{th} - \\text{on}_t^\\text{th}P^\\text{th,max} \\le 0, \\quad \\forall t\\in \\{1, \\dots, T\\} \\\\ +& \\text{ActivePowerRangeExpressionLB}_t := p_t^\\text{th} - \\text{on}_t^\\text{th}P^\\text{th,min} \\ge 0, \\quad \\forall t\\in \\{1, \\dots, T\\} +\\end{align*} +``` +""" struct FeedforwardSemiContinuousConstraint <: ConstraintType end struct FeedforwardIntegralLimitConstraint <: ConstraintType end +""" +Struct to create the constraint for upper bound feedforward limits. + +For more information check [Feedforward Formulations](@ref ff_formulations). + +The specified constraint is formulated as: + +```math +\\begin{align*} +& \\text{AffectedVariable}_t - p_t^\\text{ff,ubsl} \\le \\text{SourceVariableParameter}_t, \\quad \\forall t \\in \\{1,\\dots, T\\} +\\end{align*} +``` +""" struct FeedforwardUpperBoundConstraint <: ConstraintType end +""" +Struct to create the constraint for lower bound feedforward limits. + +For more information check [Feedforward Formulations](@ref ff_formulations). + +The specified constraint is formulated as: + +```math +\\begin{align*} +& \\text{AffectedVariable}_t + p_t^\\text{ff,lbsl} \\ge \\text{SourceVariableParameter}_t, \\quad \\forall t \\in \\{1,\\dots, T\\} +\\end{align*} +``` +""" struct FeedforwardLowerBoundConstraint <: ConstraintType end struct FeedforwardEnergyTargetConstraint <: ConstraintType end struct FlowActivePowerConstraint <: ConstraintType end #not being used diff --git a/src/core/variables.jl b/src/core/variables.jl index 5c615d6e92..057c0b0756 100644 --- a/src/core/variables.jl +++ b/src/core/variables.jl @@ -242,7 +242,7 @@ struct FlowReactivePowerToFromVariable <: VariableType end """ Struct to dispatch the creation of Phase Shifters Variables -Docs abbreviation: ``\theta^\\text{shift}`` +Docs abbreviation: ``\\theta^\\text{shift}`` """ struct PhaseShifterAngle <: VariableType end From 69fb26e22dfc1c50ce1d3b85b64ec4a0a62cc98e Mon Sep 17 00:00:00 2001 From: rodrigomha Date: Fri, 10 May 2024 21:59:36 -0700 Subject: [PATCH 65/69] finish ff docs --- docs/src/api/PowerSimulations.md | 54 +++++----- docs/src/formulation_library/Feedforward.md | 112 +++++++++++++++++++- 2 files changed, 138 insertions(+), 28 deletions(-) diff --git a/docs/src/api/PowerSimulations.md b/docs/src/api/PowerSimulations.md index ecb80f3ce7..18f6bbb941 100644 --- a/docs/src/api/PowerSimulations.md +++ b/docs/src/api/PowerSimulations.md @@ -9,33 +9,33 @@ end ### Table of Contents -1. [Device Models](#Device-Models) - - [Formulations](#Formulations) - - [Problem Templates](#Problem-Templates) -2. [Decision Models](#Decision-Models) -3. [Emulation Models](#Emulation-Models) -4. [Service Models](#Service-Models) -5. [Simulation Models](#Simulation-Models) -6. [Variables](#Variables) - - [Common Variables](#Common-Variables) - - [Thermal Unit Variables](#Thermal-Unit-Variables) - - [Storage Unit Variables](#Storage-Unit-Variables) - - [Branches and Network Variables](#Branches-and-Network-Variables) - - [Services Variables](#Services-Variables) - - [Feedforward Variables](#Feedforward-Variables) -7. [Constraints](#Constraints) - - [Common Constraints](#Common-Constraints) - - [Network Constraints](#Network-Constraints) - - [Power Variable Limit Constraints](#Power-Variable-Limit-Constraints) - - [Services Constraints](#Services-Constraints) - - [Thermal Unit Constraints](#Thermal-Unit-Constraints) - - [Renewable Unit Constraints](#Renewable-Unit-Constraints) - - [Branches Constraints](#Branches-Constraints) - - [Feedforward Constraints](#Feedforward-Constraints) -8. [Parameters](#Parameters) - - [Time Series Parameters](#Time-Series-Parameters) - - [Variable Value Parameters](#Variable-Value-Parameters) - - [Objective Function Parameters](#Objective-Function-Parameters) +* [Device Models](#Device-Models) + * [Formulations](#Formulations) + * [Problem Templates](#Problem-Templates) +* [Decision Models](#Decision-Models) +* [Emulation Models](#Emulation-Models) +* [Service Models](#Service-Models) +* [Simulation Models](#Simulation-Models) +* [Variables](#Variables) + * [Common Variables](#Common-Variables) + * [Thermal Unit Variables](#Thermal-Unit-Variables) + * [Storage Unit Variables](#Storage-Unit-Variables) + * [Branches and Network Variables](#Branches-and-Network-Variables) + * [Services Variables](#Services-Variables) + * [Feedforward Variables](#Feedforward-Variables) +* [Constraints](#Constraints) + * [Common Constraints](#Common-Constraints) + * [Network Constraints](#Network-Constraints) + * [Power Variable Limit Constraints](#Power-Variable-Limit-Constraints) + * [Services Constraints](#Services-Constraints) + * [Thermal Unit Constraints](#Thermal-Unit-Constraints) + * [Renewable Unit Constraints](#Renewable-Unit-Constraints) + * [Branches Constraints](#Branches-Constraints) + * [Feedforward Constraints](#Feedforward-Constraints) +* [Parameters](#Parameters) + * [Time Series Parameters](#Time-Series-Parameters) + * [Variable Value Parameters](#Variable-Value-Parameters) + * [Objective Function Parameters](#Objective-Function-Parameters) ```@raw html   diff --git a/docs/src/formulation_library/Feedforward.md b/docs/src/formulation_library/Feedforward.md index 47c094f260..107836b988 100644 --- a/docs/src/formulation_library/Feedforward.md +++ b/docs/src/formulation_library/Feedforward.md @@ -16,7 +16,8 @@ The creation of a FeedForward requires at least to specify the `component_type` 1. [`SemiContinuousFeedforward`](#SemiContinuousFeedForward) 2. [`FixValueFeedforward`](#FixValueFeedforward) - +3. [`UpperBoundFeedforward`](#UpperBoundFeedforward) +4. [`LowerBoundFeedforward`](#LowerBoundFeedforward) --- @@ -55,5 +56,114 @@ Limits the `ActivePowerRangeExpressionUB` and `ActivePowerRangeExpressionLB` by Thus, if the commitment parameter is zero, the dispatch is limited to zero, forcing to turn off the generator without introducing binary variables in the economic dispatch problem. +--- + ## `FixValueFeedforward` +```@docs +FixValueFeedforward +``` + +**Variables:** + +No variables are created + +**Parameters:** + +The parameter `FixValueParameter` is used to match the result obtained from the source variable (from an upper level problem). + +**Objective:** + +No changes to the objective function. + +**Expressions:** + +No changes on expressions. + +**Constraints:** + +Set the `VariableType` from the `affected_values` to be equal to the source parameter store in `FixValueParameter` + +```math +\begin{align*} +& \text{AffectedVariable}_t = \text{SourceVariableParameter}_t, \quad \forall t \in \{1,\dots, T\} +\end{align*} +``` + +--- + +## `UpperBoundFeedforward` + +```@docs +UpperBoundFeedforward +``` + +**Variables:** + +If slack variables are enabled: +- [`UpperBoundFeedForwardSlack`](@ref) + - Bounds: [0.0, ] + - Default proportional cost: 1e6 + - Symbol: ``p^\text{ff,ubsl}`` + + +**Parameters:** + +The parameter `UpperBoundValueParameter` is used to store the result obtained from the source variable (from an upper level problem) that will be used as an upper bound to the affected variable. + +**Objective:** + +The slack variable is added to the objective function using its large default cost ``+ p^\text{ff,ubsl} \cdot 10^6`` + +**Expressions:** + +No changes on expressions. + +**Constraints:** + +Set the `VariableType` from the `affected_values` to be lower than the source parameter store in `UpperBoundValueParameter`. + +```math +\begin{align*} +& \text{AffectedVariable}_t - p_t^\text{ff,ubsl} \le \text{SourceVariableParameter}_t, \quad \forall t \in \{1,\dots, T\} +\end{align*} +``` + +--- + +## `LowerBoundFeedforward` + +```@docs +LowerBoundFeedforward +``` + +**Variables:** + +If slack variables are enabled: +- [`LowerBoundFeedForwardSlack`](@ref) + - Bounds: [0.0, ] + - Default proportional cost: 1e6 + - Symbol: ``p^\text{ff,lbsl}`` + + +**Parameters:** + +The parameter `LowerBoundValueParameter` is used to store the result obtained from the source variable (from an upper level problem) that will be used as a lower bound to the affected variable. + +**Objective:** + +The slack variable is added to the objective function using its large default cost ``+ p^\text{ff,lbsl} \cdot 10^6`` + +**Expressions:** + +No changes on expressions. + +**Constraints:** + +Set the `VariableType` from the `affected_values` to be greater than the source parameter store in `LowerBoundValueParameter`. + +```math +\begin{align*} +& \text{AffectedVariable}_t + p_t^\text{ff,lbsl} \ge \text{SourceVariableParameter}_t, \quad \forall t \in \{1,\dots, T\} +\end{align*} +``` \ No newline at end of file From 45e9cce87817079b3cf86e110ce50e63c13758c8 Mon Sep 17 00:00:00 2001 From: rodrigomha Date: Sat, 11 May 2024 12:22:03 -0700 Subject: [PATCH 66/69] update typos --- docs/src/formulation_library/General.md | 6 ++-- docs/src/formulation_library/Piecewise.md | 32 +++++++++++++++++-- .../devices/common/objective_functions.jl | 8 ++--- src/feedforward/feedforwards.jl | 28 ++++++++-------- 4 files changed, 50 insertions(+), 24 deletions(-) diff --git a/docs/src/formulation_library/General.md b/docs/src/formulation_library/General.md index 11b5c2ab9f..b603df29bf 100644 --- a/docs/src/formulation_library/General.md +++ b/docs/src/formulation_library/General.md @@ -104,9 +104,9 @@ where - For `variable_cost::PiecewiseLinearData`, ``f(x)`` is the piecewise linear function obtained by connecting the `(x, y)` points `get_points(variable_cost)` in order. - For `variable_cost = PiecewiseLinearSlopeData([x0, x1, x2, ...], y0, [s0, s1, s2, ...])`, ``f(x)`` is the piecewise linear function obtained by starting at `(x0, y0)`, drawing a segment at slope `s0` to `x=x1`, drawing a segment at slope `s1` to `x=x2`, etc. -___ +--- -## `StorageManagementCost` +## `StorageCost` Adds an objective function cost term according to: @@ -118,7 +118,7 @@ Adds an objective function cost term according to: **Impact of different cost configurations:** -The following table describes all possible configuration of the `StorageManagementCost` with the target constraint in hydro or storage device models. Cases 1(a) & 2(a) will have no impact of the models operations and the target constraint will be rendered useless. In most cases that have no energy target and a non-zero value for ``C^{value}``, if this cost is too high (``C^{value} >> 0``) or too low (``C^{value} <<0``) can result in either the model holding on to stored energy till the end or the model not storing any energy in the device. This is caused by the fact that when energy target is zero, we have ``E_t = - E^{shortage}_t``, and ``- E^{shortage}_t * C^{value}`` in the objective function is replaced by ``E_t * C^{value}``, thus resulting in ``C^{value}`` to be seen as the cost of stored energy. +The following table describes all possible configuration of the `StorageCost` with the target constraint in hydro or storage device models. Cases 1(a) & 2(a) will have no impact of the models operations and the target constraint will be rendered useless. In most cases that have no energy target and a non-zero value for ``C^{value}``, if this cost is too high (``C^{value} >> 0``) or too low (``C^{value} <<0``) can result in either the model holding on to stored energy till the end or the model not storing any energy in the device. This is caused by the fact that when energy target is zero, we have ``E_t = - E^{shortage}_t``, and ``- E^{shortage}_t * C^{value}`` in the objective function is replaced by ``E_t * C^{value}``, thus resulting in ``C^{value}`` to be seen as the cost of stored energy. | Case | Energy Target | Energy Shortage Cost | Energy Value / Energy Surplus cost | Effect | diff --git a/docs/src/formulation_library/Piecewise.md b/docs/src/formulation_library/Piecewise.md index d175ed7270..2167769162 100644 --- a/docs/src/formulation_library/Piecewise.md +++ b/docs/src/formulation_library/Piecewise.md @@ -13,8 +13,8 @@ A special ordered set (SOS) is an ordered set of variables used as an additional Piecewise-linear costs are defined by a sequence of points representing the line segments for each generator: ``(P_k^\text{max}, C_k)`` on which we assume ``C_k`` is the cost of generating ``P_k^\text{max}`` power, and ``k \in \{1,\dots, K\}`` are the number of segments each generator cost function has. -!!!note - `PowerSystems` has more options to specify cost functions for each thermal unit. Independent of which form of the cost data is provided, `PowerSimulations.jl` will internally transform the data to use the λ-model formulation. See TODO: ADD PSY COST DOCS for more information. +!!! note + `PowerSystems` has more options to specify cost functions for each thermal unit. Independent of which form of the cost data is provided, `PowerSimulations.jl` will internally transform the data to use the λ-model formulation. See **TODO: ADD PSY COST DOCS** for more information. ### Commitment formulation @@ -48,4 +48,30 @@ on which ``\delta_{k,t} \in [0,1]`` is the interpolation variable, ``p`` is the ## Compact representation of PWL costs -TODO \ No newline at end of file +### Commitment Formulation + +```math +\begin{align*} + \min_{\substack{p_{t}, \delta_{k,t}}} + & \sum_{t \in \mathcal{T}} \left(\sum_{k \in \mathcal{K}} C_{k,t} \delta_{k,t} \right) \Delta t\\ + & \sum_{k \in \mathcal{K}} P_{k}^{\text{max}} \delta_{k,t} = P^{\text{min}} u_{t} + \Delta p_{t} & \forall t \in \mathcal{T}\\ + & \sum_{k \in \mathcal{K}} \delta_{k,t} = u_{t} & \forall t \in \mathcal{T}\\ + & 0 \leq \Delta p_{t} \leq \left( P^{\text{max}} - P^{\text{min}} \right)u_{t} & \forall t \in \mathcal{T}\\ + &\left \{\delta_{i,t} \dots \delta_{k,t} \right \} \in \text{SOS}_{2} & \forall t \in \mathcal{T} +\end{align*} +``` +on which ``\delta_{k,t} \in [0,1]`` is the interpolation variable, ``\Delta p`` is the active power of the generator above the minimum power and ``u \in \{0,1\}`` is the commitment variable of the generator. In the case of a PWL convex costs, i.e. increasing slopes, the SOS constraint is omitted. + +### Dispatch formulation + +```math +\begin{align*} + \min_{\substack{p_{t}, \delta_{k,t}}} + & \sum_{t \in \mathcal{T}} \left(\sum_{k \in \mathcal{K}} C_{k,t} \delta_{k,t} \right) \Delta t\\ + & \sum_{k \in \mathcal{K}} P_{k}^{\text{max}} \delta_{k,t} = P^{\text{min}} \text{on}_{t} + \Delta p_{t} & \forall t \in \mathcal{T}\\ + & \sum_{k \in \mathcal{K}} \delta_{k,t} = \text{on}_{t} & \forall t \in \mathcal{T}\\ + & 0 \leq \Delta p_{t} \leq \left( P^{\text{max}} - P^{\text{min}} \right)\text{on}_{t} & \forall t \in \mathcal{T}\\ + &\left \{\delta_{i,t} \dots \delta_{k,t} \right \} \in \text{SOS}_{2} & \forall t \in \mathcal{T} +\end{align*} +``` +on which ``\delta_{k,t} \in [0,1]`` is the interpolation variable, ``\Delta p`` is the active power of the generator above the minimum power and ``u \in \{0,1\}`` is the commitment variable of the generator. In the case of a PWL convex costs, i.e. increasing slopes, the SOS constraint is omitted. \ No newline at end of file diff --git a/src/devices_models/devices/common/objective_functions.jl b/src/devices_models/devices/common/objective_functions.jl index f64c3da743..2199bdf3ee 100644 --- a/src/devices_models/devices/common/objective_functions.jl +++ b/src/devices_models/devices/common/objective_functions.jl @@ -453,11 +453,11 @@ end Obtain the normalized PiecewiseLinear cost data in system base per unit depending on the specified power units. -Note that the costs (y-axis) are always in $/h so +Note that the costs (y-axis) are always in \$/h so they do not require transformation """ function get_piecewise_pointcurve_per_system_unit( - cost_component::PiecewiseLinearData, + cost_component::PSY.PiecewiseLinearData, ::Val{0}, # SystemBase Units system_base_power::Float64, device_base_power::Float64, @@ -466,7 +466,7 @@ function get_piecewise_pointcurve_per_system_unit( end function get_piecewise_pointcurve_per_system_unit( - cost_component::PiecewiseLinearData, + cost_component::PSY.PiecewiseLinearData, ::Val{1}, # DeviceBase Units system_base_power::Float64, device_base_power::Float64, @@ -481,7 +481,7 @@ function get_piecewise_pointcurve_per_system_unit( end function get_piecewise_pointcurve_per_system_unit( - cost_component::PiecewiseLinearData, + cost_component::PSY.PiecewiseLinearData, ::Val{2}, # Natural Units system_base_power::Float64, device_base_power::Float64, diff --git a/src/feedforward/feedforwards.jl b/src/feedforward/feedforwards.jl index 1089d86422..a69b00a2ef 100644 --- a/src/feedforward/feedforwards.jl +++ b/src/feedforward/feedforwards.jl @@ -61,10 +61,10 @@ end Constructs a parameterized upper bound constraint to implement feedforward from other models. # Arguments: -* component_type::Type{<:PSY.Component} : Specify the type of component on which the Feedforward will be applied -* source::Type{T} : Specify the VariableType, ParameterType or AuxVariableType as the source of values for the Feedforward -* affected_values::Vector{DataType} : Specify the variable on which the upper bound will be applied using the source values -* add_slacks::Bool = false : Add slacks variables to relax the upper bound constraint. +* `component_type::Type{<:PSY.Component}` : Specify the type of component on which the Feedforward will be applied +* `source::Type{T}` : Specify the VariableType, ParameterType or AuxVariableType as the source of values for the Feedforward +* `affected_values::Vector{DataType}` : Specify the variable on which the upper bound will be applied using the source values +* `add_slacks::Bool = false` : Add slacks variables to relax the upper bound constraint. """ struct UpperBoundFeedforward <: AbstractAffectFeedforward @@ -113,10 +113,10 @@ get_slacks(ff::UpperBoundFeedforward) = ff.add_slacks Constructs a parameterized lower bound constraint to implement feedforward from other models. # Arguments: -* component_type::Type{<:PSY.Component} : Specify the type of component on which the Feedforward will be applied -* source::Type{T} : Specify the VariableType, ParameterType or AuxVariableType as the source of values for the Feedforward -* affected_values::Vector{DataType} : Specify the variable on which the lower bound will be applied using the source values -* add_slacks::Bool = false : Add slacks variables to relax the lower bound constraint. +* `component_type::Type{<:PSY.Component}` : Specify the type of component on which the Feedforward will be applied +* `source::Type{T}` : Specify the VariableType, ParameterType or AuxVariableType as the source of values for the Feedforward +* `affected_values::Vector{DataType}` : Specify the variable on which the lower bound will be applied using the source values +* `add_slacks::Bool = false` : Add slacks variables to relax the lower bound constraint. """ struct LowerBoundFeedforward <: AbstractAffectFeedforward @@ -191,9 +191,9 @@ It allows to enable/disable bounds to 0.0 for a specified variable. Commonly use an another problem (typically a Unit Commitment problem). # Arguments: -* component_type::Type{<:PSY.Component} : Specify the type of component on which the Feedforward will be applied -* source::Type{T} : Specify the VariableType, ParameterType or AuxVariableType as the source of values for the Feedforward -* affected_values::Vector{DataType} : Specify the variable on which the semicontinuous limit will be applied using the source values +* `component_type::Type{<:PSY.Component}` : Specify the type of component on which the Feedforward will be applied +* `source::Type{T}` : Specify the VariableType, ParameterType or AuxVariableType as the source of values for the Feedforward +* `affected_values::Vector{DataType}` : Specify the variable on which the semicontinuous limit will be applied using the source values """ struct SemiContinuousFeedforward <: AbstractAffectFeedforward optimization_container_key::OptimizationContainerKey @@ -258,9 +258,9 @@ Fixes a Variable or Parameter Value in the model from another problem. Is the on with a Parameter or a Variable as the affected value. # Arguments: -* component_type::Type{<:PSY.Component} : Specify the type of component on which the Feedforward will be applied -* source::Type{T} : Specify the VariableType, ParameterType or AuxVariableType as the source of values for the Feedforward -* affected_values::Vector{DataType} : Specify the variable on which the fix value will be applied using the source values +* `component_type::Type{<:PSY.Component}` : Specify the type of component on which the Feedforward will be applied +* `source::Type{T}` : Specify the VariableType, ParameterType or AuxVariableType as the source of values for the Feedforward +* `affected_values::Vector{DataType}` : Specify the variable on which the fix value will be applied using the source values """ struct FixValueFeedforward <: AbstractAffectFeedforward optimization_container_key::OptimizationContainerKey From 7b1ab3e9a0960ed5ebf2a5e9fe21b42b7ebb6379 Mon Sep 17 00:00:00 2001 From: rodrigomha Date: Mon, 13 May 2024 10:29:41 -0700 Subject: [PATCH 67/69] update reactive power note --- docs/src/formulation_library/Branch.md | 2 +- docs/src/formulation_library/Load.md | 2 +- docs/src/formulation_library/RenewableGen.md | 2 +- docs/src/formulation_library/ThermalGen.md | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/src/formulation_library/Branch.md b/docs/src/formulation_library/Branch.md index 6732496105..2a70cae817 100644 --- a/docs/src/formulation_library/Branch.md +++ b/docs/src/formulation_library/Branch.md @@ -1,7 +1,7 @@ # `PowerSystems.Branch` Formulations !!! note - The usage of reactive power variables and constraints will depend on the network model used, i.e. if it uses (or not) reactive power. If the network model is purely active power based, then no variables and constraints related to reactive power are created. For the sake of completion, if the formulation allows the usage of reactive power it will be included. + The use of reactive power variables and constraints will depend on the network model used, i.e., whether it uses (or does not use) reactive power. If the network model is purely active power-based, reactive power variables and related constraints are not created. ### Table of contents diff --git a/docs/src/formulation_library/Load.md b/docs/src/formulation_library/Load.md index 49cc879dcb..dcb7b9b8d0 100644 --- a/docs/src/formulation_library/Load.md +++ b/docs/src/formulation_library/Load.md @@ -3,7 +3,7 @@ Electric load formulations define the optimization models that describe load units (demand) mathematical model in different operational settings, such as economic dispatch and unit commitment. !!! note - The usage of reactive power variables and constraints will depend on the network model used, i.e. if it uses (or not) reactive power. If the network model is purely active power based, then no variables and constraints related to reactive power are created. For the sake of completion, if the formulation allows the usage of reactive power it will be included. + The use of reactive power variables and constraints will depend on the network model used, i.e., whether it uses (or does not use) reactive power. If the network model is purely active power-based, reactive power variables and related constraints are not created. ### Table of contents diff --git a/docs/src/formulation_library/RenewableGen.md b/docs/src/formulation_library/RenewableGen.md index 505713723c..5dfca92c3e 100644 --- a/docs/src/formulation_library/RenewableGen.md +++ b/docs/src/formulation_library/RenewableGen.md @@ -3,7 +3,7 @@ Renewable generation formulations define the optimization models that describe renewable units mathematical model in different operational settings, such as economic dispatch and unit commitment. !!! note - The usage of reactive power variables and constraints will depend on the network model used, i.e. if it uses (or not) reactive power. If the network model is purely active power based, then no variables and constraints related to reactive power are created. For the sake of completion, if the formulation allows the usage of reactive power it will be included. + The use of reactive power variables and constraints will depend on the network model used, i.e., whether it uses (or does not use) reactive power. If the network model is purely active power-based, reactive power variables and related constraints are not created. !!! note Reserve variables for services are not included in the formulation, albeit their inclusion change the variables, expressions, constraints and objective functions created. A detailed description of the implications in the optimization models is described in the [Service formulation](@ref service_formulations) section. diff --git a/docs/src/formulation_library/ThermalGen.md b/docs/src/formulation_library/ThermalGen.md index 86143df4b8..e179c8c8e1 100644 --- a/docs/src/formulation_library/ThermalGen.md +++ b/docs/src/formulation_library/ThermalGen.md @@ -8,7 +8,7 @@ Thermal generation formulations define the optimization models that describe the !!! note - The usage of reactive power variables and constraints will depend on the network model used, i.e. if it uses (or not) reactive power. If the network model is purely active power based, then no variables and constraints related to reactive power are created. For the sake of completion, if the formulation allows the usage of reactive power it will be included. + The use of reactive power variables and constraints will depend on the network model used, i.e., whether it uses (or does not use) reactive power. If the network model is purely active power-based, reactive power variables and related constraints are not created. !!! note Reserve variables for services are not included in the formulation, albeit their inclusion change the variables, expressions, constraints and objective functions created. A detailed description of the implications in the optimization models is described in the [Service formulation](@ref service_formulations) section. From 59c0ba47c59e99bd9378cf495872002634bd4208 Mon Sep 17 00:00:00 2001 From: rodrigomha Date: Mon, 13 May 2024 11:13:04 -0700 Subject: [PATCH 68/69] update comments --- docs/src/formulation_library/Feedforward.md | 18 +++++------------- docs/src/formulation_library/General.md | 2 +- docs/src/formulation_library/Introduction.md | 2 +- docs/src/formulation_library/Service.md | 2 +- 4 files changed, 8 insertions(+), 16 deletions(-) diff --git a/docs/src/formulation_library/Feedforward.md b/docs/src/formulation_library/Feedforward.md index 107836b988..bdda721f36 100644 --- a/docs/src/formulation_library/Feedforward.md +++ b/docs/src/formulation_library/Feedforward.md @@ -1,16 +1,8 @@ # [FeedForward Formulations](@id ff_formulations) -In PowerSimulations, chronologies define where information is flowing. There are two types -of chronologies. +**FeedForwards** are the mechanism to define how information is shared between models. Specifically, a FeedForward defines what to do with information passed with an inter-stage chronology in a Simulation. The most common FeedForward is the `SemiContinuousFeedForward` that affects the semi-continuous range constraints of thermal generators in the economic dispatch problems based on the value of the (already solved) unit-commitment variables. -- inter-stage chronologies: Define how information flows between stages. e.g. day-ahead solutions are used to inform economic dispatch problems -- intra-stage chronologies: Define how information flows between multiple executions of a single stage. e.g. the dispatch setpoints of the first period of an economic dispatch problem are constrained by the ramping limits from setpoints in the final period of the previous problem. - -The definition of exactly what information is passed using the defined chronologies is accomplished using **FeedForwards**. - -Specifically, a FeedForward is used to define what to do with information being passed with an inter-stage chronology in a Simulation. The most common FeedForward is the `SemiContinuousFeedForward` that affects the semi-continuous range constraints of thermal generators in the economic dispatch problems based on the value of the (already solved) unit-commitment variables. - -The creation of a FeedForward requires at least to specify the `component_type` on which the FeedForward will be applied. The `source` variable specify which variable will be taken from the problem solved, for example the commitment variable of the thermal unit in the unit commitment problem. Finally, the `affected_values` specify which variables will be affected in the problem to be solved, for example the next economic dispatch problem. +The creation of a FeedForward requires at least specifying the `component_type` on which the FeedForward will be applied. The `source` variable specifies which variable will be taken from the problem solved, for example, the commitment variable of the thermal unit in the unit commitment problem. Finally, the `affected_values` specify which variables will be affected in the problem to be solved, for example, the next economic dispatch problem. ### Table of contents @@ -70,7 +62,7 @@ No variables are created **Parameters:** -The parameter `FixValueParameter` is used to match the result obtained from the source variable (from an upper level problem). +The parameter `FixValueParameter` is used to match the result obtained from the source variable (from the simulation state). **Objective:** @@ -109,7 +101,7 @@ If slack variables are enabled: **Parameters:** -The parameter `UpperBoundValueParameter` is used to store the result obtained from the source variable (from an upper level problem) that will be used as an upper bound to the affected variable. +The parameter `UpperBoundValueParameter` stores the result obtained from the source variable (from the simulation state) that will be used as an upper bound to the affected variable. **Objective:** @@ -148,7 +140,7 @@ If slack variables are enabled: **Parameters:** -The parameter `LowerBoundValueParameter` is used to store the result obtained from the source variable (from an upper level problem) that will be used as a lower bound to the affected variable. +The parameter `LowerBoundValueParameter` stores the result obtained from the source variable (from the simulation state) that will be used as a lower bound to the affected variable. **Objective:** diff --git a/docs/src/formulation_library/General.md b/docs/src/formulation_library/General.md index b603df29bf..ceb5e1b9da 100644 --- a/docs/src/formulation_library/General.md +++ b/docs/src/formulation_library/General.md @@ -118,7 +118,7 @@ Adds an objective function cost term according to: **Impact of different cost configurations:** -The following table describes all possible configuration of the `StorageCost` with the target constraint in hydro or storage device models. Cases 1(a) & 2(a) will have no impact of the models operations and the target constraint will be rendered useless. In most cases that have no energy target and a non-zero value for ``C^{value}``, if this cost is too high (``C^{value} >> 0``) or too low (``C^{value} <<0``) can result in either the model holding on to stored energy till the end or the model not storing any energy in the device. This is caused by the fact that when energy target is zero, we have ``E_t = - E^{shortage}_t``, and ``- E^{shortage}_t * C^{value}`` in the objective function is replaced by ``E_t * C^{value}``, thus resulting in ``C^{value}`` to be seen as the cost of stored energy. +The following table describes all possible configurations of the `StorageCost` with the target constraint in hydro or storage device models. Cases 1(a) & 2(a) will not impact the model's operations, and the target constraint will be rendered useless. In most cases that have no energy target and a non-zero value for ``C^{value}``, if this cost is too high (``C^{value} >> 0``) or too low (``C^{value} <<0``) can result in either the model holding on to stored energy till the end of the model not storing any energy in the device. This is caused by the fact that when the energy target is zero, we have ``E_t = - E^{shortage}_t``, and ``- E^{shortage}_t * C^{value}`` in the objective function is replaced by ``E_t * C^{value}``, thus resulting in ``C^{value}`` to be seen as the cost of stored energy. | Case | Energy Target | Energy Shortage Cost | Energy Value / Energy Surplus cost | Effect | diff --git a/docs/src/formulation_library/Introduction.md b/docs/src/formulation_library/Introduction.md index ef867b8297..47a8425d4e 100644 --- a/docs/src/formulation_library/Introduction.md +++ b/docs/src/formulation_library/Introduction.md @@ -52,7 +52,7 @@ Each `DeviceModel` formulation is described in specific in their respective page \end{align*} ``` -Note that the `StaticPowerLoad` does not impose any cost to the objective function or any constraint, but add its power demand to the supply-balance demand of the `CopperPlatePowerModel` used. Since we are using the `ThermalDispatchNoMin` formulation for the thermal generation, the lower bound for the power is 0, instead of ``P^\text{th,min}``. In addition, we are assuming a linear cost ``C^\text{th}``. Finally, the `RenewableFullDispatch` formulation allows the dispatch of the renewable unit to be between 0 and its maximum injection time series ``p_t^\text{re,param}``. +Note that the `StaticPowerLoad` does not impose any cost to the objective function or constraint but adds its power demand to the supply-balance demand of the `CopperPlatePowerModel` used. Since we are using the `ThermalDispatchNoMin` formulation for the thermal generation, the lower bound for the power is 0, instead of ``P^\text{th,min}``. In addition, we are assuming a linear cost ``C^\text{th}``. Finally, the `RenewableFullDispatch` formulation allows the dispatch of the renewable unit between 0 and its maximum injection time series ``p_t^\text{re,param}``. # Nomenclature diff --git a/docs/src/formulation_library/Service.md b/docs/src/formulation_library/Service.md index 5da88ddd41..61aaf1d335 100644 --- a/docs/src/formulation_library/Service.md +++ b/docs/src/formulation_library/Service.md @@ -148,7 +148,7 @@ mdtable(combo_table, latex = false) **Objective:** -The `ServiceRequirementVariable` is added as a piecewise linear cost based on the decreasing offers listed in the `variable_cost` time series. These decreasing cost represent the scarcity prices of not having sufficient reserves. For example, if the variable ``\text{req} = 0``, then a really high cost is paid for not having enough reserves, and if ``\text{req}`` is larger, then a lower cost (or even zero) is paid. TODO: actual implementation. +The `ServiceRequirementVariable` is added as a piecewise linear cost based on the decreasing offers listed in the `variable_cost` time series. These decreasing cost represent the scarcity prices of not having sufficient reserves. For example, if the variable ``\text{req} = 0``, then a really high cost is paid for not having enough reserves, and if ``\text{req}`` is larger, then a lower cost (or even zero) is paid. **Expressions:** From 2fce166d44c5eef3d591ccea5e0db3dfd14fc79a Mon Sep 17 00:00:00 2001 From: rodrigomha Date: Mon, 13 May 2024 11:22:36 -0700 Subject: [PATCH 69/69] fix typo --- docs/src/modeler_guide/read_results.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/modeler_guide/read_results.md b/docs/src/modeler_guide/read_results.md index 08ff92d975..6fcec568f9 100644 --- a/docs/src/modeler_guide/read_results.md +++ b/docs/src/modeler_guide/read_results.md @@ -139,7 +139,7 @@ With this, it is possible to obtain results of each `DecisionModel` and `Emulati # Use the Problem Name for Decision Problems results_uc = get_decision_problem_results(results_sim, "UC") results_ed = get_decision_problem_results(results_sim, "ED") -results_emulator = get_emulation_problem_results(results_dcp) +results_emulator = get_emulation_problem_results(results_sim) ``` Once we have each decision (or emulation) problem results, we can explore directly using the approach for Decision Models, mentioned in the previous section.