From ad72ddf3e71396db1c2118593b56c960cbb5d489 Mon Sep 17 00:00:00 2001 From: rodrigomha Date: Thu, 27 Jun 2024 17:03:52 -0700 Subject: [PATCH 1/9] add parameters structs --- src/core/parameters.jl | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/core/parameters.jl b/src/core/parameters.jl index 45f719d77c..190aea718d 100644 --- a/src/core/parameters.jl +++ b/src/core/parameters.jl @@ -243,10 +243,20 @@ Parameter to define reactive power time series struct ReactivePowerTimeSeriesParameter <: TimeSeriesParameter end """ -Paramter to define requirement time series +Parameter to define requirement time series """ struct RequirementTimeSeriesParameter <: TimeSeriesParameter end +""" +Parameter to define Flow From_To limit time series +""" +struct FromToFlowLimitParameter <: TimeSeriesParameter end + +""" +Parameter to define Flow To_From limit time series +""" +struct ToFromFlowLimitParameter <: TimeSeriesParameter end + abstract type VariableValueParameter <: RightHandSideParameter end """ From 339114bf259dbbe8ce5fcd98c26fb80c68790348 Mon Sep 17 00:00:00 2001 From: rodrigomha Date: Thu, 27 Jun 2024 17:04:34 -0700 Subject: [PATCH 2/9] add timeseries constraint --- .../device_constructors/branch_constructor.jl | 8 +++ .../devices/area_interchange.jl | 72 +++++++++++++++---- 2 files changed, 65 insertions(+), 15 deletions(-) diff --git a/src/devices_models/device_constructors/branch_constructor.jl b/src/devices_models/device_constructors/branch_constructor.jl index 9f7553b961..b1cbe2c0d2 100644 --- a/src/devices_models/device_constructors/branch_constructor.jl +++ b/src/devices_models/device_constructors/branch_constructor.jl @@ -900,6 +900,10 @@ function construct_device!( ) end devices = get_available_components(model, sys) + has_ts = PSY.has_time_series.(devices) + if any(has_ts) && !all(has_ts) + error("Not all AreaInterchange devices have time series. Check data to complete (or remove) time series.") + end add_variables!( container, FlowActivePowerVariable, @@ -915,6 +919,10 @@ function construct_device!( model, network_model, ) + if all(has_ts) + add_parameters!(container, FromToFlowLimitParameter, devices, model) + add_parameters!(container, ToFromFlowLimitParameter, devices, model) + end return end diff --git a/src/devices_models/devices/area_interchange.jl b/src/devices_models/devices/area_interchange.jl index 8df1dbacf4..4233f9c7c0 100644 --- a/src/devices_models/devices/area_interchange.jl +++ b/src/devices_models/devices/area_interchange.jl @@ -1,8 +1,16 @@ +#! format: off +get_multiplier_value(::FromToFlowLimitParameter, d::PSY.AreaInterchange, ::AbstractBranchFormulation) = -1.0 * PSY.get_from_to_flow_limit(d) +get_multiplier_value(::ToFromFlowLimitParameter, d::PSY.AreaInterchange, ::AbstractBranchFormulation) = PSY.get_to_from_flow_limit(d) +#! format: on + function get_default_time_series_names( ::Type{PSY.AreaInterchange}, ::Type{V}, ) where {V <: AbstractBranchFormulation} - return Dict{Type{<:TimeSeriesParameter}, String}() + return Dict{Type{<:TimeSeriesParameter}, String}( + FromToFlowLimitParameter => "from_to_flow_limit", + ToFromFlowLimitParameter => "to_from_flow_limit", + ) end function get_default_attributes( @@ -71,22 +79,56 @@ function add_constraints!( ) var_array = get_variable(container, FlowActivePowerVariable(), PSY.AreaInterchange) - - for device in devices - ci_name = PSY.get_name(device) - to_from_limit = PSY.get_flow_limits(device).to_from - from_to_limit = PSY.get_flow_limits(device).from_to - for t in time_steps - con_lb[ci_name, t] = - JuMP.@constraint( - get_jump_model(container), - var_array[ci_name, t] >= -1.0 * from_to_limit + if !all(PSY.has_time_series.(devices)) + for device in devices + ci_name = PSY.get_name(device) + to_from_limit = PSY.get_flow_limits(device).to_from + from_to_limit = PSY.get_flow_limits(device).from_to + for t in time_steps + con_lb[ci_name, t] = + JuMP.@constraint( + get_jump_model(container), + var_array[ci_name, t] >= -1.0 * from_to_limit + ) + con_ub[ci_name, t] = + JuMP.@constraint( + get_jump_model(container), + var_array[ci_name, t] <= to_from_limit + ) + end + end + else + param_container_from_to = + get_parameter(container, FromToFlowLimitParameter(), PSY.AreaInterchange) + param_multiplier_from_to = get_parameter_multiplier_array( + container, + FromToFlowLimitParameter(), + PSY.AreaInterchange, + ) + param_container_to_from = + get_parameter(container, ToFromFlowLimitParameter(), PSY.AreaInterchange) + param_multiplier_to_from = get_parameter_multiplier_array( + container, + ToFromFlowLimitParameter(), + PSY.AreaInterchange, + ) + jump_model = get_jump_model(container) + for device in devices + name = PSY.get_name(device) + param_from_to = get_parameter_column_refs(param_container_from_to, name) + param_to_from = get_parameter_column_refs(param_container_to_from, name) + for t in time_steps + con_lb[name, t] = JuMP.@constraint( + jump_model, + var_array[name, t] >= + param_multiplier_from_to[name, t] * param_from_to[t] ) - con_ub[ci_name, t] = - JuMP.@constraint( - get_jump_model(container), - var_array[ci_name, t] <= to_from_limit + con_ub[name, t] = JuMP.@constraint( + jump_model, + var_array[name, t] <= + param_multiplier_to_from[name, t] * param_to_from[t] ) + end end end return From b5a1f827e4f4fab3cac8f7cd7d9afdafdbedc85e Mon Sep 17 00:00:00 2001 From: rodrigomha Date: Fri, 28 Jun 2024 10:11:52 -0700 Subject: [PATCH 3/9] add check if less than two timeseries are available --- .../device_constructors/branch_constructor.jl | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/devices_models/device_constructors/branch_constructor.jl b/src/devices_models/device_constructors/branch_constructor.jl index b1cbe2c0d2..baa4768200 100644 --- a/src/devices_models/device_constructors/branch_constructor.jl +++ b/src/devices_models/device_constructors/branch_constructor.jl @@ -902,7 +902,9 @@ function construct_device!( devices = get_available_components(model, sys) has_ts = PSY.has_time_series.(devices) if any(has_ts) && !all(has_ts) - error("Not all AreaInterchange devices have time series. Check data to complete (or remove) time series.") + error( + "Not all AreaInterchange devices have time series. Check data to complete (or remove) time series.", + ) end add_variables!( container, @@ -920,6 +922,15 @@ function construct_device!( network_model, ) if all(has_ts) + for device in devices + name = PSY.get_name(device) + num_ts = length(unique(PSY.get_name.(PSY.get_time_series_keys(device)))) + if num_ts < 2 + error( + "AreaInterchange $name has less than two time series. It is required to add both from_to and to_from time series.", + ) + end + end add_parameters!(container, FromToFlowLimitParameter, devices, model) add_parameters!(container, ToFromFlowLimitParameter, devices, model) end From aeba709df0dbd2ee9387d7a4d27a63aed1cdbdbc Mon Sep 17 00:00:00 2001 From: rodrigomha Date: Fri, 28 Jun 2024 15:12:03 -0700 Subject: [PATCH 4/9] add struct for new service --- src/PowerSimulations.jl | 1 + src/core/formulations.jl | 4 ++++ src/core/parameters.jl | 10 ++++++++++ 3 files changed, 15 insertions(+) diff --git a/src/PowerSimulations.jl b/src/PowerSimulations.jl index f3ba8aad75..36af71cee2 100644 --- a/src/PowerSimulations.jl +++ b/src/PowerSimulations.jl @@ -35,6 +35,7 @@ export NonSpinningReserve export PIDSmoothACE export GroupReserve export ConstantMaxInterfaceFlow +export VariableMaxInterfaceFlow ######## Branch Models ######## export StaticBranch diff --git a/src/core/formulations.jl b/src/core/formulations.jl index 8e82f5e790..1b6ddb109a 100644 --- a/src/core/formulations.jl +++ b/src/core/formulations.jl @@ -258,3 +258,7 @@ struct NonSpinningReserve <: AbstractReservesFormulation end Struct to add a constant maximum transmission flow for specified interface """ struct ConstantMaxInterfaceFlow <: AbstractServiceFormulation end +""" +Struct to add a variable maximum transmission flow for specified interface +""" +struct VariableMaxInterfaceFlow <: AbstractServiceFormulation end diff --git a/src/core/parameters.jl b/src/core/parameters.jl index 190aea718d..7e2e60afe2 100644 --- a/src/core/parameters.jl +++ b/src/core/parameters.jl @@ -257,6 +257,16 @@ Parameter to define Flow To_From limit time series """ struct ToFromFlowLimitParameter <: TimeSeriesParameter end +""" +Parameter to define Max Flow limit for interface time series +""" +struct MaxInterfaceFlowLimitParameter <: TimeSeriesParameter end + +""" +Parameter to define Min Flow limit for interface time series +""" +struct MinInterfaceFlowLimitParameter <: TimeSeriesParameter end + abstract type VariableValueParameter <: RightHandSideParameter end """ From 27926a526d63241d47fc238cee9aa4d821bf0d39 Mon Sep 17 00:00:00 2001 From: rodrigomha Date: Fri, 28 Jun 2024 15:18:01 -0700 Subject: [PATCH 5/9] add constructor for variablemaxinterface --- .../devices/common/add_to_expression.jl | 4 +- src/operation/problem_template.jl | 8 ++ src/parameters/add_parameters.jl | 4 +- src/services_models/services_constructor.jl | 91 +++++++++++++++++++ src/services_models/transmission_interface.jl | 72 ++++++++++++++- 5 files changed, 174 insertions(+), 5 deletions(-) diff --git a/src/devices_models/devices/common/add_to_expression.jl b/src/devices_models/devices/common/add_to_expression.jl index 1e66935103..11c43e6f9e 100644 --- a/src/devices_models/devices/common/add_to_expression.jl +++ b/src/devices_models/devices/common/add_to_expression.jl @@ -1151,8 +1151,8 @@ function add_to_expression!( ::Type{InterfaceTotalFlow}, ::Type{FlowActivePowerVariable}, service::PSY.TransmissionInterface, - model::ServiceModel{PSY.TransmissionInterface, ConstantMaxInterfaceFlow}, -) + model::ServiceModel{PSY.TransmissionInterface, V}, +) where {V <: Union{ConstantMaxInterfaceFlow, VariableMaxInterfaceFlow}} expression = get_expression(container, InterfaceTotalFlow(), PSY.TransmissionInterface) service_name = get_service_name(model) for (device_type, devices) in get_contributing_devices_map(model) diff --git a/src/operation/problem_template.jl b/src/operation/problem_template.jl index c2dd160744..0b0a4b671e 100644 --- a/src/operation/problem_template.jl +++ b/src/operation/problem_template.jl @@ -264,6 +264,14 @@ function _modify_device_model!( return end +function _modify_device_model!( + ::Dict{Symbol, DeviceModel}, + ::ServiceModel{PSY.TransmissionInterface, VariableMaxInterfaceFlow}, + ::Vector, +) + return +end + function _add_services_to_device_model!(template::ProblemTemplate) service_models = get_service_models(template) devices_template = get_device_models(template) diff --git a/src/parameters/add_parameters.jl b/src/parameters/add_parameters.jl index 80e574cf09..b581893ed0 100644 --- a/src/parameters/add_parameters.jl +++ b/src/parameters/add_parameters.jl @@ -59,7 +59,7 @@ function add_parameters!( ::Type{T}, service::U, model::ServiceModel{U, V}, -) where {T <: TimeSeriesParameter, U <: PSY.Service, V <: AbstractReservesFormulation} +) where {T <: TimeSeriesParameter, U <: PSY.Service, V <: AbstractServiceFormulation} if get_rebuild_model(get_settings(container)) && has_container_key(container, T, U, PSY.get_name(service)) return @@ -258,7 +258,7 @@ function _add_parameters!( ::T, service::U, model::ServiceModel{U, V}, -) where {T <: TimeSeriesParameter, U <: PSY.Service, V <: AbstractReservesFormulation} +) where {T <: TimeSeriesParameter, U <: PSY.Service, V <: AbstractServiceFormulation} 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") diff --git a/src/services_models/services_constructor.jl b/src/services_models/services_constructor.jl index 01b8d5ccd6..09d9c9d2ec 100644 --- a/src/services_models/services_constructor.jl +++ b/src/services_models/services_constructor.jl @@ -581,3 +581,94 @@ function construct_service!( objective_function!(container, service, model) return end + +function construct_service!( + container::OptimizationContainer, + sys::PSY.System, + ::ArgumentConstructStage, + model::ServiceModel{T, VariableMaxInterfaceFlow}, + devices_template::Dict{Symbol, DeviceModel}, + incompatible_device_types::Set{<:DataType}, + network_model::NetworkModel{<:PM.AbstractPowerModel}, +) where {T <: PSY.TransmissionInterface} + interfaces = get_available_components(model, sys) + 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 + # Lazy container addition for the expressions. + lazy_container_addition!( + container, + InterfaceTotalFlow(), + T, + PSY.get_name.(interfaces), + get_time_steps(container), + ) + has_ts = PSY.has_time_series.(interfaces) + if any(has_ts) && !all(has_ts) + error( + "Not all TransmissionInterfaces devices have time series. Check data to complete (or remove) time series.", + ) + end + if all(has_ts) + for device in interfaces + name = PSY.get_name(device) + num_ts = length(unique(PSY.get_name.(PSY.get_time_series_keys(device)))) + if num_ts < 2 + error( + "TransmissionInterface $name has less than two time series. It is required to add both min_flow and max_flow time series.", + ) + end + add_parameters!(container, MinInterfaceFlowLimitParameter, device, model) + add_parameters!(container, MaxInterfaceFlowLimitParameter, device, model) + end + end + #add_feedforward_arguments!(container, model, service) + return +end + +function construct_service!( + container::OptimizationContainer, + sys::PSY.System, + ::ModelConstructStage, + model::ServiceModel{T, VariableMaxInterfaceFlow}, + devices_template::Dict{Symbol, DeviceModel}, + incompatible_device_types::Set{<:DataType}, + network_model::NetworkModel{<:PM.AbstractActivePowerModel}, +) where {T <: PSY.TransmissionInterface} + name = get_service_name(model) + service = PSY.get_component(T, sys, name) + + add_to_expression!( + container, + InterfaceTotalFlow, + FlowActivePowerVariable, + service, + model, + ) + + if get_use_slacks(model) + add_to_expression!( + container, + InterfaceTotalFlow, + InterfaceFlowSlackUp, + service, + model, + ) + add_to_expression!( + container, + InterfaceTotalFlow, + InterfaceFlowSlackDown, + service, + model, + ) + end + + add_constraints!(container, InterfaceFlowLimit, service, model) + add_feedforward_constraints!(container, model, service) + add_constraint_dual!(container, sys, model) + objective_function!(container, service, model) + return +end diff --git a/src/services_models/transmission_interface.jl b/src/services_models/transmission_interface.jl index 25c2e550eb..ce1981e143 100644 --- a/src/services_models/transmission_interface.jl +++ b/src/services_models/transmission_interface.jl @@ -6,6 +6,9 @@ get_variable_lower_bound(::InterfaceFlowSlackDown, ::PSY.TransmissionInterface, get_variable_multiplier(::InterfaceFlowSlackUp, ::PSY.TransmissionInterface, ::ConstantMaxInterfaceFlow) = 1.0 get_variable_multiplier(::InterfaceFlowSlackDown, ::PSY.TransmissionInterface, ::ConstantMaxInterfaceFlow) = -1.0 +get_multiplier_value(::MinInterfaceFlowLimitParameter, d::PSY.TransmissionInterface, ::VariableMaxInterfaceFlow) = PSY.get_min_active_power_flow_limit(d) +get_multiplier_value(::MaxInterfaceFlowLimitParameter, d::PSY.TransmissionInterface, ::VariableMaxInterfaceFlow) = PSY.get_max_active_power_flow_limit(d) + #! format: On function get_default_time_series_names( ::Type{PSY.TransmissionInterface}, @@ -14,12 +17,28 @@ function get_default_time_series_names( return Dict{Type{<:TimeSeriesParameter}, String}() end +function get_default_time_series_names( + ::Type{PSY.TransmissionInterface}, + ::Type{VariableMaxInterfaceFlow}, +) + return Dict{Type{<:TimeSeriesParameter}, String}( + MinInterfaceFlowLimitParameter => "min_active_power_flow_limit", + MaxInterfaceFlowLimitParameter => "max_active_power_flow_limit", + ) +end + function get_default_attributes( ::Type{<:PSY.TransmissionInterface}, ::Type{ConstantMaxInterfaceFlow}) return Dict{String, Any}() end +function get_default_attributes( + ::Type{<:PSY.TransmissionInterface}, + ::Type{VariableMaxInterfaceFlow}) + return Dict{String, Any}() +end + function get_initial_conditions_service_model( ::OperationModel, ::ServiceModel{T, D}, @@ -61,11 +80,62 @@ function add_constraints!(container::OptimizationContainer, return end +function add_constraints!(container::OptimizationContainer, + ::Type{InterfaceFlowLimit}, + interface::T, + model::ServiceModel{T, VariableMaxInterfaceFlow}, +) where {T <: PSY.TransmissionInterface} + expr = get_expression(container, InterfaceTotalFlow(), T) + interfaces, timesteps = axes(expr) + constraint_container_ub = lazy_container_addition!( + container, + InterfaceFlowLimit(), + T, + interfaces, + timesteps; + meta = "ub", + ) + constraint_container_lb = lazy_container_addition!( + container, + InterfaceFlowLimit(), + T, + interfaces, + timesteps; + meta = "lb", + ) + int_name = PSY.get_name(interface) + param_container_min = + get_parameter(container, MinInterfaceFlowLimitParameter(), PSY.TransmissionInterface, int_name) + @show param_multiplier_min = get_parameter_multiplier_array( + container, + MinInterfaceFlowLimitParameter(), + PSY.TransmissionInterface, + int_name, + ) + param_container_max = + get_parameter(container, MaxInterfaceFlowLimitParameter(), PSY.TransmissionInterface, int_name) + @show param_multiplier_max = get_parameter_multiplier_array( + container, + MaxInterfaceFlowLimitParameter(), + PSY.TransmissionInterface, + int_name, + ) + @show param_min = get_parameter_column_refs(param_container_min, int_name) + @show param_max = get_parameter_column_refs(param_container_max, int_name) + for t in timesteps + constraint_container_ub[int_name, t] = + JuMP.@constraint(get_jump_model(container), expr[int_name, t] <= param_multiplier_max[int_name, t] * param_max[t]) + constraint_container_lb[int_name, t] = + JuMP.@constraint(get_jump_model(container), expr[int_name, t] >= param_multiplier_min[int_name, t] * param_min[t]) + end + return +end + function objective_function!( container::OptimizationContainer, service::T, model::ServiceModel{T, U}, -) where {T <: PSY.TransmissionInterface, U <: ConstantMaxInterfaceFlow} +) where {T <: PSY.TransmissionInterface, U <: Union{ConstantMaxInterfaceFlow, VariableMaxInterfaceFlow}} # At the moment the interfaces have no costs associated with them return end From a3ec27b5000f7e821fcc6dda10df7a9b7b13809b Mon Sep 17 00:00:00 2001 From: rodrigomha Date: Fri, 28 Jun 2024 16:58:53 -0700 Subject: [PATCH 6/9] remove show --- src/services_models/transmission_interface.jl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/services_models/transmission_interface.jl b/src/services_models/transmission_interface.jl index ce1981e143..60e12cabc3 100644 --- a/src/services_models/transmission_interface.jl +++ b/src/services_models/transmission_interface.jl @@ -106,7 +106,7 @@ function add_constraints!(container::OptimizationContainer, int_name = PSY.get_name(interface) param_container_min = get_parameter(container, MinInterfaceFlowLimitParameter(), PSY.TransmissionInterface, int_name) - @show param_multiplier_min = get_parameter_multiplier_array( + param_multiplier_min = get_parameter_multiplier_array( container, MinInterfaceFlowLimitParameter(), PSY.TransmissionInterface, @@ -114,14 +114,14 @@ function add_constraints!(container::OptimizationContainer, ) param_container_max = get_parameter(container, MaxInterfaceFlowLimitParameter(), PSY.TransmissionInterface, int_name) - @show param_multiplier_max = get_parameter_multiplier_array( + param_multiplier_max = get_parameter_multiplier_array( container, MaxInterfaceFlowLimitParameter(), PSY.TransmissionInterface, int_name, ) - @show param_min = get_parameter_column_refs(param_container_min, int_name) - @show param_max = get_parameter_column_refs(param_container_max, int_name) + param_min = get_parameter_column_refs(param_container_min, int_name) + param_max = get_parameter_column_refs(param_container_max, int_name) for t in timesteps constraint_container_ub[int_name, t] = JuMP.@constraint(get_jump_model(container), expr[int_name, t] <= param_multiplier_max[int_name, t] * param_max[t]) From 976e95cc04769f9870d1539e4655faf3c7ead6c9 Mon Sep 17 00:00:00 2001 From: rodrigomha Date: Fri, 28 Jun 2024 16:59:02 -0700 Subject: [PATCH 7/9] add area interface test with TS --- test/test_network_constructors.jl | 127 ++++++++++++++++++++++++++++++ 1 file changed, 127 insertions(+) diff --git a/test/test_network_constructors.jl b/test/test_network_constructors.jl index c0dfd41a56..62be7f13f6 100644 --- a/test/test_network_constructors.jl +++ b/test/test_network_constructors.jl @@ -777,6 +777,59 @@ end ) end +@testset "2 Areas AreaBalance PowerModel with TimeSeries" begin + c_sys = PSB.build_system(PSISystems, "two_area_pjm_DA") + load = first(get_components(PowerLoad, c_sys)) + ts_array = get_time_series_array(SingleTimeSeries, load, "max_active_power") + tstamp = timestamp(ts_array) + area_int = first(get_components(AreaInterchange, c_sys)) + day_data = [ + 0.9, 0.85, 0.95, 0.2, 0.0, 0.0, + 0.9, 0.85, 0.95, 0.2, 0.0, 0.0, + 0.9, 0.85, 0.95, 0.2, 0.0, 0.0, + 0.9, 0.85, 0.95, 0.2, 0.0, 0.0, + ] + weekly_data = repeat(day_data, 7) + ts_from_to = SingleTimeSeries( + "from_to_flow_limit", + TimeArray(tstamp, weekly_data); + scaling_factor_multiplier = get_from_to_flow_limit, + ) + ts_to_from = SingleTimeSeries( + "to_from_flow_limit", + TimeArray(tstamp, weekly_data); + scaling_factor_multiplier = get_from_to_flow_limit, + ) + add_time_series!(c_sys, area_int, ts_from_to) + add_time_series!(c_sys, area_int, ts_to_from) + ## Transform Time Series ## + transform_single_time_series!(c_sys, Hour(24), Hour(24)) + template = get_thermal_dispatch_template_network(NetworkModel(AreaBalancePowerModel)) + set_device_model!(template, AreaInterchange, StaticBranch) + ps_model = + DecisionModel(template, c_sys; resolution = Hour(1), optimizer = HiGHS_optimizer) + + @test build!(ps_model; output_dir = mktempdir(; cleanup = true)) == + PSI.ModelBuildStatus.BUILT + @test solve!(ps_model) == PSI.RunStatus.SUCCESSFULLY_FINALIZED + + moi_tests(ps_model, 264, 0, 264, 264, 48, false) + + opt_container = PSI.get_optimization_container(ps_model) + copper_plate_constraints = + PSI.get_constraint(opt_container, CopperPlateBalanceConstraint(), PSY.Area) + @test size(copper_plate_constraints) == (2, 24) + + psi_checksolve_test(ps_model, [MOI.OPTIMAL], 482055, 1) + + results = OptimizationProblemResults(ps_model) + interarea_flow = read_variable(results, "FlowActivePowerVariable__AreaInterchange") + # The values for these tests come from the data + @test interarea_flow[4, "1_2"] != 0.0 + @test interarea_flow[5, "1_2"] == 0.0 + @test interarea_flow[6, "1_2"] == 0.0 +end + @testset "2 Areas AreaPTDFPowerModel" begin c_sys = PSB.build_system(PSISystems, "two_area_pjm_DA") transform_single_time_series!(c_sys, Hour(24), Hour(1)) @@ -846,3 +899,77 @@ end ), ) end + +@testset "2 Areas AreaPTDFPowerModel with Time Series" begin + c_sys = PSB.build_system(PSISystems, "two_area_pjm_DA") + load = first(get_components(PowerLoad, c_sys)) + new_line = Line(; + name = "C2_D1", + available = true, + active_power_flow = 0.0, + reactive_power_flow = 0.0, + arc = Arc(; + from = get_component(ACBus, c_sys, "Bus_nodeC_2"), + to = get_component(ACBus, c_sys, "Bus_nodeD_1"), + ), + r = 0.00297, + x = 0.0297, + b = (from = 0.00337, to = 0.00337), + rating = 40.53, + angle_limits = (min = -0.7, max = 0.7), + ) + add_component!(c_sys, new_line) + ts_array = get_time_series_array(SingleTimeSeries, load, "max_active_power") + tstamp = timestamp(ts_array) + area_int = first(get_components(AreaInterchange, c_sys)) + day_data = [ + 0.9, 0.85, 0.95, 0.2, 0.0, 0.0, + 0.9, 0.85, 0.95, 0.2, 0.0, 0.0, + 0.9, 0.85, 0.95, 0.2, 0.0, 0.0, + 0.9, 0.85, 0.95, 0.2, 0.0, 0.0, + ] + weekly_data = repeat(day_data, 7) + ts_from_to = SingleTimeSeries( + "from_to_flow_limit", + TimeArray(tstamp, weekly_data); + scaling_factor_multiplier = get_from_to_flow_limit, + ) + ts_to_from = SingleTimeSeries( + "to_from_flow_limit", + TimeArray(tstamp, weekly_data); + scaling_factor_multiplier = get_from_to_flow_limit, + ) + add_time_series!(c_sys, area_int, ts_from_to) + add_time_series!(c_sys, area_int, ts_to_from) + ## Transform Time Series ## + transform_single_time_series!(c_sys, Hour(24), Hour(24)) + set_flow_limits!( + get_component(AreaInterchange, c_sys, "1_2"), + (from_to = 1.0, to_from = 1.0), + ) + template = get_thermal_dispatch_template_network(NetworkModel(AreaPTDFPowerModel)) + set_device_model!(template, AreaInterchange, StaticBranch) + set_device_model!(template, MonitoredLine, StaticBranchUnbounded) + ps_model = + DecisionModel(template, c_sys; resolution = Hour(1), optimizer = HiGHS_optimizer) + + @test build!(ps_model; output_dir = mktempdir(; cleanup = true)) == + PSI.ModelBuildStatus.BUILT + @test solve!(ps_model) == PSI.RunStatus.SUCCESSFULLY_FINALIZED + + moi_tests(ps_model, 600, 0, 600, 600, 384, false) + + opt_container = PSI.get_optimization_container(ps_model) + copper_plate_constraints = + PSI.get_constraint(opt_container, CopperPlateBalanceConstraint(), PSY.Area) + @test size(copper_plate_constraints) == (2, 24) + + psi_checksolve_test(ps_model, [MOI.OPTIMAL], 662467, 1) + + results = OptimizationProblemResults(ps_model) + interarea_flow = read_variable(results, "FlowActivePowerVariable__AreaInterchange") + # The values for these tests come from the data + @test interarea_flow[1, "1_2"] != 0.0 + @test interarea_flow[5, "1_2"] == 0.0 + @test interarea_flow[6, "1_2"] == 0.0 +end From 7e41bbdb8336dc0670d6851b07221154f1935e49 Mon Sep 17 00:00:00 2001 From: rodrigomha Date: Mon, 1 Jul 2024 16:05:44 -0700 Subject: [PATCH 8/9] add tx interface with ts test --- test/test_services_constructor.jl | 73 +++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) diff --git a/test/test_services_constructor.jl b/test/test_services_constructor.jl index 46d5800833..4956904cff 100644 --- a/test/test_services_constructor.jl +++ b/test/test_services_constructor.jl @@ -449,3 +449,76 @@ end moi_tests(model, 312, 0, 288, 288, 168, false) =# end + +@testset "Test Transmission Interface with TimeSeries" begin + c_sys5_uc = PSB.build_system(PSITestSystems, "c_sys5_uc"; add_reserves = true) + interface = TransmissionInterface(; + name = "west_east", + available = true, + active_power_flow_limits = (min = 0.0, max = 400.0), + ) + interface_lines = [ + get_component(Line, c_sys5_uc, "1"), + get_component(Line, c_sys5_uc, "2"), + get_component(Line, c_sys5_uc, "6"), + ] + add_service!(c_sys5_uc, interface, interface_lines) + # Add TimeSeries Data + data_minflow = Dict( + DateTime("2024-01-01T00:00:00") => zeros(24), + DateTime("2024-01-02T00:00:00") => zeros(24), + ) + + forecast_minflow = Deterministic( + "min_active_power_flow_limit", + data_minflow, + Hour(1); + scaling_factor_multiplier = get_min_active_power_flow_limit, + ) + + data_maxflow = Dict( + DateTime("2024-01-01T00:00:00") => [ + 0.9, 0.85, 0.95, 0.2, 0.15, 0.2, + 0.9, 0.85, 0.95, 0.2, 0.15, 0.2, + 0.9, 0.85, 0.95, 0.2, 0.5, 0.5, + 0.9, 0.85, 0.95, 0.2, 0.6, 0.6, + ], + DateTime("2024-01-02T00:00:00") => [ + 0.9, 0.85, 0.95, 0.2, 0.15, 0.2, + 0.9, 0.85, 0.95, 0.2, 0.15, 0.2, + 0.9, 0.85, 0.95, 0.2, 0.5, 0.5, + 0.9, 0.85, 0.95, 0.2, 0.6, 0.6, + ], + ) + + forecast_maxflow = Deterministic( + "max_active_power_flow_limit", + data_maxflow, + Hour(1); + scaling_factor_multiplier = get_max_active_power_flow_limit, + ) + + add_time_series!(c_sys5_uc, interface, forecast_minflow) + add_time_series!(c_sys5_uc, interface, forecast_maxflow) + + template = get_thermal_dispatch_template_network(DCPPowerModel) + set_service_model!( + template, + ServiceModel(TransmissionInterface, ConstantMaxInterfaceFlow; use_slacks = true), + ) + + model = DecisionModel(template, c_sys5_uc) + @test build!(model; output_dir = mktempdir(; cleanup = true)) == + PSI.ModelBuildStatus.BUILT + moi_tests(model, 432, 144, 288, 288, 288, false) + + template = get_thermal_dispatch_template_network(PTDFPowerModel) + set_service_model!( + template, + ServiceModel(TransmissionInterface, ConstantMaxInterfaceFlow; use_slacks = true), + ) + model = DecisionModel(template, c_sys5_uc) + @test build!(model; output_dir = mktempdir(; cleanup = true)) == + PSI.ModelBuildStatus.BUILT + moi_tests(model, 312, 0, 288, 288, 168, false) +end From 20d9d1836376fdb411c33404a889ee64976949d9 Mon Sep 17 00:00:00 2001 From: Jose Daniel Lara Date: Tue, 2 Jul 2024 09:21:37 -0600 Subject: [PATCH 9/9] codecov fix --- .github/workflows/main-tests.yml | 2 +- .github/workflows/pr_testing.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/main-tests.yml b/.github/workflows/main-tests.yml index 0fdaa6aa15..b34d2876f3 100644 --- a/.github/workflows/main-tests.yml +++ b/.github/workflows/main-tests.yml @@ -31,7 +31,7 @@ jobs: env: PYTHON: "" - uses: julia-actions/julia-processcoverage@v1 - - uses: codecov/codecov-action@v1 + - uses: codecov/codecov-action@v4 with: file: ./lcov.info flags: unittests diff --git a/.github/workflows/pr_testing.yml b/.github/workflows/pr_testing.yml index 2debe86fc0..ee99371f1d 100644 --- a/.github/workflows/pr_testing.yml +++ b/.github/workflows/pr_testing.yml @@ -27,7 +27,7 @@ jobs: env: PYTHON: "" - uses: julia-actions/julia-processcoverage@v1 - - uses: codecov/codecov-action@v1 + - uses: codecov/codecov-action@v4 with: file: ./lcov.info flags: unittests