From e5a987053114f794a89d937bc250a41a0f87c39b Mon Sep 17 00:00:00 2001 From: rodrigomha Date: Tue, 15 Oct 2024 10:59:26 -0700 Subject: [PATCH 01/33] update fuelcost param changes --- src/core/parameters.jl | 5 +++++ .../device_constructors/thermalgeneration_constructor.jl | 4 ++++ src/devices_models/devices/thermal_generation.jl | 5 ++++- src/parameters/add_parameters.jl | 6 ++++++ 4 files changed, 19 insertions(+), 1 deletion(-) diff --git a/src/core/parameters.jl b/src/core/parameters.jl index 2946dd68f..e772d6e37 100644 --- a/src/core/parameters.jl +++ b/src/core/parameters.jl @@ -276,6 +276,11 @@ Parameter to define Min Flow limit for interface time series """ struct MinInterfaceFlowLimitParameter <: TimeSeriesParameter end +""" +Parameter to define fuel cost time series +""" +struct FuelCostTimeSeriesParameter <: TimeSeriesParameter end + abstract type VariableValueParameter <: RightHandSideParameter end """ diff --git a/src/devices_models/device_constructors/thermalgeneration_constructor.jl b/src/devices_models/device_constructors/thermalgeneration_constructor.jl index 5225586c4..00269bc0a 100644 --- a/src/devices_models/device_constructors/thermalgeneration_constructor.jl +++ b/src/devices_models/device_constructors/thermalgeneration_constructor.jl @@ -731,6 +731,10 @@ function construct_device!( add_variables!(container, ActivePowerVariable, devices, D()) + if haskey(get_time_series_names(model), FuelCostTimeSeriesParameter) + add_parameters!(container, FuelCostTimeSeriesParameter, devices, model) + end + add_to_expression!( container, ActivePowerBalance, diff --git a/src/devices_models/devices/thermal_generation.jl b/src/devices_models/devices/thermal_generation.jl index 66809d18d..3f3be9f01 100644 --- a/src/devices_models/devices/thermal_generation.jl +++ b/src/devices_models/devices/thermal_generation.jl @@ -50,6 +50,7 @@ get_variable_upper_bound(::StartVariable, d::PSY.ThermalGen, ::AbstractThermalFo get_variable_binary(::Union{ColdStartVariable, WarmStartVariable, HotStartVariable}, ::Type{PSY.ThermalMultiStart}, ::AbstractThermalFormulation) = true ########################### Parameter related set functions ################################ +get_multiplier_value(::FuelCostTimeSeriesParameter, d::PSY.ThermalGen, ::AbstractThermalFormulation) = PSY.get_proportional_term(d.operation_cost.variable.value_curve) get_parameter_multiplier(::VariableValueParameter, d::PSY.ThermalGen, ::AbstractThermalFormulation) = 1.0 get_initial_parameter_value(::VariableValueParameter, d::PSY.ThermalGen, ::AbstractThermalFormulation) = 1.0 get_expression_multiplier(::OnStatusParameter, ::ActivePowerRangeExpressionUB, d::PSY.ThermalGen, ::AbstractThermalFormulation) = PSY.get_active_power_limits(d).max @@ -193,7 +194,9 @@ function get_default_time_series_names( ::Type{U}, ::Type{V}, ) where {U <: PSY.ThermalGen, V <: Union{FixedOutput, AbstractThermalFormulation}} - return Dict{Type{<:TimeSeriesParameter}, String}() + return Dict{Type{<:TimeSeriesParameter}, String}( + FuelCostTimeSeriesParameter => "fuel_cost", + ) end function get_default_attributes( diff --git a/src/parameters/add_parameters.jl b/src/parameters/add_parameters.jl index 76f1ef209..ae39d01d4 100644 --- a/src/parameters/add_parameters.jl +++ b/src/parameters/add_parameters.jl @@ -197,6 +197,9 @@ function _add_time_series_parameters!( device_names = String[] initial_values = Dict{String, AbstractArray}() for device in devices + if !PSY.has_time_series(device) + continue + end push!(device_names, PSY.get_name(device)) ts_uuid = string(IS.get_time_series_uuid(ts_type, device, ts_name)) if !(ts_uuid in keys(initial_values)) @@ -226,6 +229,9 @@ function _add_time_series_parameters!( end for device in devices + if !PSY.has_time_series(device) + continue + end name = PSY.get_name(device) multiplier = get_multiplier_value(T(), device, W()) for step in time_steps From 677a8a3dea806eb06f28419881a16d288180b450 Mon Sep 17 00:00:00 2001 From: Jose Daniel Lara Date: Sun, 13 Oct 2024 12:10:13 -0600 Subject: [PATCH 02/33] change file name to avoid confussion --- src/PowerSimulations.jl | 2 +- .../devices/{interfaces.jl => default_interface_methods.jl} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename src/devices_models/devices/{interfaces.jl => default_interface_methods.jl} (100%) diff --git a/src/PowerSimulations.jl b/src/PowerSimulations.jl index 0a36addcd..cd8544a63 100644 --- a/src/PowerSimulations.jl +++ b/src/PowerSimulations.jl @@ -556,7 +556,7 @@ include("devices_models/devices/common/duration_constraints.jl") include("devices_models/devices/common/get_time_series.jl") # Device Modeling components -include("devices_models/devices/interfaces.jl") +include("devices_models/devices/default_interface_methods.jl") include("devices_models/devices/common/add_to_expression.jl") include("devices_models/devices/common/set_expression.jl") include("devices_models/devices/renewable_generation.jl") diff --git a/src/devices_models/devices/interfaces.jl b/src/devices_models/devices/default_interface_methods.jl similarity index 100% rename from src/devices_models/devices/interfaces.jl rename to src/devices_models/devices/default_interface_methods.jl From e74a1394454670f233aeb27d318a41d3778401ca Mon Sep 17 00:00:00 2001 From: Jose Daniel Lara Date: Sun, 13 Oct 2024 14:09:03 -0600 Subject: [PATCH 03/33] add ts parameter calls in constructor --- .../thermalgeneration_constructor.jl | 142 ++++++++++++++++++ 1 file changed, 142 insertions(+) diff --git a/src/devices_models/device_constructors/thermalgeneration_constructor.jl b/src/devices_models/device_constructors/thermalgeneration_constructor.jl index 00269bc0a..bbb169b86 100644 --- a/src/devices_models/device_constructors/thermalgeneration_constructor.jl +++ b/src/devices_models/device_constructors/thermalgeneration_constructor.jl @@ -56,6 +56,10 @@ function construct_device!( initial_conditions!(container, devices, D()) + if haskey(get_time_series_names(model), ActivePowerTimeSeriesParameter) + add_parameters!(container, ActivePowerTimeSeriesParameter, devices, model) + end + add_to_expression!( container, ActivePowerBalance, @@ -135,6 +139,17 @@ function construct_device!( add_constraints!(container, RampConstraint, devices, model, network_model) add_constraints!(container, DurationConstraint, devices, model, network_model) + if haskey(get_time_series_names(model), ActivePowerTimeSeriesParameter) + add_constraints!( + container, + ActivePowerVariableTimeSeriesLimitsConstraint, + ActivePowerRangeExpressionUB, + devices, + model, + network_model, + ) + end + add_feedforward_constraints!(container, model, devices) objective_function!(container, devices, model, get_network_formulation(network_model)) @@ -164,6 +179,10 @@ function construct_device!( initial_conditions!(container, devices, D()) + if haskey(get_time_series_names(model), ActivePowerTimeSeriesParameter) + add_parameters!(container, ActivePowerTimeSeriesParameter, devices, model) + end + add_to_expression!( container, ActivePowerBalance, @@ -227,6 +246,16 @@ function construct_device!( add_constraints!(container, CommitmentConstraint, devices, model, network_model) add_constraints!(container, RampConstraint, devices, model, network_model) add_constraints!(container, DurationConstraint, devices, model, network_model) + if haskey(get_time_series_names(model), ActivePowerTimeSeriesParameter) + add_constraints!( + container, + ActivePowerVariableTimeSeriesLimitsConstraint, + ActivePowerRangeExpressionUB, + devices, + model, + network_model, + ) + end add_feedforward_constraints!(container, model, devices) @@ -256,6 +285,10 @@ function construct_device!( initial_conditions!(container, devices, ThermalBasicUnitCommitment()) + if haskey(get_time_series_names(model), ActivePowerTimeSeriesParameter) + add_parameters!(container, ActivePowerTimeSeriesParameter, devices, model) + end + add_to_expression!( container, ActivePowerBalance, @@ -335,6 +368,17 @@ function construct_device!( ) add_constraints!(container, CommitmentConstraint, devices, model, network_model) + if haskey(get_time_series_names(model), ActivePowerTimeSeriesParameter) + add_constraints!( + container, + ActivePowerVariableTimeSeriesLimitsConstraint, + ActivePowerRangeExpressionUB, + devices, + model, + network_model, + ) + end + add_feedforward_constraints!(container, model, devices) objective_function!(container, devices, model, get_network_formulation(network_model)) @@ -361,6 +405,10 @@ function construct_device!( initial_conditions!(container, devices, ThermalBasicUnitCommitment()) + if haskey(get_time_series_names(model), ActivePowerTimeSeriesParameter) + add_parameters!(container, ActivePowerTimeSeriesParameter, devices, model) + end + add_to_expression!( container, ActivePowerBalance, @@ -423,6 +471,16 @@ function construct_device!( ) add_constraints!(container, CommitmentConstraint, devices, model, network_model) + if haskey(get_time_series_names(model), ActivePowerTimeSeriesParameter) + add_constraints!( + container, + ActivePowerVariableTimeSeriesLimitsConstraint, + ActivePowerRangeExpressionUB, + devices, + model, + network_model, + ) + end add_feedforward_constraints!(container, model, devices) @@ -833,6 +891,10 @@ function construct_device!( initial_conditions!(container, devices, ThermalMultiStartUnitCommitment()) + if haskey(get_time_series_names(model), ActivePowerTimeSeriesParameter) + add_parameters!(container, ActivePowerTimeSeriesParameter, devices, model) + end + add_to_expression!( container, ActivePowerBalance, @@ -935,6 +997,16 @@ function construct_device!( network_model, ) add_constraints!(container, ActiveRangeICConstraint, devices, model, network_model) + if haskey(get_time_series_names(model), ActivePowerTimeSeriesParameter) + add_constraints!( + container, + ActivePowerVariableTimeSeriesLimitsConstraint, + ActivePowerRangeExpressionUB, + devices, + model, + network_model, + ) + end add_feedforward_constraints!(container, model, devices) @@ -969,6 +1041,10 @@ function construct_device!( add_variables!(container, TimeDurationOff, devices, ThermalMultiStartUnitCommitment()) add_variables!(container, PowerOutput, devices, ThermalMultiStartUnitCommitment()) + if haskey(get_time_series_names(model), ActivePowerTimeSeriesParameter) + add_parameters!(container, ActivePowerTimeSeriesParameter, devices, model) + end + add_to_expression!( container, ActivePowerBalance, @@ -1057,6 +1133,16 @@ function construct_device!( network_model, ) add_constraints!(container, ActiveRangeICConstraint, devices, model, network_model) + if haskey(get_time_series_names(model), ActivePowerTimeSeriesParameter) + add_constraints!( + container, + ActivePowerVariableTimeSeriesLimitsConstraint, + ActivePowerRangeExpressionUB, + devices, + model, + network_model, + ) + end add_feedforward_constraints!(container, model, devices) @@ -1096,6 +1182,10 @@ function construct_device!( initial_conditions!(container, devices, ThermalCompactUnitCommitment()) + if haskey(get_time_series_names(model), ActivePowerTimeSeriesParameter) + add_parameters!(container, ActivePowerTimeSeriesParameter, devices, model) + end + add_to_expression!( container, ActivePowerBalance, @@ -1173,6 +1263,16 @@ function construct_device!( add_constraints!(container, CommitmentConstraint, devices, model, network_model) add_constraints!(container, RampConstraint, devices, model, network_model) add_constraints!(container, DurationConstraint, devices, model, network_model) + if haskey(get_time_series_names(model), ActivePowerTimeSeriesParameter) + add_constraints!( + container, + ActivePowerVariableTimeSeriesLimitsConstraint, + ActivePowerRangeExpressionUB, + devices, + model, + network_model, + ) + end add_feedforward_constraints!(container, model, devices) @@ -1206,6 +1306,10 @@ function construct_device!( initial_conditions!(container, devices, ThermalCompactUnitCommitment()) + if haskey(get_time_series_names(model), ActivePowerTimeSeriesParameter) + add_parameters!(container, ActivePowerTimeSeriesParameter, devices, model) + end + add_to_expression!( container, ActivePowerBalance, @@ -1274,6 +1378,16 @@ function construct_device!( add_constraints!(container, CommitmentConstraint, devices, model, network_model) add_constraints!(container, RampConstraint, devices, model, network_model) add_constraints!(container, DurationConstraint, devices, model, network_model) + if haskey(get_time_series_names(model), ActivePowerTimeSeriesParameter) + add_constraints!( + container, + ActivePowerVariableTimeSeriesLimitsConstraint, + ActivePowerRangeExpressionUB, + devices, + model, + network_model, + ) + end add_feedforward_constraints!(container, model, devices) @@ -1311,6 +1425,10 @@ function construct_device!( initial_conditions!(container, devices, ThermalBasicCompactUnitCommitment()) + if haskey(get_time_series_names(model), ActivePowerTimeSeriesParameter) + add_parameters!(container, ActivePowerTimeSeriesParameter, devices, model) + end + add_to_expression!( container, ActivePowerBalance, @@ -1386,6 +1504,16 @@ function construct_device!( network_model, ) add_constraints!(container, CommitmentConstraint, devices, model, network_model) + if haskey(get_time_series_names(model), ActivePowerTimeSeriesParameter) + add_constraints!( + container, + ActivePowerVariableTimeSeriesLimitsConstraint, + ActivePowerRangeExpressionUB, + devices, + model, + network_model, + ) + end add_feedforward_constraints!(container, model, devices) @@ -1417,6 +1545,10 @@ function construct_device!( initial_conditions!(container, devices, ThermalBasicCompactUnitCommitment()) + if haskey(get_time_series_names(model), ActivePowerTimeSeriesParameter) + add_parameters!(container, ActivePowerTimeSeriesParameter, devices, model) + end + add_to_expression!( container, ActivePowerBalance, @@ -1483,6 +1615,16 @@ function construct_device!( ) add_constraints!(container, CommitmentConstraint, devices, model, network_model) + if haskey(get_time_series_names(model), ActivePowerTimeSeriesParameter) + add_constraints!( + container, + ActivePowerVariableTimeSeriesLimitsConstraint, + ActivePowerRangeExpressionUB, + devices, + model, + network_model, + ) + end add_feedforward_constraints!(container, model, devices) From a8d9f28c63a953b5f387ede8ea59addf71784899 Mon Sep 17 00:00:00 2001 From: Jose Daniel Lara Date: Sun, 13 Oct 2024 14:09:24 -0600 Subject: [PATCH 04/33] add constraint call --- .../devices/thermal_generation.jl | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/devices_models/devices/thermal_generation.jl b/src/devices_models/devices/thermal_generation.jl index 3f3be9f01..9cde131af 100644 --- a/src/devices_models/devices/thermal_generation.jl +++ b/src/devices_models/devices/thermal_generation.jl @@ -445,6 +445,30 @@ function _get_data_for_range_ic( return ini_conds end +function add_constraints!( + container::OptimizationContainer, + ::Type{ActivePowerVariableTimeSeriesLimitsConstraint}, + U::Type{<:Union{ActivePowerVariable, ActivePowerRangeExpressionUB}}, + devices::IS.FlattenIteratorWrapper{V}, + model::DeviceModel{V, W}, + ::NetworkModel{X}, +) where { + V <: PSY.ThermalGen, + W <: AbstractThermalUnitCommitment, + X <: PM.AbstractPowerModel, +} + add_parameterized_upper_bound_range_constraints( + container, + ActivePowerVariableTimeSeriesLimitsConstraint, + U, + ActivePowerTimeSeriesParameter, + devices, + model, + X, + ) + return +end + """ This function adds range constraint for the first time period. Constraint (10) from PGLIB formulation """ From 4c29c68f5deea60e4f27d52053264ae00ae39a8f Mon Sep 17 00:00:00 2001 From: Jose Daniel Lara Date: Sun, 13 Oct 2024 14:09:37 -0600 Subject: [PATCH 05/33] add checks for ts property --- .../devices/common/range_constraint.jl | 19 ++++++++++++++++--- src/services_models/services_constructor.jl | 2 +- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/src/devices_models/devices/common/range_constraint.jl b/src/devices_models/devices/common/range_constraint.jl index 0a7c4b804..37e5495ca 100644 --- a/src/devices_models/devices/common/range_constraint.jl +++ b/src/devices_models/devices/common/range_constraint.jl @@ -706,6 +706,9 @@ function lower_bound_range_with_parameter!( jump_model = get_jump_model(container) time_steps = axes(constraint_container)[2] for device in devices + if !(PSY.has_time_series(device)) + continue + end name = PSY.get_name(device) param = get_parameter_column_refs(param_container, name) for t in time_steps @@ -730,8 +733,11 @@ function _add_parameterized_lower_bound_range_constraints_impl!( W <: AbstractDeviceFormulation, } time_steps = get_time_steps(container) - names = [PSY.get_name(d) for d in devices] - + names = [PSY.get_name(d) for d in devices if PSY.has_time_series(d)] + if isempty(names) + @debug "There are no $V devices with time series data" + return + end constraint = add_constraints_container!(container, T(), V, names, time_steps; meta = "lb") @@ -842,6 +848,9 @@ function upper_bound_range_with_parameter!( time_steps = axes(constraint_container)[2] for device in devices name = PSY.get_name(device) + if !(PSY.has_time_series(device)) + continue + end param = get_parameter_column_refs(param_container, name) for t in time_steps constraint_container[name, t] = @@ -865,7 +874,11 @@ function _add_parameterized_upper_bound_range_constraints_impl!( W <: AbstractDeviceFormulation, } time_steps = get_time_steps(container) - names = [PSY.get_name(d) for d in devices] + names = [PSY.get_name(d) for d in devices if PSY.has_time_series(d)] + if isempty(names) + @debug "There are no $V devices with time series data" + return + end constraint = add_constraints_container!(container, T(), V, names, time_steps; meta = "ub") diff --git a/src/services_models/services_constructor.jl b/src/services_models/services_constructor.jl index 6deb80d30..8b02409f0 100644 --- a/src/services_models/services_constructor.jl +++ b/src/services_models/services_constructor.jl @@ -548,7 +548,7 @@ function construct_service!( PSY.get_name.(interfaces), get_time_steps(container), ) - #add_feedforward_arguments!(container, model, service) + add_feedforward_arguments!(container, model, service) return end From 9324ae7bbcce11fa3af751fec32ddc11906234cf Mon Sep 17 00:00:00 2001 From: Jose Daniel Lara Date: Sun, 13 Oct 2024 14:58:25 -0600 Subject: [PATCH 06/33] improve error print --- src/devices_models/devices/default_interface_methods.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/devices_models/devices/default_interface_methods.jl b/src/devices_models/devices/default_interface_methods.jl index 0fd71e624..5391344ae 100644 --- a/src/devices_models/devices/default_interface_methods.jl +++ b/src/devices_models/devices/default_interface_methods.jl @@ -11,7 +11,7 @@ get_variable_lower_bound(_, ::PSY.Component, __) = nothing get_variable_upper_bound(_, ::PSY.Component, __) = nothing get_multiplier_value(x, y::PSY.Component, z) = - error("Unable to get parameter $x for device $y for formulation $z") + error("Unable to get parameter $x for device $(IS.summary(y)) for formulation $z") get_expression_type_for_reserve(_, y::Type{<:PSY.Component}, z) = error("`get_expression_type_for_reserve` must be implemented for $y and $z") From b4ac3d634d3e7cdff5c94b5fb66ba58f0d39c58a Mon Sep 17 00:00:00 2001 From: Jose Daniel Lara Date: Sun, 13 Oct 2024 14:58:37 -0600 Subject: [PATCH 07/33] add multiplier function --- src/devices_models/devices/thermal_generation.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/devices_models/devices/thermal_generation.jl b/src/devices_models/devices/thermal_generation.jl index 9cde131af..ebe8bedf9 100644 --- a/src/devices_models/devices/thermal_generation.jl +++ b/src/devices_models/devices/thermal_generation.jl @@ -50,7 +50,7 @@ get_variable_upper_bound(::StartVariable, d::PSY.ThermalGen, ::AbstractThermalFo get_variable_binary(::Union{ColdStartVariable, WarmStartVariable, HotStartVariable}, ::Type{PSY.ThermalMultiStart}, ::AbstractThermalFormulation) = true ########################### Parameter related set functions ################################ -get_multiplier_value(::FuelCostTimeSeriesParameter, d::PSY.ThermalGen, ::AbstractThermalFormulation) = PSY.get_proportional_term(d.operation_cost.variable.value_curve) +get_multiplier_value(::ActivePowerTimeSeriesParameter, d::PSY.ThermalGen, ::AbstractThermalFormulation) = PSY.get_max_active_power(d) get_parameter_multiplier(::VariableValueParameter, d::PSY.ThermalGen, ::AbstractThermalFormulation) = 1.0 get_initial_parameter_value(::VariableValueParameter, d::PSY.ThermalGen, ::AbstractThermalFormulation) = 1.0 get_expression_multiplier(::OnStatusParameter, ::ActivePowerRangeExpressionUB, d::PSY.ThermalGen, ::AbstractThermalFormulation) = PSY.get_active_power_limits(d).max From f409dcf90aea8511e64ef685f985e622f0db1b06 Mon Sep 17 00:00:00 2001 From: Jose Daniel Lara Date: Sun, 13 Oct 2024 14:58:55 -0600 Subject: [PATCH 08/33] add constructor test --- ..._device_thermal_generation_constructors.jl | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/test/test_device_thermal_generation_constructors.jl b/test/test_device_thermal_generation_constructors.jl index cdb3a3881..9197bcd5e 100644 --- a/test/test_device_thermal_generation_constructors.jl +++ b/test/test_device_thermal_generation_constructors.jl @@ -921,3 +921,49 @@ end end end end + +@testset "Thermal with max_active_power time series" begin + device_model = DeviceModel( + ThermalStandard, + ThermalStandardUnitCommitment; + time_series_names = Dict(ActivePowerTimeSeriesParameter => "max_active_power")) + c_sys5 = PSB.build_system(PSITestSystems, "c_sys5") + + derate_data = SortedDict{Dates.DateTime, TimeSeries.TimeArray}() + data_ts = collect( + DateTime("1/1/2024 0:00:00", "d/m/y H:M:S"):Hour(1):DateTime( + "1/1/2024 23:00:00", + "d/m/y H:M:S", + ), + ) + for t in 1:2 + ini_time = data_ts[1] + Day(t - 1) + derate_data[ini_time] = + TimeArray(data_ts + Day(t - 1), fill!(Vector{Float64}(undef, 24), 0.8)) + end + solitude = get_component(ThermalStandard, c_sys5, "Solitude") + PSY.add_time_series!( + c_sys5, + solitude, + PSY.Deterministic("max_active_power", derate_data), + ) + + model = DecisionModel( + MockOperationProblem, + DCPPowerModel, + c_sys5) + + mock_construct_device!(model, device_model) + moi_tests(model, 480, 0, 504, 120, 120, true) + key = PSI.ConstraintKey( + ActivePowerVariableTimeSeriesLimitsConstraint, + ThermalStandard, + "ub", + ) + constraint = PSI.get_constraint(PSI.get_optimization_container(model), key) + ub_value = get_max_active_power(solitude) * 0.8 + for ix in eachindex(constraint) + @test JuMP.normalized_rhs(constraint[ix]) == ub_value + end + psi_checkobjfun_test(model, GAEVF) +end From 5bd5dffd872c01ecc967ad82e116d302ffb44d5b Mon Sep 17 00:00:00 2001 From: Jose Daniel Lara Date: Sun, 13 Oct 2024 15:00:43 -0600 Subject: [PATCH 09/33] fix test --- src/services_models/services_constructor.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/services_models/services_constructor.jl b/src/services_models/services_constructor.jl index 8b02409f0..e645c2cf8 100644 --- a/src/services_models/services_constructor.jl +++ b/src/services_models/services_constructor.jl @@ -534,9 +534,9 @@ function construct_service!( network_model::NetworkModel{<:PM.AbstractPowerModel}, ) where {T <: PSY.TransmissionInterface} interfaces = get_available_components(model, sys) + interface = PSY.get_component(T, sys, get_service_name(model)) if get_use_slacks(model) # Adding the slacks can be done in a cleaner fashion - interface = PSY.get_component(T, sys, get_service_name(model)) @assert PSY.get_available(interface) transmission_interface_slacks!(container, interface) end @@ -548,7 +548,7 @@ function construct_service!( PSY.get_name.(interfaces), get_time_steps(container), ) - add_feedforward_arguments!(container, model, service) + add_feedforward_arguments!(container, model, interface) return end From 792e5471c75a8e5e543de32bbbee4b4dec94b19b Mon Sep 17 00:00:00 2001 From: Jose Daniel Lara Date: Sun, 13 Oct 2024 15:52:39 -0600 Subject: [PATCH 10/33] comment out unrelated broken method --- src/services_models/services_constructor.jl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/services_models/services_constructor.jl b/src/services_models/services_constructor.jl index e645c2cf8..f547d622b 100644 --- a/src/services_models/services_constructor.jl +++ b/src/services_models/services_constructor.jl @@ -548,7 +548,8 @@ function construct_service!( PSY.get_name.(interfaces), get_time_steps(container), ) - add_feedforward_arguments!(container, model, interface) + # TODO:broken + # add_feedforward_arguments!(container, model, interface) return end From 46085e76ad33071bab96a0087a5dda025fdb32d3 Mon Sep 17 00:00:00 2001 From: Jose Daniel Lara Date: Sun, 13 Oct 2024 17:55:41 -0600 Subject: [PATCH 11/33] add missing ts check --- src/parameters/update_container_parameter_values.jl | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/parameters/update_container_parameter_values.jl b/src/parameters/update_container_parameter_values.jl index 5784c0964..be42d2b00 100644 --- a/src/parameters/update_container_parameter_values.jl +++ b/src/parameters/update_container_parameter_values.jl @@ -50,6 +50,9 @@ function _update_parameter_values!( components = get_available_components(device_model, get_system(model)) ts_uuids = Set{String}() for component in components + if !PSY.has_time_series(component) + continue + end ts_uuid = string(IS.get_time_series_uuid(U, component, ts_name)) if !(ts_uuid in ts_uuids) ts_vector = get_time_series_values!( From 4ba5a3bdd3219a610d7a3359189cde2881bf813a Mon Sep 17 00:00:00 2001 From: rodrigomha Date: Tue, 15 Oct 2024 10:59:26 -0700 Subject: [PATCH 12/33] update fuelcost param changes --- src/devices_models/devices/thermal_generation.jl | 1 + 1 file changed, 1 insertion(+) diff --git a/src/devices_models/devices/thermal_generation.jl b/src/devices_models/devices/thermal_generation.jl index ebe8bedf9..b3573d405 100644 --- a/src/devices_models/devices/thermal_generation.jl +++ b/src/devices_models/devices/thermal_generation.jl @@ -51,6 +51,7 @@ get_variable_binary(::Union{ColdStartVariable, WarmStartVariable, HotStartVariab ########################### Parameter related set functions ################################ get_multiplier_value(::ActivePowerTimeSeriesParameter, d::PSY.ThermalGen, ::AbstractThermalFormulation) = PSY.get_max_active_power(d) +get_multiplier_value(::FuelCostTimeSeriesParameter, d::PSY.ThermalGen, ::AbstractThermalFormulation) = PSY.get_proportional_term(d.operation_cost.variable.value_curve) get_parameter_multiplier(::VariableValueParameter, d::PSY.ThermalGen, ::AbstractThermalFormulation) = 1.0 get_initial_parameter_value(::VariableValueParameter, d::PSY.ThermalGen, ::AbstractThermalFormulation) = 1.0 get_expression_multiplier(::OnStatusParameter, ::ActivePowerRangeExpressionUB, d::PSY.ThermalGen, ::AbstractThermalFormulation) = PSY.get_active_power_limits(d).max From f69b942f1009e9773e2de548a38811a6e900af15 Mon Sep 17 00:00:00 2001 From: rodrigomha Date: Tue, 15 Oct 2024 16:35:14 -0700 Subject: [PATCH 13/33] add new params and exprs --- src/core/expressions.jl | 1 + src/core/parameters.jl | 10 +++++----- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/core/expressions.jl b/src/core/expressions.jl index f5ad354a7..173cfe7f4 100644 --- a/src/core/expressions.jl +++ b/src/core/expressions.jl @@ -8,6 +8,7 @@ struct EmergencyUp <: ExpressionType end struct EmergencyDown <: ExpressionType end struct RawACE <: ExpressionType end struct ProductionCostExpression <: CostExpressions end +struct FuelConsumptionExpression <: ExpressionType end struct ActivePowerRangeExpressionLB <: RangeConstraintLBExpressions end struct ActivePowerRangeExpressionUB <: RangeConstraintUBExpressions end struct ComponentReserveUpBalanceExpression <: ExpressionType end diff --git a/src/core/parameters.jl b/src/core/parameters.jl index e772d6e37..ae90e880c 100644 --- a/src/core/parameters.jl +++ b/src/core/parameters.jl @@ -276,11 +276,6 @@ Parameter to define Min Flow limit for interface time series """ struct MinInterfaceFlowLimitParameter <: TimeSeriesParameter end -""" -Parameter to define fuel cost time series -""" -struct FuelCostTimeSeriesParameter <: TimeSeriesParameter end - abstract type VariableValueParameter <: RightHandSideParameter end """ @@ -308,6 +303,11 @@ Parameter to define cost function coefficient """ struct CostFunctionParameter <: ObjectiveFunctionParameter end +""" +Parameter to define fuel cost time series +""" +struct FuelCostParameter <: ObjectiveFunctionParameter end + abstract type AuxVariableValueParameter <: RightHandSideParameter end struct EventParameter <: ParameterType end From 27d80fcc78aafdbffdc69637425cf4d0f6300091 Mon Sep 17 00:00:00 2001 From: rodrigomha Date: Tue, 15 Oct 2024 16:35:29 -0700 Subject: [PATCH 14/33] update device model to take any parameter type --- src/core/device_model.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/device_model.jl b/src/core/device_model.jl index edc65bba8..1d30dd888 100644 --- a/src/core/device_model.jl +++ b/src/core/device_model.jl @@ -49,7 +49,7 @@ mutable struct DeviceModel{D <: PSY.Device, B <: AbstractDeviceFormulation} use_slacks::Bool duals::Vector{DataType} services::Vector{ServiceModel} - time_series_names::Dict{Type{<:TimeSeriesParameter}, String} + time_series_names::Dict{Type{<:PSI.ParameterType}, String} attributes::Dict{String, Any} subsystem::Union{Nothing, String} From 93a8fa2d9163d945c4d05797d54cbeb9a9bc94c8 Mon Sep 17 00:00:00 2001 From: rodrigomha Date: Tue, 15 Oct 2024 16:36:02 -0700 Subject: [PATCH 15/33] [WIP] update abstract thermal dispatch to include fuel expressions --- .../thermalgeneration_constructor.jl | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/devices_models/device_constructors/thermalgeneration_constructor.jl b/src/devices_models/device_constructors/thermalgeneration_constructor.jl index bbb169b86..6d5d19fa6 100644 --- a/src/devices_models/device_constructors/thermalgeneration_constructor.jl +++ b/src/devices_models/device_constructors/thermalgeneration_constructor.jl @@ -789,8 +789,8 @@ function construct_device!( add_variables!(container, ActivePowerVariable, devices, D()) - if haskey(get_time_series_names(model), FuelCostTimeSeriesParameter) - add_parameters!(container, FuelCostTimeSeriesParameter, devices, model) + if haskey(get_time_series_names(model), FuelCostParameter) + add_parameters!(container, FuelCostParameter, devices, model) end add_to_expression!( @@ -804,6 +804,8 @@ function construct_device!( add_expressions!(container, ProductionCostExpression, devices, model) + add_expressions!(container, FuelConsumptionExpression, devices, model) + add_to_expression!( container, ActivePowerRangeExpressionLB, @@ -820,6 +822,13 @@ function construct_device!( model, network_model, ) + add_to_expression!( + container, + FuelConsumptionExpression, + ActivePowerVariable, + devices, + model, + ) add_feedforward_arguments!(container, model, devices) return From deef888dba9e620d894858e7184af8d21fad613c Mon Sep 17 00:00:00 2001 From: rodrigomha Date: Tue, 15 Oct 2024 16:36:14 -0700 Subject: [PATCH 16/33] update thermal time series --- src/devices_models/devices/thermal_generation.jl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/devices_models/devices/thermal_generation.jl b/src/devices_models/devices/thermal_generation.jl index b3573d405..ac6d3e3e3 100644 --- a/src/devices_models/devices/thermal_generation.jl +++ b/src/devices_models/devices/thermal_generation.jl @@ -51,7 +51,7 @@ get_variable_binary(::Union{ColdStartVariable, WarmStartVariable, HotStartVariab ########################### Parameter related set functions ################################ get_multiplier_value(::ActivePowerTimeSeriesParameter, d::PSY.ThermalGen, ::AbstractThermalFormulation) = PSY.get_max_active_power(d) -get_multiplier_value(::FuelCostTimeSeriesParameter, d::PSY.ThermalGen, ::AbstractThermalFormulation) = PSY.get_proportional_term(d.operation_cost.variable.value_curve) +get_multiplier_value(::FuelCostParameter, d::PSY.ThermalGen, ::AbstractThermalFormulation) = 1.0 get_parameter_multiplier(::VariableValueParameter, d::PSY.ThermalGen, ::AbstractThermalFormulation) = 1.0 get_initial_parameter_value(::VariableValueParameter, d::PSY.ThermalGen, ::AbstractThermalFormulation) = 1.0 get_expression_multiplier(::OnStatusParameter, ::ActivePowerRangeExpressionUB, d::PSY.ThermalGen, ::AbstractThermalFormulation) = PSY.get_active_power_limits(d).max @@ -195,8 +195,8 @@ function get_default_time_series_names( ::Type{U}, ::Type{V}, ) where {U <: PSY.ThermalGen, V <: Union{FixedOutput, AbstractThermalFormulation}} - return Dict{Type{<:TimeSeriesParameter}, String}( - FuelCostTimeSeriesParameter => "fuel_cost", + return Dict{Any, String}( + FuelCostParameter => "fuel_cost", ) end From 10d60f32e9fca11368ca5715603bfafa358083a8 Mon Sep 17 00:00:00 2001 From: rodrigomha Date: Tue, 15 Oct 2024 16:36:34 -0700 Subject: [PATCH 17/33] update expression for fuel consumption --- .../devices/common/add_to_expression.jl | 65 +++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/src/devices_models/devices/common/add_to_expression.jl b/src/devices_models/devices/common/add_to_expression.jl index 9c53a40e0..533267abc 100644 --- a/src/devices_models/devices/common/add_to_expression.jl +++ b/src/devices_models/devices/common/add_to_expression.jl @@ -26,6 +26,25 @@ function add_expressions!( return end +function add_expressions!( + container::OptimizationContainer, + ::Type{T}, + devices::U, + model::DeviceModel{D, W}, +) where { + T <: FuelConsumptionExpression, + U <: Union{Vector{D}, IS.FlattenIteratorWrapper{D}}, + W <: AbstractDeviceFormulation, +} where {D <: PSY.Component} + time_steps = get_time_steps(container) + names = [ + PSY.get_name(d) for + d in devices if PSY.get_variable(PSY.get_operation_cost(d)) isa PSY.FuelCurve + ] + add_expression_container!(container, T(), D, names, time_steps) + return +end + function add_expressions!( container::OptimizationContainer, ::Type{T}, @@ -1533,6 +1552,52 @@ function add_to_expression!( return end +function add_to_expression!( + container::OptimizationContainer, + ::Type{T}, + ::Type{U}, + devices::IS.FlattenIteratorWrapper{V}, + model::DeviceModel{V, W}, +) where { + T <: FuelConsumptionExpression, + U <: ActivePowerVariable, + V <: PSY.ThermalGen, + W <: AbstractThermalDispatchFormulation, +} + expression = get_expression(container, T(), V) + variable = get_variable(container, U(), V) + time_steps = get_time_steps(container) + base_power = get_base_power(container) + for d in devices + var_cost = PSY.get_variable(PSY.get_operation_cost(d)) + if !(var_cost isa PSY.FuelCurve) + continue + end + name = PSY.get_name(d) + device_base_power = PSY.get_base_power(d) + value_curve = PSY.get_value_curve(var_cost) + if value_curve isa PSY.LinearCurve + power_units = PSY.get_power_units(var_cost) + proportional_term = PSY.get_proportional_term(value_curve) + prop_term_per_unit = get_proportional_cost_per_system_unit( + proportional_term, + power_units, + base_power, + device_base_power, + ) + for t in time_steps + fuel_expr = variable[name, t] * prop_term_per_unit + JuMP.add_to_expression!( + expression[name, t], + fuel_expr, + ) + end + else + error("Not implemented yet") + end + end +end + #= function add_to_expression!( container::OptimizationContainer, From 97156f5df67a049ac2ca2fc3a34f963afa4d9062 Mon Sep 17 00:00:00 2001 From: rodrigomha Date: Tue, 15 Oct 2024 16:36:55 -0700 Subject: [PATCH 18/33] add fuel cost timeseries for linear --- .../common/objective_function/linear_curve.jl | 24 +++++++++++++++---- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/src/devices_models/devices/common/objective_function/linear_curve.jl b/src/devices_models/devices/common/objective_function/linear_curve.jl index 06d47c9b2..7d76ecdb0 100644 --- a/src/devices_models/devices/common/objective_function/linear_curve.jl +++ b/src/devices_models/devices/common/objective_function/linear_curve.jl @@ -115,12 +115,26 @@ end function _add_fuel_linear_variable_cost!( container::OptimizationContainer, ::T, - component::PSY.Component, - fuel_curve::Float64, + component::V, + heat_rate::Float64, # already normalized in MMBTU/p.u. fuel_cost::IS.TimeSeriesKey, -) where {T <: VariableType} - error("Not implemented yet") - _add_linearcurve_variable_cost!(container, T(), component, fuel_curve) +) where {T <: VariableType, V <: PSY.Component} + parameter = get_parameter_array(container, FuelCostParameter(), V) + multiplier = get_parameter_multiplier_array(container, FuelCostParameter(), V) + expression = get_expression(container, FuelConsumptionExpression(), V) + name = PSY.get_name(component) + for t in get_time_steps(container) + cost_expr = expression[name, t] * parameter[name, t] * multiplier[name, t] + add_to_expression!( + container, + ProductionCostExpression, + cost_expr, + component, + t, + ) + add_to_objective_variant_expression!(container, cost_expr) + end + return end """ From 8ecdd2163bc12847e81c98f1c9e887263c0b7d72 Mon Sep 17 00:00:00 2001 From: rodrigomha Date: Tue, 15 Oct 2024 16:37:06 -0700 Subject: [PATCH 19/33] add fuel cost params methods --- src/parameters/add_parameters.jl | 64 +++++++++++++++++++++++++++++++- 1 file changed, 63 insertions(+), 1 deletion(-) diff --git a/src/parameters/add_parameters.jl b/src/parameters/add_parameters.jl index ae39d01d4..4024848fd 100644 --- a/src/parameters/add_parameters.jl +++ b/src/parameters/add_parameters.jl @@ -34,6 +34,23 @@ function add_parameters!( return end +function add_parameters!( + container::OptimizationContainer, + ::Type{T}, + devices::U, + model::DeviceModel{D, W}, +) where { + T <: FuelCostParameter, + U <: Union{Vector{D}, IS.FlattenIteratorWrapper{D}}, + W <: AbstractDeviceFormulation, +} where {D <: PSY.Component} + if get_rebuild_model(get_settings(container)) && has_container_key(container, T, D) + return + end + _add_parameters!(container, T(), devices, model) + return +end + function add_parameters!( container::OptimizationContainer, ::Type{T}, @@ -256,7 +273,52 @@ function _add_parameters!( U <: Union{Vector{D}, IS.FlattenIteratorWrapper{D}}, W <: AbstractDeviceFormulation, } where {D <: PSY.Component} - _add_time_series_parameters!(container, param, devices, model) + #error("here") + ts_type = get_default_time_series_type(container) + if !(ts_type <: Union{PSY.AbstractDeterministic, PSY.StaticTimeSeries}) + error("add_parameters! for TimeSeriesParameter is not compatible with $ts_type") + end + time_steps = get_time_steps(container) + # TODO: Check for timeseries only for fuel cost + device_names = [PSY.get_name(x) for x in devices if PSY.has_time_series(x)] + jump_model = get_jump_model(container) + + param_container = add_param_container!( + container, + param, + D, + ActivePowerVariable, + PSI.SOSStatusVariable.NO_VARIABLE, + false, + Float64, + device_names, + time_steps, + ) + + ts_name = get_time_series_names(model)[T] + + for device in devices + if !PSY.has_time_series(device) + continue + end + ts_vals = get_time_series_initial_values!(container, ts_type, device, ts_name) + name = PSY.get_name(device) + for step in time_steps + PSI.set_parameter!( + param_container, + jump_model, + ts_vals[step], + name, + step, + ) + PSI.set_multiplier!( + param_container, + get_multiplier_value(T(), device, W()), + name, + step, + ) + end + end return end From 73528eb16fc2f3803dd2be1c4fd165bb75bbb303 Mon Sep 17 00:00:00 2001 From: rodrigomha Date: Tue, 15 Oct 2024 16:39:52 -0700 Subject: [PATCH 20/33] remove commented error --- src/parameters/add_parameters.jl | 1 - 1 file changed, 1 deletion(-) diff --git a/src/parameters/add_parameters.jl b/src/parameters/add_parameters.jl index 4024848fd..d06881d0c 100644 --- a/src/parameters/add_parameters.jl +++ b/src/parameters/add_parameters.jl @@ -273,7 +273,6 @@ function _add_parameters!( U <: Union{Vector{D}, IS.FlattenIteratorWrapper{D}}, W <: AbstractDeviceFormulation, } where {D <: PSY.Component} - #error("here") ts_type = get_default_time_series_type(container) if !(ts_type <: Union{PSY.AbstractDeterministic, PSY.StaticTimeSeries}) error("add_parameters! for TimeSeriesParameter is not compatible with $ts_type") From 06a4253a95c76e714e7f79ac9a9836b6e14201b0 Mon Sep 17 00:00:00 2001 From: rodrigomha Date: Wed, 16 Oct 2024 16:44:47 -0700 Subject: [PATCH 21/33] add export --- src/PowerSimulations.jl | 1 + 1 file changed, 1 insertion(+) diff --git a/src/PowerSimulations.jl b/src/PowerSimulations.jl index cd8544a63..3b3fe5246 100644 --- a/src/PowerSimulations.jl +++ b/src/PowerSimulations.jl @@ -323,6 +323,7 @@ export EmergencyUp export EmergencyDown export RawACE export ProductionCostExpression +export FuelConsumptionExpression export ActivePowerRangeExpressionLB export ActivePowerRangeExpressionUB From 3582a85f73936780121b1ebfe1fbc3d42db8496b Mon Sep 17 00:00:00 2001 From: rodrigomha Date: Wed, 16 Oct 2024 16:44:57 -0700 Subject: [PATCH 22/33] add write result --- src/core/expressions.jl | 1 + 1 file changed, 1 insertion(+) diff --git a/src/core/expressions.jl b/src/core/expressions.jl index 173cfe7f4..e1e8454eb 100644 --- a/src/core/expressions.jl +++ b/src/core/expressions.jl @@ -17,6 +17,7 @@ struct InterfaceTotalFlow <: ExpressionType end struct PTDFBranchFlow <: ExpressionType end should_write_resulting_value(::Type{<:CostExpressions}) = true +should_write_resulting_value(::Type{FuelConsumptionExpression}) = true should_write_resulting_value(::Type{InterfaceTotalFlow}) = true should_write_resulting_value(::Type{RawACE}) = true should_write_resulting_value(::Type{ActivePowerBalance}) = true From 97f6de34c10929e78ff10e1a02f6a366d16b0765 Mon Sep 17 00:00:00 2001 From: rodrigomha Date: Wed, 16 Oct 2024 16:45:41 -0700 Subject: [PATCH 23/33] avoid mutation of invariant terms --- src/core/optimization_container.jl | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/core/optimization_container.jl b/src/core/optimization_container.jl index b94438f9b..c2702c64c 100644 --- a/src/core/optimization_container.jl +++ b/src/core/optimization_container.jl @@ -35,8 +35,12 @@ function get_objective_expression(v::ObjectiveFunction) else # JuMP doesn't support expression conversion from Affn to QuadExpressions if isa(v.invariant_terms, JuMP.GenericQuadExpr) - return JuMP.add_to_expression!(v.invariant_terms, v.variant_terms) + # Avoid mutation of invariant term + temp_expr = JuMP.QuadExpr() + JuMP.add_to_expression!(temp_expr, v.invariant_terms) + return JuMP.add_to_expression!(temp_expr, v.variant_terms) else + # This will mutate the variant terms, but these are reseted at each step. return JuMP.add_to_expression!(v.variant_terms, v.invariant_terms) end end From 2aecfd5c6b3ee32249b62e50d7b702ed656b95b2 Mon Sep 17 00:00:00 2001 From: rodrigomha Date: Wed, 16 Oct 2024 16:46:16 -0700 Subject: [PATCH 24/33] add quadratic and dt to fuel consumption --- .../devices/common/add_to_expression.jl | 35 ++++++++++++++++--- 1 file changed, 31 insertions(+), 4 deletions(-) diff --git a/src/devices_models/devices/common/add_to_expression.jl b/src/devices_models/devices/common/add_to_expression.jl index 533267abc..9c053de1e 100644 --- a/src/devices_models/devices/common/add_to_expression.jl +++ b/src/devices_models/devices/common/add_to_expression.jl @@ -1522,7 +1522,7 @@ function add_to_expression!( cost_expression::Union{JuMP.AbstractJuMPScalar, Float64}, component::T, time_period::Int, -) where {S <: CostExpressions, T <: PSY.Component} +) where {S <: Union{CostExpressions, FuelConsumptionExpression}, T <: PSY.Component} if has_container_key(container, S, T) device_cost_expression = get_expression(container, S(), T) component_name = PSY.get_name(component) @@ -1568,6 +1568,8 @@ function add_to_expression!( variable = get_variable(container, U(), V) time_steps = get_time_steps(container) base_power = get_base_power(container) + resolution = get_resolution(container) + dt = Dates.value(resolution) / MILLISECONDS_IN_HOUR for d in devices var_cost = PSY.get_variable(PSY.get_operation_cost(d)) if !(var_cost isa PSY.FuelCurve) @@ -1586,14 +1588,39 @@ function add_to_expression!( device_base_power, ) for t in time_steps - fuel_expr = variable[name, t] * prop_term_per_unit + fuel_expr = variable[name, t] * prop_term_per_unit * dt + JuMP.add_to_expression!( + expression[name, t], + fuel_expr, + ) + end + elseif value_curve isa PSY.QuadraticCurve + power_units = PSY.get_power_units(var_cost) + proportional_term = PSY.get_proportional_term(value_curve) + quadratic_term = PSY.get_quadratic_term(value_curve) + prop_term_per_unit = get_proportional_cost_per_system_unit( + proportional_term, + power_units, + base_power, + device_base_power, + ) + quad_term_per_unit = get_quadratic_cost_per_system_unit( + quadratic_term, + power_units, + base_power, + device_base_power, + ) + for t in time_steps + fuel_expr = + ( + variable[name, t] .^ 2 * quad_term_per_unit + + variable[name, t] * prop_term_per_unit + ) * dt JuMP.add_to_expression!( expression[name, t], fuel_expr, ) end - else - error("Not implemented yet") end end end From 04b4dc4d72bc11fecba19995e8fce8826bdde2bd Mon Sep 17 00:00:00 2001 From: rodrigomha Date: Wed, 16 Oct 2024 16:51:37 -0700 Subject: [PATCH 25/33] Add parameter for fuel cost Co-authored-by: jd-lara --- src/parameters/add_parameters.jl | 7 +- src/parameters/update_cost_parameters.jl | 104 ++++++++++++++++++----- 2 files changed, 89 insertions(+), 22 deletions(-) diff --git a/src/parameters/add_parameters.jl b/src/parameters/add_parameters.jl index d06881d0c..d4c7a3cd6 100644 --- a/src/parameters/add_parameters.jl +++ b/src/parameters/add_parameters.jl @@ -275,11 +275,16 @@ function _add_parameters!( } where {D <: PSY.Component} ts_type = get_default_time_series_type(container) if !(ts_type <: Union{PSY.AbstractDeterministic, PSY.StaticTimeSeries}) - error("add_parameters! for TimeSeriesParameter is not compatible with $ts_type") + error( + "add_parameters! for ObjectiveFunctionParameter is not compatible with $ts_type", + ) end time_steps = get_time_steps(container) # TODO: Check for timeseries only for fuel cost device_names = [PSY.get_name(x) for x in devices if PSY.has_time_series(x)] + if isempty(device_names) + return + end jump_model = get_jump_model(container) param_container = add_param_container!( diff --git a/src/parameters/update_cost_parameters.jl b/src/parameters/update_cost_parameters.jl index 600eca675..6adc85707 100644 --- a/src/parameters/update_cost_parameters.jl +++ b/src/parameters/update_cost_parameters.jl @@ -14,31 +14,62 @@ function _update_parameter_values!( template = get_template(model) device_model = get_model(template, V) components = get_available_components(device_model, get_system(model)) - for component in components if _has_variable_cost_parameter(component) name = PSY.get_name(component) - ts_vector = PSY.get_variable_cost( - component, - PSY.get_operation_cost(component); - start_time = initial_forecast_time, - len = horizon, - ) - variable_cost_forecast_values = TimeSeries.values(ts_vector) - for (t, value) in enumerate(variable_cost_forecast_values) - if attributes.uses_compact_power - # TODO implement this - value, _ = _convert_variable_cost(value) - end - # TODO removed an apparently unused block of code here? - _set_param_value!(parameter_array, value, name, t) - update_variable_cost!( - container, - parameter_array, - parameter_multiplier, - attributes, + op_cost = PSY.get_operation_cost(component) + if op_cost isa PSY.MarketBidCost + ts_vector = PSY.get_variable_cost( component, - t, + PSY.get_operation_cost(component); + start_time = initial_forecast_time, + len = horizon, + ) + variable_cost_forecast_values = TimeSeries.values(ts_vector) + for (t, value) in enumerate(variable_cost_forecast_values) + if attributes.uses_compact_power + # TODO implement this + value, _ = _convert_variable_cost(value) + end + # TODO removed an apparently unused block of code here? + _set_param_value!(parameter_array, value, name, t) + update_variable_cost!( + container, + parameter_array, + parameter_multiplier, + attributes, + component, + t, + ) + end + elseif op_cost isa PSY.ThermalGenerationCost + fuel_curve = PSY.get_variable(op_cost) + ts_vector = PSY.get_fuel_cost( + component; + start_time = initial_forecast_time, + len = horizon, + ) + fuel_cost_forecast_values = TimeSeries.values(ts_vector) + for (t, value) in enumerate(fuel_cost_forecast_values) + if attributes.uses_compact_power + # TODO implement this + value, _ = _convert_variable_cost(value) + end + # TODO removed an apparently unused block of code here? + _set_param_value!(parameter_array, value, name, t) + update_variable_cost!( + container, + parameter_array, + parameter_multiplier, + attributes, + component, + fuel_curve, + t, + ) + end + else + error( + "Update Cost Function Parameter not implemented for $(typeof(op_cost))", ) end end @@ -50,6 +81,15 @@ _has_variable_cost_parameter(component::PSY.Component) = _has_variable_cost_parameter(PSY.get_operation_cost(component)) _has_variable_cost_parameter(::PSY.MarketBidCost) = true _has_variable_cost_parameter(::T) where {T <: PSY.OperationalCost} = false +function _has_variable_cost_parameter(cost::T) where {T <: PSY.ThermalGenerationCost} + var_cost = PSY.get_variable(cost) + if var_cost isa PSY.FuelCurve + if PSY.get_fuel_cost(var_cost) isa IS.TimeSeriesKey + return true + end + end + return false +end function _update_pwl_cost_expression( container::OptimizationContainer, @@ -123,3 +163,25 @@ function update_variable_cost!( set_expression!(container, ProductionCostExpression, gen_cost, component, time_period) return end + +function update_variable_cost!( + container::OptimizationContainer, + parameter_array::JuMPFloatArray, + parameter_multiplier::JuMPFloatArray, + ::CostFunctionAttributes{Float64}, + component::T, + fuel_curve::PSY.FuelCurve, + time_period::Int, +) where {T <: PSY.Component} + component_name = PSY.get_name(component) + fuel_cost = parameter_array[component_name, time_period] + if all(iszero.(last.(fuel_cost))) + return + end + mult_ = parameter_multiplier[component_name, time_period] + expression = get_expression(container, FuelConsumptionExpression(), T) + cost_expr = expression[component_name, time_period] * fuel_cost * mult_ + add_to_objective_variant_expression!(container, cost_expr) + set_expression!(container, ProductionCostExpression, cost_expr, component, time_period) + return +end From 50f7ed494437c3c9232409f0aa3677669ab5fd7b Mon Sep 17 00:00:00 2001 From: rodrigomha Date: Wed, 16 Oct 2024 16:53:39 -0700 Subject: [PATCH 26/33] update getter for fuel cost pwl Co-authored-by: jd-lara --- .../common/objective_function/common.jl | 44 ++++++++--- .../objective_function/piecewise_linear.jl | 77 ++++++++++++++++--- 2 files changed, 99 insertions(+), 22 deletions(-) diff --git a/src/devices_models/devices/common/objective_function/common.jl b/src/devices_models/devices/common/objective_function/common.jl index c02b55a51..84297bca4 100644 --- a/src/devices_models/devices/common/objective_function/common.jl +++ b/src/devices_models/devices/common/objective_function/common.jl @@ -265,18 +265,42 @@ end ################################################## function _get_fuel_cost_value( - ::OptimizationContainer, - fuel_cost::Float64, - ::Int, -) - return fuel_cost + container::OptimizationContainer, + component::T, + time_period::Int, +) where {T <: PSY.Component} + # TODO: Check time series for derating to work later + if PSY.has_time_series(component) + parameter_array = get_parameter_array(container, FuelCostParameter(), T) + parameter_multiplier = + get_parameter_multiplier_array(container, FuelCostParameter(), T) + name = PSY.get_name(component) + return parameter_array[name, time_period] * parameter_multiplier[name, time_period] + else + return PSY.get_fuel_cost(component) + end end -function _get_fuel_cost_value( +function _add_time_varying_fuel_variable_cost!( container::OptimizationContainer, + ::T, + component::V, fuel_cost::IS.TimeSeriesKey, - time_period::Int, -) - error("Not implemented yet fuel cost") - return fuel_cost +) where {T <: VariableType, V <: PSY.Component} + parameter = get_parameter_array(container, FuelCostParameter(), V) + multiplier = get_parameter_multiplier_array(container, FuelCostParameter(), V) + expression = get_expression(container, FuelConsumptionExpression(), V) + name = PSY.get_name(component) + for t in get_time_steps(container) + cost_expr = expression[name, t] * parameter[name, t] * multiplier[name, t] + add_to_expression!( + container, + ProductionCostExpression, + cost_expr, + component, + t, + ) + add_to_objective_variant_expression!(container, cost_expr) + end + return end diff --git a/src/devices_models/devices/common/objective_function/piecewise_linear.jl b/src/devices_models/devices/common/objective_function/piecewise_linear.jl index d173cd85d..f39378f2c 100644 --- a/src/devices_models/devices/common/objective_function/piecewise_linear.jl +++ b/src/devices_models/devices/common/objective_function/piecewise_linear.jl @@ -320,22 +320,18 @@ function _get_pwl_cost_expression( base_power, device_base_power, ) - fuel_cost = PSY.get_fuel_cost(cost_function) - fuel_cost_value = _get_fuel_cost_value( - container, - fuel_cost, - time_period, - ) # Multiplier is not necessary here. There is no negative cost for fuel curves. resolution = get_resolution(container) dt = Dates.value(resolution) / MILLISECONDS_IN_HOUR - return _get_pwl_cost_expression( + # TODO: Update name get_pwl_cost_expression + fuel_consumption_expression = _get_pwl_cost_expression( container, component, time_period, cost_data_normalized, - dt * fuel_cost_value, + dt, ) + return fuel_consumption_expression end ################################################## @@ -484,10 +480,7 @@ function _add_variable_cost_to_objective!( container::OptimizationContainer, ::T, component::PSY.Component, - cost_function::Union{ - PSY.CostCurve{PSY.PiecewisePointCurve}, - PSY.FuelCurve{PSY.PiecewisePointCurve}, - }, + cost_function::PSY.CostCurve{PSY.PiecewisePointCurve}, ::U, ) where {T <: VariableType, U <: AbstractDeviceFormulation} component_name = PSY.get_name(component) @@ -515,6 +508,66 @@ function _add_variable_cost_to_objective!( return end +""" +Creates piecewise linear cost function using a sum of variables and expression with sign and time step included. + +# Arguments + + - 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_function::PSY.CostCurve{PSY.PiecewisePointCurve}: container for piecewise linear cost +""" +function _add_variable_cost_to_objective!( + container::OptimizationContainer, + ::T, + component::PSY.Component, + cost_function::PSY.FuelCurve{PSY.PiecewisePointCurve}, + ::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 + value_curve = PSY.get_value_curve(cost_function) + cost_component = PSY.get_function_data(value_curve) + 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_fuel_consumption_expressions = + _add_pwl_term!(container, component, cost_function, T(), U()) + is_time_variant = PSY.has_time_series(component) + for t in get_time_steps(container) + fuel_cost_value = _get_fuel_cost_value( + container, + component, + t, + ) + pwl_cost_expression = pwl_fuel_consumption_expressions[t] * fuel_cost_value + add_to_expression!( + container, + ProductionCostExpression, + pwl_cost_expression, + component, + t, + ) + add_to_expression!( + container, + FuelConsumptionExpression, + pwl_fuel_consumption_expressions[t], + component, + t, + ) + if is_time_variant + add_to_objective_variant_expression!(container, pwl_cost_expression) + else + add_to_objective_invariant_expression!(container, pwl_cost_expression) + end + end + return +end + ################################################## ###### CostCurve: PiecewiseIncrementalCurve ###### ######### and PiecewiseAverageCurve ############## From 3d74a48d65529a120f14be4cdcdbc523d8f54605 Mon Sep 17 00:00:00 2001 From: rodrigomha Date: Wed, 16 Oct 2024 16:54:02 -0700 Subject: [PATCH 27/33] update linear and quadratic methods to use new function --- .../common/objective_function/linear_curve.jl | 18 ++---------------- .../objective_function/quadratic_curve.jl | 9 +-------- 2 files changed, 3 insertions(+), 24 deletions(-) diff --git a/src/devices_models/devices/common/objective_function/linear_curve.jl b/src/devices_models/devices/common/objective_function/linear_curve.jl index 7d76ecdb0..91e2a9eed 100644 --- a/src/devices_models/devices/common/objective_function/linear_curve.jl +++ b/src/devices_models/devices/common/objective_function/linear_curve.jl @@ -116,24 +116,10 @@ function _add_fuel_linear_variable_cost!( container::OptimizationContainer, ::T, component::V, - heat_rate::Float64, # already normalized in MMBTU/p.u. + ::Float64, # already normalized in MMBTU/p.u. fuel_cost::IS.TimeSeriesKey, ) where {T <: VariableType, V <: PSY.Component} - parameter = get_parameter_array(container, FuelCostParameter(), V) - multiplier = get_parameter_multiplier_array(container, FuelCostParameter(), V) - expression = get_expression(container, FuelConsumptionExpression(), V) - name = PSY.get_name(component) - for t in get_time_steps(container) - cost_expr = expression[name, t] * parameter[name, t] * multiplier[name, t] - add_to_expression!( - container, - ProductionCostExpression, - cost_expr, - component, - t, - ) - add_to_objective_variant_expression!(container, cost_expr) - end + _add_time_varying_fuel_variable_cost!(container, T(), component, fuel_cost) return end diff --git a/src/devices_models/devices/common/objective_function/quadratic_curve.jl b/src/devices_models/devices/common/objective_function/quadratic_curve.jl index 686dc0d23..0bc31bbe5 100644 --- a/src/devices_models/devices/common/objective_function/quadratic_curve.jl +++ b/src/devices_models/devices/common/objective_function/quadratic_curve.jl @@ -212,14 +212,7 @@ function _add_fuel_quadratic_variable_cost!( quadratic_fuel_curve::Float64, fuel_cost::IS.TimeSeriesKey, ) where {T <: VariableType} - error("Not implemented yet") - _add_quadraticcurve_variable_cost!( - container, - T(), - component, - proportional_fuel_curve, - quadratic_fuel_curve, - ) + _add_time_varying_fuel_variable_cost!(container, T(), component, fuel_cost) end @doc raw""" From d20061c6f990a9d2fd6dd533da9ca40e5bba602e Mon Sep 17 00:00:00 2001 From: rodrigomha Date: Wed, 16 Oct 2024 20:58:55 -0700 Subject: [PATCH 28/33] add expressions for all formulations --- .../thermalgeneration_constructor.jl | 145 +++++++++++++++++- .../devices/common/add_to_expression.jl | 90 ++++++++++- 2 files changed, 231 insertions(+), 4 deletions(-) diff --git a/src/devices_models/device_constructors/thermalgeneration_constructor.jl b/src/devices_models/device_constructors/thermalgeneration_constructor.jl index 6d5d19fa6..37a5332e2 100644 --- a/src/devices_models/device_constructors/thermalgeneration_constructor.jl +++ b/src/devices_models/device_constructors/thermalgeneration_constructor.jl @@ -78,6 +78,7 @@ function construct_device!( ) add_expressions!(container, ProductionCostExpression, devices, model) + add_expressions!(container, FuelConsumptionExpression, devices, model) add_to_expression!( container, @@ -95,6 +96,13 @@ function construct_device!( model, network_model, ) + add_to_expression!( + container, + FuelConsumptionExpression, + ActivePowerVariable, + devices, + model, + ) add_feedforward_arguments!(container, model, devices) return end @@ -193,6 +201,7 @@ function construct_device!( ) add_expressions!(container, ProductionCostExpression, devices, model) + add_expressions!(container, FuelConsumptionExpression, devices, model) add_to_expression!( container, @@ -210,6 +219,13 @@ function construct_device!( model, network_model, ) + add_to_expression!( + container, + FuelConsumptionExpression, + ActivePowerVariable, + devices, + model, + ) add_feedforward_arguments!(container, model, devices) return @@ -307,6 +323,7 @@ function construct_device!( ) add_expressions!(container, ProductionCostExpression, devices, model) + add_expressions!(container, FuelConsumptionExpression, devices, model) add_to_expression!( container, @@ -324,6 +341,13 @@ function construct_device!( model, network_model, ) + add_to_expression!( + container, + FuelConsumptionExpression, + ActivePowerVariable, + devices, + model, + ) add_feedforward_arguments!(container, model, devices) return @@ -419,6 +443,7 @@ function construct_device!( ) add_expressions!(container, ProductionCostExpression, devices, model) + add_expressions!(container, FuelConsumptionExpression, devices, model) add_to_expression!( container, @@ -436,6 +461,13 @@ function construct_device!( model, network_model, ) + add_to_expression!( + container, + FuelConsumptionExpression, + ActivePowerVariable, + devices, + model, + ) add_feedforward_arguments!(container, model, devices) return @@ -524,6 +556,7 @@ function construct_device!( ) add_expressions!(container, ProductionCostExpression, devices, model) + add_expressions!(container, FuelConsumptionExpression, devices, model) add_to_expression!( container, @@ -541,6 +574,13 @@ function construct_device!( model, network_model, ) + add_to_expression!( + container, + FuelConsumptionExpression, + ActivePowerVariable, + devices, + model, + ) add_feedforward_arguments!(container, model, devices) return @@ -618,6 +658,7 @@ function construct_device!( ) add_expressions!(container, ProductionCostExpression, devices, model) + add_expressions!(container, FuelConsumptionExpression, devices, model) add_to_expression!( container, @@ -635,6 +676,13 @@ function construct_device!( model, network_model, ) + add_to_expression!( + container, + FuelConsumptionExpression, + ActivePowerVariable, + devices, + model, + ) add_feedforward_arguments!(container, model, devices) return @@ -711,6 +759,7 @@ function construct_device!( ) add_expressions!(container, ProductionCostExpression, devices, model) + add_expressions!(container, FuelConsumptionExpression, devices, model) add_to_expression!( container, @@ -728,6 +777,13 @@ function construct_device!( model, network_model, ) + add_to_expression!( + container, + FuelConsumptionExpression, + ActivePowerVariable, + devices, + model, + ) add_feedforward_arguments!(container, model, devices) return @@ -803,7 +859,6 @@ function construct_device!( ) add_expressions!(container, ProductionCostExpression, devices, model) - add_expressions!(container, FuelConsumptionExpression, devices, model) add_to_expression!( @@ -931,6 +986,7 @@ function construct_device!( ) add_expressions!(container, ProductionCostExpression, devices, model) + add_expressions!(container, FuelConsumptionExpression, devices, model) add_to_expression!( container, @@ -948,6 +1004,16 @@ function construct_device!( model, network_model, ) + #TODO: Implement for AbovePowerMinimum + #= + add_to_expression!( + container, + FuelConsumptionExpression, + PowerAboveMinimumVariable,, + devices, + model, + ) + =# add_feedforward_arguments!(container, model, devices) return @@ -1073,6 +1139,7 @@ function construct_device!( ) add_expressions!(container, ProductionCostExpression, devices, model) + add_expressions!(container, FuelConsumptionExpression, devices, model) add_to_expression!( container, @@ -1090,6 +1157,16 @@ function construct_device!( model, network_model, ) + #TODO: Implement for AbovePowerMinimum + #= + add_to_expression!( + container, + FuelConsumptionExpression, + PowerAboveMinimumVariable,, + devices, + model, + ) + =# add_feedforward_arguments!(container, model, devices) return @@ -1213,6 +1290,7 @@ function construct_device!( ) add_expressions!(container, ProductionCostExpression, devices, model) + add_expressions!(container, FuelConsumptionExpression, devices, model) add_to_expression!( container, @@ -1230,6 +1308,16 @@ function construct_device!( model, network_model, ) + #TODO: Implement for AbovePowerMinimum + #= + add_to_expression!( + container, + FuelConsumptionExpression, + PowerAboveMinimumVariable,, + devices, + model, + ) + =# add_feedforward_arguments!(container, model, devices) return @@ -1337,6 +1425,7 @@ function construct_device!( ) add_expressions!(container, ProductionCostExpression, devices, model) + add_expressions!(container, FuelConsumptionExpression, devices, model) add_to_expression!( container, @@ -1354,6 +1443,16 @@ function construct_device!( model, network_model, ) + #TODO: Implement for AbovePowerMinimum + #= + add_to_expression!( + container, + FuelConsumptionExpression, + PowerAboveMinimumVariable,, + devices, + model, + ) + =# add_feedforward_arguments!(container, model, devices) return end @@ -1456,6 +1555,7 @@ function construct_device!( ) add_expressions!(container, ProductionCostExpression, devices, model) + add_expressions!(container, FuelConsumptionExpression, devices, model) add_to_expression!( container, @@ -1473,6 +1573,16 @@ function construct_device!( model, network_model, ) + #TODO: Implement for AbovePowerMinimum + #= + add_to_expression!( + container, + FuelConsumptionExpression, + PowerAboveMinimumVariable,, + devices, + model, + ) + =# add_feedforward_arguments!(container, model, devices) return @@ -1576,6 +1686,7 @@ function construct_device!( ) add_expressions!(container, ProductionCostExpression, devices, model) + add_expressions!(container, FuelConsumptionExpression, devices, model) add_to_expression!( container, @@ -1593,6 +1704,16 @@ function construct_device!( model, network_model, ) + #TODO: Implement for AbovePowerMinimum + #= + add_to_expression!( + container, + FuelConsumptionExpression, + PowerAboveMinimumVariable,, + devices, + model, + ) + =# add_feedforward_arguments!(container, model, devices) return end @@ -1672,6 +1793,7 @@ function construct_device!( ) add_expressions!(container, ProductionCostExpression, devices, model) + add_expressions!(container, FuelConsumptionExpression, devices, model) add_to_expression!( container, @@ -1705,6 +1827,16 @@ function construct_device!( model, network_model, ) + #TODO: Implement for AbovePowerMinimum + #= + add_to_expression!( + container, + FuelConsumptionExpression, + PowerAboveMinimumVariable,, + devices, + model, + ) + =# return end @@ -1789,6 +1921,7 @@ function construct_device!( initial_conditions!(container, devices, ThermalCompactDispatch()) add_expressions!(container, ProductionCostExpression, devices, model) + add_expressions!(container, FuelConsumptionExpression, devices, model) add_to_expression!( container, @@ -1806,6 +1939,16 @@ function construct_device!( model, network_model, ) + #TODO: Implement for AbovePowerMinimum + #= + add_to_expression!( + container, + FuelConsumptionExpression, + PowerAboveMinimumVariable,, + devices, + model, + ) + =# return end diff --git a/src/devices_models/devices/common/add_to_expression.jl b/src/devices_models/devices/common/add_to_expression.jl index 9c053de1e..b0a74234d 100644 --- a/src/devices_models/devices/common/add_to_expression.jl +++ b/src/devices_models/devices/common/add_to_expression.jl @@ -41,7 +41,9 @@ function add_expressions!( PSY.get_name(d) for d in devices if PSY.get_variable(PSY.get_operation_cost(d)) isa PSY.FuelCurve ] - add_expression_container!(container, T(), D, names, time_steps) + if !isempty(names) + add_expression_container!(container, T(), D, names, time_steps) + end return end @@ -1562,9 +1564,86 @@ function add_to_expression!( T <: FuelConsumptionExpression, U <: ActivePowerVariable, V <: PSY.ThermalGen, - W <: AbstractThermalDispatchFormulation, + W <: AbstractDeviceFormulation, +} + variable = get_variable(container, U(), V) + time_steps = get_time_steps(container) + base_power = get_base_power(container) + resolution = get_resolution(container) + dt = Dates.value(resolution) / MILLISECONDS_IN_HOUR + for d in devices + var_cost = PSY.get_variable(PSY.get_operation_cost(d)) + if !(var_cost isa PSY.FuelCurve) + continue + end + expression = get_expression(container, T(), V) + name = PSY.get_name(d) + device_base_power = PSY.get_base_power(d) + value_curve = PSY.get_value_curve(var_cost) + if value_curve isa PSY.LinearCurve + power_units = PSY.get_power_units(var_cost) + proportional_term = PSY.get_proportional_term(value_curve) + prop_term_per_unit = get_proportional_cost_per_system_unit( + proportional_term, + power_units, + base_power, + device_base_power, + ) + for t in time_steps + fuel_expr = variable[name, t] * prop_term_per_unit * dt + JuMP.add_to_expression!( + expression[name, t], + fuel_expr, + ) + end + elseif value_curve isa PSY.QuadraticCurve + power_units = PSY.get_power_units(var_cost) + proportional_term = PSY.get_proportional_term(value_curve) + quadratic_term = PSY.get_quadratic_term(value_curve) + prop_term_per_unit = get_proportional_cost_per_system_unit( + proportional_term, + power_units, + base_power, + device_base_power, + ) + quad_term_per_unit = get_quadratic_cost_per_system_unit( + quadratic_term, + power_units, + base_power, + device_base_power, + ) + # TODO: Fix this FuelConsumptionExpression AffExpr to QuadExpr + #= + for t in time_steps + fuel_expr = + ( + variable[name, t] .^ 2 * quad_term_per_unit + + variable[name, t] * prop_term_per_unit + ) * dt + JuMP.add_to_expression!( + expression[name, t], + fuel_expr, + ) + end + =# + end + end +end + +#TODO: FuelConsumption for PowerAboveMinimumVariable +#= +function add_to_expression!( + container::OptimizationContainer, + ::Type{T}, + ::Type{U}, + devices::IS.FlattenIteratorWrapper{V}, + model::DeviceModel{V, W}, +) where { + T <: FuelConsumptionExpression, + U <: PowerAboveMinimumVariable, + V <: PSY.ThermalGen, + W <: AbstractDeviceFormulation, } - expression = get_expression(container, T(), V) variable = get_variable(container, U(), V) time_steps = get_time_steps(container) base_power = get_base_power(container) @@ -1575,6 +1654,7 @@ function add_to_expression!( if !(var_cost isa PSY.FuelCurve) continue end + expression = get_expression(container, T(), V) name = PSY.get_name(d) device_base_power = PSY.get_base_power(d) value_curve = PSY.get_value_curve(var_cost) @@ -1610,6 +1690,8 @@ function add_to_expression!( base_power, device_base_power, ) + # TODO: Fix this FuelConsumptionExpression AffExpr to QuadExpr + #= for t in time_steps fuel_expr = ( @@ -1621,9 +1703,11 @@ function add_to_expression!( fuel_expr, ) end + =# end end end +=# #= function add_to_expression!( From 33017ab59002dc0a9c65ffae8efc59f0bd9af985 Mon Sep 17 00:00:00 2001 From: rodrigomha Date: Thu, 17 Oct 2024 14:52:56 -0700 Subject: [PATCH 29/33] add fuel cost test --- ..._device_thermal_generation_constructors.jl | 60 +++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/test/test_device_thermal_generation_constructors.jl b/test/test_device_thermal_generation_constructors.jl index 9197bcd5e..45809467d 100644 --- a/test/test_device_thermal_generation_constructors.jl +++ b/test/test_device_thermal_generation_constructors.jl @@ -967,3 +967,63 @@ end end psi_checkobjfun_test(model, GAEVF) end + +@testset "Thermal with fuel cost time series" begin + sys = PSB.build_system(PSITestSystems, "c_sys5_re_fuel_cost") + + template = ProblemTemplate( + NetworkModel( + CopperPlatePowerModel; + duals = [CopperPlateBalanceConstraint], + ), + ) + + set_device_model!(template, ThermalStandard, ThermalDispatchNoMin) + set_device_model!(template, PowerLoad, StaticPowerLoad) + set_device_model!(template, RenewableDispatch, RenewableFullDispatch) + + model = DecisionModel( + template, + sys; + name = "UC", + optimizer = HiGHS_optimizer, + system_to_file = false, + store_variable_names = true, + optimizer_solve_log_print = false, + ) + models = SimulationModels(; + decision_models = [ + model, + ], + ) + sequence = SimulationSequence(; + models = models, + feedforwards = Dict( + ), + ini_cond_chronology = InterProblemChronology(), + ) + + sim = Simulation(; + name = "compact_sim", + steps = 2, + models = models, + sequence = sequence, + initial_time = DateTime("2024-01-01T00:00:00"), + simulation_folder = mktempdir(), + ) + + build!(sim; console_level = Logging.Error, serialize = false) + moi_tests(model, 432, 0, 192, 120, 72, false) + execute!(sim; enable_progress_bar = true) + + sim_res = SimulationResults(sim) + res_uc = get_decision_problem_results(sim_res, "UC") + th_uc = read_realized_variable(res_uc, "ActivePowerVariable__ThermalStandard") + p_brighton = th_uc[!, "Brighton"] + p_solitude = th_uc[!, "Solitude"] + + @test sum(p_brighton[1:24]) < 50.0 # Barely used when expensive + @test sum(p_brighton[25:48]) > 5000.0 # Used a lot when cheap + @test sum(p_solitude[1:24]) > 5000.0 # Used a lot when cheap + @test sum(p_solitude[25:48]) < 50.0 # Barely used when expensive +end From 1108b6b80fbbc22cebbef70750f84e407b205058 Mon Sep 17 00:00:00 2001 From: rodrigomha Date: Tue, 5 Nov 2024 15:52:37 -0800 Subject: [PATCH 30/33] move cost term around to update objective function with variable fuel cost --- .../common/objective_function/common.jl | 4 ++-- .../devices/thermal_generation.jl | 24 +++++++++++-------- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/src/devices_models/devices/common/objective_function/common.jl b/src/devices_models/devices/common/objective_function/common.jl index 84297bca4..b3318a5e9 100644 --- a/src/devices_models/devices/common/objective_function/common.jl +++ b/src/devices_models/devices/common/objective_function/common.jl @@ -107,9 +107,9 @@ function add_proportional_cost!( multiplier = objective_function_multiplier(U(), V()) for d in devices op_cost_data = PSY.get_operation_cost(d) - cost_term = proportional_cost(op_cost_data, U(), d, V()) - iszero(cost_term) && continue for t in get_time_steps(container) + cost_term = proportional_cost(container, op_cost_data, U(), d, V(), t) + iszero(cost_term) && continue if !PSY.get_must_run(d) exp = _add_proportional_term!(container, U(), d, cost_term * multiplier, t) add_to_expression!(container, ProductionCostExpression, exp, d, t) diff --git a/src/devices_models/devices/thermal_generation.jl b/src/devices_models/devices/thermal_generation.jl index ac6d3e3e3..ae11c88a9 100644 --- a/src/devices_models/devices/thermal_generation.jl +++ b/src/devices_models/devices/thermal_generation.jl @@ -77,8 +77,8 @@ initial_condition_variable(::InitialTimeDurationOff, d::PSY.ThermalGen, ::Abstra ########################Objective Function################################################## # TODO: Decide what is the cost for OnVariable, if fixed or constant term in variable -function proportional_cost(cost::PSY.ThermalGenerationCost, S::OnVariable, T::PSY.ThermalGen, U::AbstractThermalFormulation) - return onvar_cost(cost, S, T, U) + PSY.get_constant_term(PSY.get_vom_cost(PSY.get_variable(cost))) + PSY.get_fixed(cost) +function proportional_cost(container::OptimizationContainer, cost::PSY.ThermalGenerationCost, S::OnVariable, T::PSY.ThermalGen, U::AbstractThermalFormulation, t::Int) + return onvar_cost(container, cost, S, T, U, t) + PSY.get_constant_term(PSY.get_vom_cost(PSY.get_variable(cost))) + PSY.get_fixed(cost) end proportional_cost(cost::PSY.MarketBidCost, ::OnVariable, ::PSY.ThermalGen, ::AbstractThermalFormulation) = PSY.get_no_load_cost(cost) @@ -113,8 +113,8 @@ variable_cost(cost::PSY.OperationalCost, ::PowerAboveMinimumVariable, ::PSY.Ther """ Theoretical Cost at power output zero. Mathematically is the intercept with the y-axis """ -function onvar_cost(cost::PSY.ThermalGenerationCost, S::OnVariable, d::PSY.ThermalGen, U::AbstractThermalFormulation) - return _onvar_cost(PSY.get_variable(cost), d) +function onvar_cost(container::OptimizationContainer, cost::PSY.ThermalGenerationCost, S::OnVariable, d::PSY.ThermalGen, U::AbstractThermalFormulation, t::Int) + return _onvar_cost(container, PSY.get_variable(cost), d, t) end function _onvar_cost(cost_function::PSY.CostCurve{PSY.PiecewisePointCurve}, d::PSY.ThermalGen) @@ -122,7 +122,7 @@ function _onvar_cost(cost_function::PSY.CostCurve{PSY.PiecewisePointCurve}, d::P return 0.0 end -function _onvar_cost(cost_function::Union{PSY.CostCurve{PSY.LinearCurve}, PSY.CostCurve{PSY.QuadraticCurve}}, d::PSY.ThermalGen) +function _onvar_cost(::OptimizationContainer, cost_function::Union{PSY.CostCurve{PSY.LinearCurve}, PSY.CostCurve{PSY.QuadraticCurve}}, d::PSY.ThermalGen, ::Int) value_curve = PSY.get_value_curve(cost_function) cost_component = PSY.get_function_data(value_curve) # Always in \$/h @@ -130,22 +130,22 @@ function _onvar_cost(cost_function::Union{PSY.CostCurve{PSY.LinearCurve}, PSY.Co return constant_term end -function _onvar_cost(cost_function::PSY.CostCurve{PSY.PiecewiseIncrementalCurve}, d::PSY.ThermalGen) +function _onvar_cost(::OptimizationContainer, cost_function::PSY.CostCurve{PSY.PiecewiseIncrementalCurve}, d::PSY.ThermalGen, ::Int) # Input at min is used to transform to InputOutputCurve return 0.0 end -function _onvar_cost(cost_function::PSY.FuelCurve{PSY.PiecewisePointCurve}, d::PSY.ThermalGen) +function _onvar_cost(::OptimizationContainer, cost_function::PSY.FuelCurve{PSY.PiecewisePointCurve}, d::PSY.ThermalGen, ::Int) # OnVariableCost is included in the Point itself for PiecewisePointCurve return 0.0 end -function _onvar_cost(cost_function::PSY.FuelCurve{PSY.PiecewiseIncrementalCurve}, d::PSY.ThermalGen) +function _onvar_cost(::OptimizationContainer, cost_function::PSY.FuelCurve{PSY.PiecewiseIncrementalCurve}, d::PSY.ThermalGen, ::Int) # Input at min is used to transform to InputOutputCurve return 0.0 end -function _onvar_cost(cost_function::Union{PSY.FuelCurve{PSY.LinearCurve}, PSY.FuelCurve{PSY.QuadraticCurve}}, d::PSY.ThermalGen) +function _onvar_cost(container::OptimizationContainer, cost_function::Union{PSY.FuelCurve{PSY.LinearCurve}, PSY.FuelCurve{PSY.QuadraticCurve}}, d::T, t::Int) where {T <: PSY.ThermalGen} value_curve = PSY.get_value_curve(cost_function) cost_component = PSY.get_function_data(value_curve) # In Unit/h (unit typically in ) @@ -154,7 +154,11 @@ function _onvar_cost(cost_function::Union{PSY.FuelCurve{PSY.LinearCurve}, PSY.Fu if typeof(fuel_cost) <: Float64 return constant_term * fuel_cost else - error("Time series not implemented yet") + parameter_array = get_parameter_array(container, FuelCostParameter(), T) + parameter_multiplier = + get_parameter_multiplier_array(container, FuelCostParameter(), T) + name = PSY.get_name(d) + return constant_term * parameter_array[name, t] * parameter_multiplier[name, t] end end From e7ab9a49dfc484fcc318901475fcbd34c2b3467d Mon Sep 17 00:00:00 2001 From: rodrigomha Date: Tue, 5 Nov 2024 15:52:51 -0800 Subject: [PATCH 31/33] update add to expression for fuel consumption using compact model --- .../thermalgeneration_constructor.jl | 74 +++++++++++-------- .../devices/common/add_to_expression.jl | 50 +++++-------- 2 files changed, 59 insertions(+), 65 deletions(-) diff --git a/src/devices_models/device_constructors/thermalgeneration_constructor.jl b/src/devices_models/device_constructors/thermalgeneration_constructor.jl index 37a5332e2..eb6f041c3 100644 --- a/src/devices_models/device_constructors/thermalgeneration_constructor.jl +++ b/src/devices_models/device_constructors/thermalgeneration_constructor.jl @@ -958,6 +958,9 @@ function construct_device!( if haskey(get_time_series_names(model), ActivePowerTimeSeriesParameter) add_parameters!(container, ActivePowerTimeSeriesParameter, devices, model) end + if haskey(get_time_series_names(model), FuelCostParameter) + add_parameters!(container, FuelCostParameter, devices, model) + end add_to_expression!( container, @@ -1004,16 +1007,14 @@ function construct_device!( model, network_model, ) - #TODO: Implement for AbovePowerMinimum - #= + add_to_expression!( container, FuelConsumptionExpression, - PowerAboveMinimumVariable,, + PowerAboveMinimumVariable, devices, model, ) - =# add_feedforward_arguments!(container, model, devices) return @@ -1119,6 +1120,9 @@ function construct_device!( if haskey(get_time_series_names(model), ActivePowerTimeSeriesParameter) add_parameters!(container, ActivePowerTimeSeriesParameter, devices, model) end + if haskey(get_time_series_names(model), FuelCostParameter) + add_parameters!(container, FuelCostParameter, devices, model) + end add_to_expression!( container, @@ -1157,16 +1161,14 @@ function construct_device!( model, network_model, ) - #TODO: Implement for AbovePowerMinimum - #= + add_to_expression!( container, FuelConsumptionExpression, - PowerAboveMinimumVariable,, + PowerAboveMinimumVariable, devices, model, ) - =# add_feedforward_arguments!(container, model, devices) return @@ -1271,6 +1273,9 @@ function construct_device!( if haskey(get_time_series_names(model), ActivePowerTimeSeriesParameter) add_parameters!(container, ActivePowerTimeSeriesParameter, devices, model) end + if haskey(get_time_series_names(model), FuelCostParameter) + add_parameters!(container, FuelCostParameter, devices, model) + end add_to_expression!( container, @@ -1308,16 +1313,14 @@ function construct_device!( model, network_model, ) - #TODO: Implement for AbovePowerMinimum - #= + add_to_expression!( container, FuelConsumptionExpression, - PowerAboveMinimumVariable,, + PowerAboveMinimumVariable, devices, model, ) - =# add_feedforward_arguments!(container, model, devices) return @@ -1406,6 +1409,9 @@ function construct_device!( if haskey(get_time_series_names(model), ActivePowerTimeSeriesParameter) add_parameters!(container, ActivePowerTimeSeriesParameter, devices, model) end + if haskey(get_time_series_names(model), FuelCostParameter) + add_parameters!(container, FuelCostParameter, devices, model) + end add_to_expression!( container, @@ -1443,16 +1449,14 @@ function construct_device!( model, network_model, ) - #TODO: Implement for AbovePowerMinimum - #= + add_to_expression!( container, FuelConsumptionExpression, - PowerAboveMinimumVariable,, + PowerAboveMinimumVariable, devices, model, ) - =# add_feedforward_arguments!(container, model, devices) return end @@ -1536,6 +1540,9 @@ function construct_device!( if haskey(get_time_series_names(model), ActivePowerTimeSeriesParameter) add_parameters!(container, ActivePowerTimeSeriesParameter, devices, model) end + if haskey(get_time_series_names(model), FuelCostParameter) + add_parameters!(container, FuelCostParameter, devices, model) + end add_to_expression!( container, @@ -1573,16 +1580,14 @@ function construct_device!( model, network_model, ) - #TODO: Implement for AbovePowerMinimum - #= + add_to_expression!( container, FuelConsumptionExpression, - PowerAboveMinimumVariable,, + PowerAboveMinimumVariable, devices, model, ) - =# add_feedforward_arguments!(container, model, devices) return @@ -1667,6 +1672,9 @@ function construct_device!( if haskey(get_time_series_names(model), ActivePowerTimeSeriesParameter) add_parameters!(container, ActivePowerTimeSeriesParameter, devices, model) end + if haskey(get_time_series_names(model), FuelCostParameter) + add_parameters!(container, FuelCostParameter, devices, model) + end add_to_expression!( container, @@ -1704,16 +1712,15 @@ function construct_device!( model, network_model, ) - #TODO: Implement for AbovePowerMinimum - #= + add_to_expression!( container, FuelConsumptionExpression, - PowerAboveMinimumVariable,, + PowerAboveMinimumVariable, devices, model, ) - =# + add_feedforward_arguments!(container, model, devices) return end @@ -1778,6 +1785,9 @@ function construct_device!( add_variables!(container, PowerOutput, devices, ThermalCompactDispatch()) add_parameters!(container, OnStatusParameter, devices, model) + if haskey(get_time_series_names(model), FuelCostParameter) + add_parameters!(container, FuelCostParameter, devices, model) + end add_feedforward_arguments!(container, model, devices) @@ -1827,16 +1837,15 @@ function construct_device!( model, network_model, ) - #TODO: Implement for AbovePowerMinimum - #= + add_to_expression!( container, FuelConsumptionExpression, - PowerAboveMinimumVariable,, + PowerAboveMinimumVariable, devices, model, ) - =# + return end @@ -1897,6 +1906,9 @@ function construct_device!( add_variables!(container, PowerOutput, devices, ThermalCompactDispatch()) add_parameters!(container, OnStatusParameter, devices, model) + if haskey(get_time_series_names(model), FuelCostParameter) + add_parameters!(container, FuelCostParameter, devices, model) + end add_feedforward_arguments!(container, model, devices) @@ -1939,16 +1951,14 @@ function construct_device!( model, network_model, ) - #TODO: Implement for AbovePowerMinimum - #= + add_to_expression!( container, FuelConsumptionExpression, - PowerAboveMinimumVariable,, + PowerAboveMinimumVariable, devices, model, ) - =# return end diff --git a/src/devices_models/devices/common/add_to_expression.jl b/src/devices_models/devices/common/add_to_expression.jl index b0a74234d..1df33b5a2 100644 --- a/src/devices_models/devices/common/add_to_expression.jl +++ b/src/devices_models/devices/common/add_to_expression.jl @@ -1630,8 +1630,6 @@ function add_to_expression!( end end -#TODO: FuelConsumption for PowerAboveMinimumVariable -#= function add_to_expression!( container::OptimizationContainer, ::Type{T}, @@ -1658,6 +1656,7 @@ function add_to_expression!( name = PSY.get_name(d) device_base_power = PSY.get_base_power(d) value_curve = PSY.get_value_curve(var_cost) + P_min = PSY.get_active_power_limits(d).min if value_curve isa PSY.LinearCurve power_units = PSY.get_power_units(var_cost) proportional_term = PSY.get_proportional_term(value_curve) @@ -1668,46 +1667,31 @@ function add_to_expression!( device_base_power, ) for t in time_steps - fuel_expr = variable[name, t] * prop_term_per_unit * dt - JuMP.add_to_expression!( - expression[name, t], - fuel_expr, - ) - end - elseif value_curve isa PSY.QuadraticCurve - power_units = PSY.get_power_units(var_cost) - proportional_term = PSY.get_proportional_term(value_curve) - quadratic_term = PSY.get_quadratic_term(value_curve) - prop_term_per_unit = get_proportional_cost_per_system_unit( - proportional_term, - power_units, - base_power, - device_base_power, - ) - quad_term_per_unit = get_quadratic_cost_per_system_unit( - quadratic_term, - power_units, - base_power, - device_base_power, - ) - # TODO: Fix this FuelConsumptionExpression AffExpr to QuadExpr - #= - for t in time_steps + sos_status = _get_sos_value(container, W, d) + if sos_status == SOSStatusVariable.NO_VARIABLE + bin = 1.0 + elseif sos_status == SOSStatusVariable.PARAMETER + param = get_default_on_parameter(d) + bin = get_parameter(container, param, V).parameter_array[name, t] + elseif sos_status == SOSStatusVariable.VARIABLE + var = get_default_on_variable(d) + bin = get_variable(container, var, V)[name, t] + else + @assert false + end fuel_expr = - ( - variable[name, t] .^ 2 * quad_term_per_unit + - variable[name, t] * prop_term_per_unit - ) * dt + variable[name, t] * prop_term_per_unit * dt + + P_min * bin * prop_term_per_unit * dt JuMP.add_to_expression!( expression[name, t], fuel_expr, ) end - =# + elseif value_curve isa PSY.QuadraticCurve + error("Quadratic Curves are not accepted with Compact Formulation: $W") end end end -=# #= function add_to_expression!( From a6aae9dbba96d0dd7bddda993bb7e0b8379b49a9 Mon Sep 17 00:00:00 2001 From: rodrigomha Date: Tue, 5 Nov 2024 15:59:34 -0800 Subject: [PATCH 32/33] update function call for onvar cost --- src/devices_models/devices/thermal_generation.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/devices_models/devices/thermal_generation.jl b/src/devices_models/devices/thermal_generation.jl index ae11c88a9..506282e89 100644 --- a/src/devices_models/devices/thermal_generation.jl +++ b/src/devices_models/devices/thermal_generation.jl @@ -117,7 +117,7 @@ function onvar_cost(container::OptimizationContainer, cost::PSY.ThermalGeneratio return _onvar_cost(container, PSY.get_variable(cost), d, t) end -function _onvar_cost(cost_function::PSY.CostCurve{PSY.PiecewisePointCurve}, d::PSY.ThermalGen) +function _onvar_cost(::OptimizationContainer, cost_function::PSY.CostCurve{PSY.PiecewisePointCurve}, d::PSY.ThermalGen, ::Int) # OnVariableCost is included in the Point itself for PiecewisePointCurve return 0.0 end From 8cf452da75700631c802e5068050edffa9b19f67 Mon Sep 17 00:00:00 2001 From: rodrigomha Date: Tue, 5 Nov 2024 16:33:49 -0800 Subject: [PATCH 33/33] add test quad + pwl --- .../devices/common/add_to_expression.jl | 1 + ..._device_thermal_generation_constructors.jl | 78 +++++++++++++++++++ 2 files changed, 79 insertions(+) diff --git a/src/devices_models/devices/common/add_to_expression.jl b/src/devices_models/devices/common/add_to_expression.jl index 1df33b5a2..d14956b26 100644 --- a/src/devices_models/devices/common/add_to_expression.jl +++ b/src/devices_models/devices/common/add_to_expression.jl @@ -1612,6 +1612,7 @@ function add_to_expression!( base_power, device_base_power, ) + error("TODO: Implement FuelConsumptionExpression AffExpr to QuadExpr") # TODO: Fix this FuelConsumptionExpression AffExpr to QuadExpr #= for t in time_steps diff --git a/test/test_device_thermal_generation_constructors.jl b/test/test_device_thermal_generation_constructors.jl index 45809467d..c1e36abe9 100644 --- a/test/test_device_thermal_generation_constructors.jl +++ b/test/test_device_thermal_generation_constructors.jl @@ -1027,3 +1027,81 @@ end @test sum(p_solitude[1:24]) > 5000.0 # Used a lot when cheap @test sum(p_solitude[25:48]) < 50.0 # Barely used when expensive end + +@testset "Thermal with fuel cost time series with Quadratic and PWL" begin + sys = PSB.build_system(PSITestSystems, "c_sys5_re_fuel_cost") + + template = ProblemTemplate( + NetworkModel( + CopperPlatePowerModel; + duals = [CopperPlateBalanceConstraint], + ), + ) + + solitude = get_component(ThermalStandard, sys, "Solitude") + op_cost = get_operation_cost(solitude) + ts = deepcopy(get_time_series(Deterministic, solitude, "fuel_cost")) + remove_time_series!(sys, Deterministic, solitude, "fuel_cost") + quad_curve = QuadraticCurve(0.05, 1.0, 0.0) + new_th_cost = ThermalGenerationCost(; + variable = FuelCurve(; + value_curve = quad_curve, + fuel_cost = 1.0, + ), + fixed = op_cost.fixed, + start_up = op_cost.start_up, + shut_down = op_cost.shut_down, + ) + + set_operation_cost!(solitude, new_th_cost) + add_time_series!( + sys, + solitude, + ts, + ) + + set_device_model!(template, ThermalStandard, ThermalBasicUnitCommitment) + set_device_model!(template, PowerLoad, StaticPowerLoad) + set_device_model!(template, RenewableDispatch, RenewableFullDispatch) + + model = DecisionModel( + template, + sys; + name = "UC", + optimizer = HiGHS_optimizer, + system_to_file = false, + store_variable_names = true, + optimizer_solve_log_print = false, + ) + models = SimulationModels(; + decision_models = [ + model, + ], + ) + sequence = SimulationSequence(; + models = models, + feedforwards = Dict( + ), + ini_cond_chronology = InterProblemChronology(), + ) + + sim = Simulation(; + name = "compact_sim", + steps = 2, + models = models, + sequence = sequence, + initial_time = DateTime("2024-01-01T00:00:00"), + simulation_folder = mktempdir(), + ) + + build!(sim; console_level = Logging.Error, serialize = false) + # TODO Tests + moi_tests(model, 1, 2, 3, 4, 5, false) + execute!(sim; enable_progress_bar = true) + + sim_res = SimulationResults(sim) + res_uc = get_decision_problem_results(sim_res, "UC") + th_uc = read_realized_variable(res_uc, "ActivePowerVariable__ThermalStandard") + p_brighton = th_uc[!, "Brighton"] + p_solitude = th_uc[!, "Solitude"] +end