diff --git a/Project.toml b/Project.toml index 25d9591..87185af 100644 --- a/Project.toml +++ b/Project.toml @@ -28,7 +28,6 @@ PowerFlowsExt = "PowerFlows" [sources] InfrastructureSystems = {url = "https://github.com/NREL-Sienna/InfrastructureSystems.jl", rev = "IS4"} -InfrastructureOptimizationModels = {url = "https://github.com/NREL-Sienna/InfrastructureOptimizationModels.jl", rev = "main"} [compat] Dates = "1" diff --git a/src/PowerOperationsModels.jl b/src/PowerOperationsModels.jl index d2dcbdd..675bd6c 100644 --- a/src/PowerOperationsModels.jl +++ b/src/PowerOperationsModels.jl @@ -141,8 +141,8 @@ import InfrastructureOptimizationModels: _include_constant_min_gen_power_in_constraint, add_variable_cost_to_objective!, _vom_offer_direction, - _add_pwl_constraint!, - add_pwl_term!, + add_pwl_constraint_delta!, + add_pwl_term_delta!, get_output_offer_curves, # Internal utilities used by market bid overrides and proportional_cost is_time_variant, @@ -175,7 +175,7 @@ import InfrastructureOptimizationModels: set_service_model!, finalize_template!, make_empty_jump_model_with_settings, - _set_model! + set_model! using InfrastructureOptimizationModels # TODO: use explicit imports. @@ -527,6 +527,7 @@ export HydroWaterFactorModel export HydroWaterModelReservoir export HydroTurbineBilinearDispatch export HydroTurbineWaterLinearDispatch +export HydroTurbineBin2BilinearDispatch export HydroEnergyModelReservoir export HydroTurbineEnergyDispatch export HydroTurbineEnergyCommitment diff --git a/src/common_models/market_bid_overrides.jl b/src/common_models/market_bid_overrides.jl index 4de701b..10e6ea8 100644 --- a/src/common_models/market_bid_overrides.jl +++ b/src/common_models/market_bid_overrides.jl @@ -175,7 +175,7 @@ function add_variable_cost_to_objective!( ::ImportExportSourceModel, ) isnothing(get_output_offer_curves(cost_function)) && return - add_pwl_term!( + add_pwl_term_delta!( IncrementalOffer(), container, component, @@ -194,7 +194,7 @@ function add_variable_cost_to_objective!( ::ImportExportSourceModel, ) isnothing(get_input_offer_curves(cost_function)) && return - add_pwl_term!( + add_pwl_term_delta!( DecrementalOffer(), container, component, @@ -221,7 +221,7 @@ function add_variable_cost_to_objective!( if !(isnothing(get_output_offer_curves(cost_function))) error("Component $(component_name) is not allowed to participate as a supply.") end - add_pwl_term!( + add_pwl_term_delta!( DecrementalOffer(), container, component, @@ -241,7 +241,7 @@ _vom_offer_direction(::AbstractControllablePowerLoadFormulation) = DecrementalOf """ PWL block offer constraints for ORDC (ReserveDemandCurve). """ -function _add_pwl_constraint!( +function add_pwl_constraint_delta!( container::OptimizationContainer, component::T, ::U, @@ -273,7 +273,7 @@ end """ PWL cost terms for StepwiseCostReserve (AbstractServiceFormulation). """ -function add_pwl_term!( +function add_pwl_term_delta!( container::OptimizationContainer, component::T, cost_data::PSY.CostCurve{PSY.PiecewiseIncrementalCurve}, @@ -300,7 +300,7 @@ function add_pwl_term!( slopes = IS.get_y_coords(data) break_points = PSY.get_x_coords(data) for t in time_steps - pwl_vars = add_pwl_variables!( + pwl_vars = add_pwl_variables_delta!( container, PiecewiseLinearBlockIncrementalOffer, T, @@ -309,9 +309,9 @@ function add_pwl_term!( length(slopes); upper_bound = Inf, ) - add_pwl_constraint!(container, component, U(), break_points, pwl_vars, t) + add_pwl_constraint_delta!(container, component, U(), break_points, pwl_vars, t) pwl_cost_expressions[t] = - get_pwl_cost_expression(pwl_vars, slopes, multiplier * dt) + get_pwl_cost_expression_delta(pwl_vars, slopes, multiplier * dt) end return pwl_cost_expressions end diff --git a/src/core/formulations.jl b/src/core/formulations.jl index f212c1a..25700ad 100644 --- a/src/core/formulations.jl +++ b/src/core/formulations.jl @@ -321,6 +321,11 @@ Formulation type to add injection variables for a HydroTurbine connected to rese """ struct HydroTurbineBilinearDispatch <: AbstractHydroDispatchFormulation end +""" +Formulation type to add injection variables for a HydroTurbine connected to reservoirs using a bilinear model (with water flow variables) [`PowerSystems.HydroGen`](@extref). Uses a linearized approximation. +""" +struct HydroTurbineBin2BilinearDispatch <: AbstractHydroDispatchFormulation end + """ Formulation type to add injection variables for a HydroTurbine connected to reservoirs using a linear model [`PowerSystems.HydroGen`](@extref). The model assumes a shallow reservoir. The head for the conversion between water flow and power can be approximated as a linear function of the water flow on which the head elevation is always the intake elevation. diff --git a/src/core/problem_template.jl b/src/core/problem_template.jl index e230078..5bdfa61 100644 --- a/src/core/problem_template.jl +++ b/src/core/problem_template.jl @@ -140,7 +140,7 @@ function set_device_model!( template::OperationsProblemTemplate, model::DeviceModel{D}, ) where {D <: IS.InfrastructureSystemsComponent} - _set_model!(template.devices, model) + set_model!(template.devices, model) return end @@ -152,7 +152,7 @@ function set_device_model!( template::OperationsProblemTemplate, model::DeviceModel{D}, ) where {D <: PSY.Branch} - _set_model!(template.branches, model) + set_model!(template.branches, model) return end @@ -191,7 +191,7 @@ function set_service_model!( service_name::String, model::ServiceModel{T, <:AbstractServiceFormulation}, ) where {T <: PSY.Service} - _set_model!(template.services, (service_name, Symbol(T)), model) + set_model!(template.services, (service_name, Symbol(T)), model) return end @@ -199,7 +199,7 @@ function set_service_model!( template::OperationsProblemTemplate, model::ServiceModel{<:PSY.Service, <:AbstractServiceFormulation}, ) - _set_model!(template.services, model) + set_model!(template.services, model) return end diff --git a/src/energy_storage_models/storage_models.jl b/src/energy_storage_models/storage_models.jl index 8396fdc..1f3730d 100644 --- a/src/energy_storage_models/storage_models.jl +++ b/src/energy_storage_models/storage_models.jl @@ -1916,7 +1916,7 @@ function _add_variable_cost_to_objective!( @debug "Market Bid" _group = LOG_GROUP_COST_FUNCTIONS component_name incremental_cost_curves = PSY.get_incremental_offer_curves(cost_function) if !isnothing(incremental_cost_curves) - add_pwl_term!( + add_pwl_term_delta!( IncrementalOffer(), container, component, @@ -1942,7 +1942,7 @@ function _add_variable_cost_to_objective!( @debug "Market Bid" _group = LOG_GROUP_COST_FUNCTIONS component_name decremental_cost_curves = PSY.get_decremental_offer_curves(cost_function) if !isnothing(decremental_cost_curves) - add_pwl_term!( + add_pwl_term_delta!( DecrementalOffer(), container, component, diff --git a/src/network_models/instantiate_network_model.jl b/src/network_models/instantiate_network_model.jl index b086d4d..f30cb18 100644 --- a/src/network_models/instantiate_network_model.jl +++ b/src/network_models/instantiate_network_model.jl @@ -106,6 +106,29 @@ function _get_irreducible_buses_due_to_dlrs( return collect(irreducible_buses) end +function _get_unmodeled_branch_types( + branch_models::BranchModelContainer, + sys::PSY.System, +) + unmodeled = DataType[] + for d in PSY.get_existing_device_types(sys) + if d <: PSY.ACTransmission && !haskey(branch_models, Symbol(d)) + push!(unmodeled, d) + end + end + return unmodeled +end + +function _validate_network_and_branches( + model::NetworkModel, + branch_models::BranchModelContainer, + sys::PSY.System, +) + unmodeled = _get_unmodeled_branch_types(branch_models, sys) + IOM._check_branch_network_compatibility(model, unmodeled) + return +end + ################################################################################# # Generic fallback for AbstractPowerModel (Ybus-based models: ACP, ACR, etc.) ################################################################################# @@ -116,6 +139,7 @@ function IOM.instantiate_network_model!( number_of_steps::Int, sys::PSY.System, ) where {T <: AbstractPowerModel} + _validate_network_and_branches(model, branch_models, sys) if isempty(model.subnetworks) model.subnetworks = PNM.find_subnetworks(sys) end @@ -181,6 +205,7 @@ function IOM.instantiate_network_model!( number_of_steps::Int, sys::PSY.System, ) + _validate_network_and_branches(model, branch_models, sys) PNM.populate_branch_maps_by_type!(model.network_reduction) empty!(model.reduced_branch_tracker) IOM.set_number_of_steps!(model.reduced_branch_tracker, number_of_steps) @@ -197,6 +222,7 @@ function IOM.instantiate_network_model!( number_of_steps::Int, sys::PSY.System, ) + _validate_network_and_branches(model, branch_models, sys) if isempty(model.subnetworks) model.subnetworks = PNM.find_subnetworks(sys) end @@ -225,7 +251,7 @@ function IOM.instantiate_network_model!( model, branch_models, ) - IOM._check_branch_network_compatibility(model, branch_models, sys) + _validate_network_and_branches(model, branch_models, sys) if IOM.get_PTDF_matrix(model) === nothing || !isempty(irreducible_buses) if IOM.get_PTDF_matrix(model) !== nothing @warn "Provided PTDF Matrix is being ignored since irreducible buses were identified because of DLRs. Recalculating PTDF Matrix with PowerNetworkMatrices.PTDF and the identified irreducible buses." @@ -334,7 +360,7 @@ function IOM.instantiate_network_model!( model, branch_models, ) - IOM._check_branch_network_compatibility(model, branch_models, sys) + _validate_network_and_branches(model, branch_models, sys) if IOM.get_PTDF_matrix(model) === nothing || !isempty(irreducible_buses) if IOM.get_PTDF_matrix(model) !== nothing @warn "Provided PTDF Matrix is being ignored since irreducible buses were identified because of DLRs. Recalculating PTDF Matrix with PowerNetworkMatrices.PTDF and the identified irreducible buses." diff --git a/src/services_models/reserves.jl b/src/services_models/reserves.jl index 7eee109..a6b63d7 100644 --- a/src/services_models/reserves.jl +++ b/src/services_models/reserves.jl @@ -543,7 +543,7 @@ function _add_reserves_variable_cost_to_objective!( end pwl_cost_expressions = - add_pwl_term!(container, component, variable_cost, T(), U()) + add_pwl_term_delta!(container, component, variable_cost, T(), U()) for t in time_steps add_to_expression!( container, diff --git a/src/static_injector_models/hydro_generation.jl b/src/static_injector_models/hydro_generation.jl index 9603d19..29d2eae 100644 --- a/src/static_injector_models/hydro_generation.jl +++ b/src/static_injector_models/hydro_generation.jl @@ -488,7 +488,7 @@ function add_variables!( T <: HydroTurbineFlowRateVariable, U <: Union{Vector{D}, IS.FlattenIteratorWrapper{D}}, W <: Union{Vector{E}, IS.FlattenIteratorWrapper{E}}, - X <: Union{HydroTurbineBilinearDispatch, HydroTurbineWaterLinearDispatch}, + X <: Union{HydroTurbineBilinearDispatch, HydroTurbineWaterLinearDispatch, HydroTurbineBin2BilinearDispatch}, } where { D <: PSY.HydroTurbine, E <: PSY.HydroReservoir, @@ -1835,6 +1835,80 @@ function add_constraints!( return end +""" +This function define the relationship between turbined flow and power produced with a linear approximation for the bilinear product. +""" +function add_constraints!( + container::OptimizationContainer, + sys::PSY.System, + ::Type{TurbinePowerOutputConstraint}, + devices::IS.FlattenIteratorWrapper{V}, + model::DeviceModel{V, W}, + ::NetworkModel{X}, +) where { + V <: PSY.HydroTurbine, + W <: HydroTurbineBin2BilinearDispatch, + X <: PM.AbstractPowerModel, +} + time_steps = get_time_steps(container) + base_power = get_model_base_power(container) + names = PSY.get_name.(devices) + constraint = + add_constraints_container!( + container, + TurbinePowerOutputConstraint(), + V, + names, + time_steps, + ) + power = get_variable(container, ActivePowerVariable(), V) + flow = get_variable(container, HydroTurbineFlowRateVariable(), V) + head = get_variable(container, HydroReservoirHeadVariable(), PSY.HydroReservoir) + for d in devices + name = PSY.get_name(d) + conversion_factor = PSY.get_conversion_factor(d) + reservoirs = filter(PSY.get_available, PSY.get_connected_head_reservoirs(sys, d)) + powerhouse_elevation = PSY.get_powerhouse_elevation(d) + + fh_prod = IOM._add_bilinear_approx!( + IOM.Bin2Config(IOM.SolverSOS2QuadConfig(4)), + container, + V, + PSY.get_name.(reservoirs), + time_steps, + flow[name, :, :], + head, + [ + ( + min = get_variable_lower_bound(HydroTurbineFlowRateVariable(), d, W()), + max = get_variable_upper_bound(HydroTurbineFlowRateVariable(), d, W()) + ) for _=1:length(reservoirs) + ], + [ + ( + min = get_variable_lower_bound(HydroReservoirHeadVariable(), res, W()), + max = get_variable_upper_bound(HydroReservoirHeadVariable(), res, W()) + ) for res in reservoirs + ], + "$(get_name(d))_FlowHeadProduct" + ) + + for t in time_steps + constraint[name, t] = JuMP.@constraint( + container.JuMPmodel, + power[name, t] == + GRAVITATIONAL_CONSTANT * WATER_DENSITY * conversion_factor * + sum( + fh_prod[PSY.get_name(res), t] + - powerhouse_elevation * flow[name, PSY.get_name(res), t] + for res in reservoirs + ) / (1e6 * base_power) + ) + end + end + return +end + ############################################################################ ############################### Expressions ################################ ############################################################################ diff --git a/src/static_injector_models/hydrogeneration_constructor.jl b/src/static_injector_models/hydrogeneration_constructor.jl index c27e5e2..b7ae1f2 100644 --- a/src/static_injector_models/hydrogeneration_constructor.jl +++ b/src/static_injector_models/hydrogeneration_constructor.jl @@ -1809,7 +1809,7 @@ function construct_device!( network_model::NetworkModel{S}, ) where { H <: PSY.HydroTurbine, - D <: Union{HydroTurbineBilinearDispatch, HydroTurbineWaterLinearDispatch}, + D <: Union{HydroTurbineBilinearDispatch, HydroTurbineWaterLinearDispatch, HydroTurbineBin2BilinearDispatch}, S <: AbstractActivePowerModel, } devices = get_available_components(model, sys) @@ -1873,7 +1873,7 @@ function construct_device!( network_model::NetworkModel{S}, ) where { H <: PSY.HydroTurbine, - D <: Union{HydroTurbineBilinearDispatch, HydroTurbineWaterLinearDispatch}, + D <: Union{HydroTurbineBilinearDispatch, HydroTurbineWaterLinearDispatch, HydroTurbineBin2BilinearDispatch}, S <: AbstractActivePowerModel, } devices = get_available_components(model, sys) diff --git a/src/static_injector_models/thermal_generation.jl b/src/static_injector_models/thermal_generation.jl index 3e41292..0f7efd2 100644 --- a/src/static_injector_models/thermal_generation.jl +++ b/src/static_injector_models/thermal_generation.jl @@ -1559,7 +1559,7 @@ Add PWL cost terms for ThermalDispatchNoMin formulation. Rejects non-convex or negative-slope PWL data since ThermalDispatchNoMin cannot use SOS-2 formulations. """ -function IOM.add_pwl_term!( +function IOM.add_pwl_term_lambda!( container::IOM.OptimizationContainer, component::T, cost_function::Union{ @@ -1621,7 +1621,7 @@ function IOM.add_pwl_term!( temp_cost_function = IOM.create_temporary_cost_function_in_system_per_unit(cost_function, data) for t in time_steps - IOM.add_pwl_variables!(container, T, name, t, data) + IOM.add_pwl_variables_lambda!(container, T, name, t, data) power_var = IOM.get_variable(container, U(), T)[name, t] IOM._add_pwl_constraint_standard!( container, @@ -1632,7 +1632,7 @@ function IOM.add_pwl_term!( power_var, ) pwl_cost = - IOM.get_pwl_cost_expression( + IOM.get_pwl_cost_expression_lambda( container, component, t, diff --git a/test/Project.toml b/test/Project.toml index e676ec5..91d6928 100644 --- a/test/Project.toml +++ b/test/Project.toml @@ -32,7 +32,6 @@ TimerOutputs = "a759f4b9-e2f1-59dc-863e-4aeb61b1ea8f" UUIDs = "cf7118a7-6976-5b1a-9a39-7adc72f591a4" [sources] -InfrastructureOptimizationModels = {rev = "main", url = "https://github.com/NREL-Sienna/InfrastructureOptimizationModels.jl"} InfrastructureSystems = {rev = "IS4", url = "https://github.com/NREL-Sienna/InfrastructureSystems.jl"} [compat] diff --git a/test/test_device_hydro_constructors.jl b/test/test_device_hydro_constructors.jl index a04fdf6..cf56601 100644 --- a/test/test_device_hydro_constructors.jl +++ b/test/test_device_hydro_constructors.jl @@ -1,637 +1,697 @@ ######################################### #### RESERVOIR BUDGET DISPATCH TESTS #### ######################################### -@testset "Hydro DCPLossLess with HydroTurbineEnergyDispatch and HydroEnergyModelReservoir (with budget) Formulations" begin - turbine_model = DeviceModel(HydroTurbine, HydroTurbineEnergyDispatch) - reservoir_model = DeviceModel( - HydroReservoir, - HydroEnergyModelReservoir; - attributes = Dict{String, Any}( - "energy_target" => false, - "hydro_budget" => true, - ), - ) - - c_sys5_hyd = PSB.build_system(PSITestSystems, "c_sys5_hy_turbine_energy") - - # No Parameters Testing - model = DecisionModel(MockOperationProblem, DCPPowerModel, c_sys5_hyd) - mock_construct_device!(model, turbine_model) - mock_construct_device!(model, reservoir_model) - moi_tests(model, 120, 0, 25, 24, 24, false) - psi_checkobjfun_test(model, GAEVF) -end - -@testset "Hydro ACPPowerModel with HydroTurbineEnergyDispatch and HydroEnergyModelReservoir (with budget) Formulations" begin - turbine_model = DeviceModel(HydroTurbine, HydroTurbineEnergyDispatch) - reservoir_model = DeviceModel( - HydroReservoir, - HydroEnergyModelReservoir; - use_slacks = true, - attributes = Dict{String, Any}( - "energy_target" => false, - "hydro_budget" => true, - ), - ) - - c_sys5_hyd = PSB.build_system(PSITestSystems, "c_sys5_hy_turbine_energy") - - # No Parameters Testing - model = DecisionModel(MockOperationProblem, ACPPowerModel, c_sys5_hyd) - mock_construct_device!(model, turbine_model) - mock_construct_device!(model, reservoir_model) - moi_tests(model, 192, 0, 49, 48, 24, false) - psi_checkobjfun_test(model, GAEVF) -end - -########################################### -#### RESERVOIR BUDGET COMMITMENT TESTS #### -########################################### - -@testset "Hydro DCPLossLess with HydroTurbineEnergyCommitment and HydroEnergyModelReservoir (with budget) Formulations" begin - turbine_model = DeviceModel(HydroTurbine, HydroTurbineEnergyCommitment) - reservoir_model = DeviceModel( - HydroReservoir, - HydroEnergyModelReservoir; - attributes = Dict{String, Any}( - "energy_target" => false, - "hydro_budget" => true, - ), - ) - - c_sys5_hyd = PSB.build_system(PSITestSystems, "c_sys5_hy_turbine_energy") - - # No Parameters Testing - model = DecisionModel(MockOperationProblem, DCPPowerModel, c_sys5_hyd) - mock_construct_device!(model, turbine_model) - mock_construct_device!(model, reservoir_model) - moi_tests(model, 144, 0, 25, 24, 24, true) - psi_checkobjfun_test(model, GAEVF) -end - -@testset "Hydro ACPPowerModel with HydroTurbineEnergyCommitment and HydroEnergyModelReservoir (with budget) Formulations" begin - turbine_model = DeviceModel(HydroTurbine, HydroTurbineEnergyCommitment) - reservoir_model = DeviceModel( - HydroReservoir, - HydroEnergyModelReservoir; - attributes = Dict{String, Any}( - "energy_target" => false, - "hydro_budget" => true, - ), - ) - - c_sys5_hyd = PSB.build_system(PSITestSystems, "c_sys5_hy_turbine_energy") - - # No Parameters Testing - model = DecisionModel(MockOperationProblem, ACPPowerModel, c_sys5_hyd) - mock_construct_device!(model, turbine_model) - mock_construct_device!(model, reservoir_model) - moi_tests(model, 168, 0, 49, 48, 24, true) - psi_checkobjfun_test(model, GAEVF) -end - -######################################### -#### RESERVOIR TARGET DISPATCH TESTS #### -######################################### - -@testset "Hydro DCPLossLess with HydroTurbineEnergyDispatch and HydroEnergyModelReservoir (with target) Formulations" begin - turbine_model = DeviceModel(HydroTurbine, HydroTurbineEnergyDispatch) - reservoir_model = DeviceModel( - HydroReservoir, - HydroEnergyModelReservoir; - attributes = Dict{String, Any}( - "energy_target" => true, - "hydro_budget" => false, - ), - ) - - c_sys5_hyd = PSB.build_system(PSITestSystems, "c_sys5_hy_turbine_energy") - - # No Parameters Testing - model = DecisionModel(MockOperationProblem, DCPPowerModel, c_sys5_hyd) - mock_construct_device!(model, turbine_model) - mock_construct_device!(model, reservoir_model) - moi_tests(model, 120, 0, 24, 24, 25, false) - psi_checkobjfun_test(model, GAEVF) -end - -@testset "Hydro ACPPowerModel with HydroTurbineEnergyDispatch and HydroEnergyModelReservoir (with target) Formulations" begin - turbine_model = DeviceModel(HydroTurbine, HydroTurbineEnergyDispatch) - reservoir_model = DeviceModel( - HydroReservoir, - HydroEnergyModelReservoir; - attributes = Dict{String, Any}( - "energy_target" => true, - "hydro_budget" => false, - ), - ) - - c_sys5_hyd = PSB.build_system(PSITestSystems, "c_sys5_hy_turbine_energy") - - # No Parameters Testing - model = DecisionModel(MockOperationProblem, ACPPowerModel, c_sys5_hyd) - mock_construct_device!(model, turbine_model) - mock_construct_device!(model, reservoir_model) - moi_tests(model, 144, 0, 48, 48, 25, false) - psi_checkobjfun_test(model, GAEVF) -end - -######################################### -########### RUN OF RIVER TESTS ########## -######################################### - -@testset "Solving ED Hydro System using Dispatch Run of River" begin - sys = PSB.build_system(PSITestSystems, "c_sys5_hy") - networks = [ACPPowerModel, DCPPowerModel] - - test_outputs = - Dict{Any, Float64}(ACPPowerModel => 136581.41, DCPPowerModel => 135382.37) - - for net in networks - @testset "HydroRoR ED model $(net)" begin - template = get_thermal_dispatch_template_network(net) - set_device_model!(template, HydroDispatch, HydroDispatchRunOfRiver) - ED = DecisionModel( - EconomicDispatchProblem, - template, - sys; - optimizer = ipopt_optimizer, - ) - @test build!(ED; output_dir = mktempdir(; cleanup = true)) == - ModelBuildStatus.BUILT - psi_checksolve_test( - ED, - [MOI.OPTIMAL, MOI.LOCALLY_SOLVED], - test_outputs[net], - 1000, - ) - end - end -end - -@testset "Solving ED Hydro System using Commitment Run of River" begin - sys = PSB.build_system(PSITestSystems, "c_sys5_hy") - net = CopperPlatePowerModel - - template = get_thermal_dispatch_template_network(net) - set_device_model!(template, HydroDispatch, HydroCommitmentRunOfRiver) - - @testset "HydroRoR ED model $(net)" begin - ED = - DecisionModel(UnitCommitmentProblem, template, sys; optimizer = HiGHS_optimizer) - @test build!(ED; output_dir = mktempdir(; cleanup = true)) == - ModelBuildStatus.BUILT - psi_checksolve_test(ED, [MOI.OPTIMAL, MOI.LOCALLY_SOLVED], 135382.38, 1000) - end -end - -@testset "Test Reserves from Hydro with RunOfRiver" begin - template = OperationsProblemTemplate(CopperPlatePowerModel) - set_device_model!(template, PowerLoad, StaticPowerLoad) - set_device_model!(template, HydroTurbine, HydroDispatchRunOfRiver) - set_service_model!( - template, - ServiceModel(VariableReserve{ReserveUp}, RangeReserve, "Reserve5"), - ) - set_service_model!( - template, - ServiceModel(VariableReserve{ReserveDown}, RangeReserve, "Reserve6"), - ) - set_service_model!( - template, - ServiceModel(ReserveDemandCurve{ReserveUp}, StepwiseCostReserve, "ORDC1"), - ) - - c_sys5_hyd = PSB.build_system( - PSITestSystems, - "c_sys5_hyd"; - add_reserves = true, - force_build = true, - ) - model = DecisionModel(template, c_sys5_hyd) - @test build!(model; output_dir = mktempdir(; cleanup = true)) == - ModelBuildStatus.BUILT - # The value of this test needs to be revised - # moi_tests(model, 240, 0, 48, 96, 72, false) -end - -######################################### -###### RESERVOIR SYSTEM TESTS ########### -######################################### - -@testset "Solving ED Hydro System using Dispatch with Reservoir" begin - sys = PSB.build_system(PSITestSystems, "c_sys5_hy_turbine_energy") - networks = [ACPPowerModel, DCPPowerModel] - turbine_model = DeviceModel(HydroTurbine, HydroTurbineEnergyDispatch) - models = [ - DeviceModel( - HydroReservoir, - HydroEnergyModelReservoir; - attributes = Dict{String, Any}( - "energy_target" => true, - "hydro_budget" => false, - ), - ), - DeviceModel( - HydroReservoir, - HydroEnergyModelReservoir; - attributes = Dict{String, Any}( - "energy_target" => false, - "hydro_budget" => true, - ), - ), - ] - test_outputs = Dict{Any, Float64}( - (ACPPowerModel, HydroEnergyModelReservoir, true) => 136296.0, - (DCPPowerModel, HydroEnergyModelReservoir, true) => 135404.0, - (ACPPowerModel, HydroEnergyModelReservoir, false) => 131305.0, - (DCPPowerModel, HydroEnergyModelReservoir, false) => 130479.0, - ) - - for net in networks - for reservoir_model in models - formulation = get_formulation(reservoir_model) - attrs = IOM.get_attributes(reservoir_model) - energy_target = get(attrs, "energy_target", false) - hydro_budget = get(attrs, "hydro_budget", false) - @testset "$(formulation) with energy_target: $energy_target, and hydro_budget: $hydro_budget, ED model on $(net)" begin - template = get_thermal_dispatch_template_network(net) - set_device_model!(template, turbine_model) - set_device_model!(template, reservoir_model) - - ED = DecisionModel( - EconomicDispatchProblem, - template, - sys; - optimizer = ipopt_optimizer, - ) - @test build!(ED; output_dir = mktempdir(; cleanup = true)) == - ModelBuildStatus.BUILT - psi_checksolve_test( - ED, - [MOI.OPTIMAL, MOI.LOCALLY_SOLVED], - test_outputs[(net, formulation, energy_target)], - 1000, - ) - end - end - end -end - -@testset "Solving ED Hydro System using Commitment with Reservoir" begin - sys = PSB.build_system(PSITestSystems, "c_sys5_hy_turbine_energy") - net = DCPPowerModel - turbine_model = DeviceModel(HydroTurbine, HydroTurbineEnergyCommitment) - models = [ - DeviceModel( - HydroReservoir, - HydroEnergyModelReservoir; - attributes = Dict{String, Any}( - "energy_target" => true, - "hydro_budget" => false, - ), - ), - DeviceModel( - HydroReservoir, - HydroEnergyModelReservoir; - attributes = Dict{String, Any}( - "energy_target" => false, - "hydro_budget" => true, - ), - ), - ] - test_outputs = Dict{Any, Float64}( - (DCPPowerModel, HydroEnergyModelReservoir, true) => 141061.0, - (DCPPowerModel, HydroEnergyModelReservoir, false) => 144109.0, - ) - - for reservoir_model in models - formulation = get_formulation(reservoir_model) - attrs = IOM.get_attributes(reservoir_model) - energy_target = get(attrs, "energy_target", false) - hydro_budget = get(attrs, "hydro_budget", false) - @testset "$(formulation) with energy_target: $energy_target, and hydro_budget: $hydro_budget, ED model on $(net)" begin - template = get_thermal_dispatch_template_network(net) - set_device_model!(template, turbine_model) - set_device_model!(template, reservoir_model) - - ED = DecisionModel( - EconomicDispatchProblem, - template, - sys; - optimizer = HiGHS_optimizer, - ) - @test build!(ED; output_dir = mktempdir(; cleanup = true)) == - ModelBuildStatus.BUILT - psi_checksolve_test( - ED, - [MOI.OPTIMAL, MOI.LOCALLY_SOLVED], - test_outputs[(net, formulation, energy_target)], - 10000, #update to 10k to handle the difference in Mac and Ubuntu. Likely a HiGHS issue. - ) - end - end -end - -######################################################## -####### Hydro DISPATCH RUN OF RIVER BUDGET TEST ######## -######################################################## -@testset "Test Hydro Dispatch Run Of River Formulations " begin - device_model = DeviceModel(HydroDispatch, HydroDispatchRunOfRiverBudget; - use_slacks = true, attributes = Dict("hydro_budget_interval" => Hour(24))) - - sys = PSB.build_system(PSITestSystems, "c_sys5_hy"; add_single_time_series = true) - hy = only(get_components(HydroDispatch, sys)) - max_power = get_max_active_power(hy) - resolution = Dates.Hour(1) - tstamp = range(DateTime("2024-01-01T00:00:00"); step = resolution, length = 48) - data = ones(length(tstamp)) / (get_base_power(sys) * max_power) - ts = SingleTimeSeries("hydro_budget", TimeArray(tstamp, data)) - add_time_series!(sys, hy, ts) - transform_single_time_series!(sys, Hour(24), Hour(24)) - - model = DecisionModel(MockOperationProblem, CopperPlatePowerModel, sys) - mock_construct_device!(model, device_model) - moi_tests(model, 48, 0, 50, 24, 0, false) - psi_checkobjfun_test(model, GAEVF) -end - -@testset "Solve Hydro Dispatch Run Of River" begin - output_dir = mktempdir(; cleanup = true) - - c_sys5_hy = PSB.build_system(PSITestSystems, "c_sys5_hy"; add_single_time_series = true) - - hydro_budget = 24 - eps = 1e-6 - - hy = only(get_components(HydroDispatch, c_sys5_hy)) - max_power = get_max_active_power(hy) - - tstamp = range(DateTime("2024-01-01T00:00:00"); step = Dates.Hour(1), length = 48) - data = ones(length(tstamp)) / (get_base_power(c_sys5_hy) * max_power) - ts = SingleTimeSeries("hydro_budget", TimeArray(tstamp, data)) - add_time_series!(c_sys5_hy, first(get_components(HydroDispatch, c_sys5_hy)), ts) - #remove_time_series!(c_sys5_hy, Deterministic) - transform_single_time_series!(c_sys5_hy, Hour(24), Hour(24)) - - template_uc = OperationsProblemTemplate() - set_device_model!(template_uc, ThermalStandard, ThermalBasicUnitCommitment) - set_device_model!(template_uc, RenewableDispatch, RenewableFullDispatch) - set_device_model!(template_uc, PowerLoad, StaticPowerLoad) - set_device_model!(template_uc, RenewableNonDispatch, FixedOutput) - set_device_model!( - template_uc, - DeviceModel(HydroDispatch, HydroDispatchRunOfRiverBudget; - attributes = Dict("hydro_budget_interval" => Hour(hydro_budget))), - ) - model = DecisionModel( - template_uc, - c_sys5_hy; - optimizer = HiGHS_optimizer, - store_variable_names = true, - ) - - @test build!(model; output_dir = output_dir) == - ModelBuildStatus.BUILT - - @test solve!(model; output_dir = output_dir) == - IS.Simulation.RunStatus.SUCCESSFULLY_FINALIZED - - outputs = OptimizationProblemOutputs(model) - df = read_variable(outputs, "ActivePowerVariable__HydroDispatch") - hydro_power_sum = - sum(df[!, :value]) - - @test abs(hydro_power_sum - hydro_budget) <= eps -end - -@testset "Make Hydro Dispatch Run Of River with Reserves" begin - output_dir = mktempdir(; cleanup = true) - - c_sys5_hy = PSB.build_system( - PSITestSystems, - "c_sys5_hy"; - add_single_time_series = true, - add_reserves = true, - ) - - # Fix reserve parameters - reg_up = only(get_components(VariableReserve{ReserveUp}, c_sys5_hy)) - reg_dn = only(get_components(VariableReserve{ReserveDown}, c_sys5_hy)) - set_deployed_fraction!(reg_up, 0.0) - set_deployed_fraction!(reg_dn, 0.0) - set_requirement!(reg_up, 0.01) - set_requirement!(reg_dn, 0.01) - - hydro_budget = 24 - eps = 1e-6 - - hy = only(get_components(HydroDispatch, c_sys5_hy)) - - # Update Service allocation - # Remove reg up from hydro, but leave reg dn - remove_service!(hy, reg_up) - - # Add reg up to thermals - for th in get_components(ThermalStandard, c_sys5_hy) - add_service!(th, reg_up, c_sys5_hy) - end - - max_power = get_max_active_power(hy) - tstamp = range(DateTime("2024-01-01T00:00:00"); step = Dates.Hour(1), length = 48) - data = ones(length(tstamp)) / (get_base_power(c_sys5_hy) * max_power) - ts = SingleTimeSeries("hydro_budget", TimeArray(tstamp, data)) - add_time_series!(c_sys5_hy, first(get_components(HydroDispatch, c_sys5_hy)), ts) - - ## add extra hydro budget - hy_copy = HydroDispatch(; - name = "HydroDispatchCopy", - available = get_available(hy), - bus = get_bus(hy), - active_power = get_active_power(hy), - reactive_power = get_reactive_power(hy), - rating = get_rating(hy), - prime_mover_type = get_prime_mover_type(hy), - active_power_limits = get_active_power_limits(hy), - reactive_power_limits = get_reactive_power_limits(hy), - ramp_limits = get_ramp_limits(hy), - time_limits = get_time_limits(hy), - base_power = get_base_power(hy), - ) - add_component!(c_sys5_hy, hy_copy) - copy_time_series!(hy_copy, hy) - - transform_single_time_series!(c_sys5_hy, Hour(24), Hour(24)) - - template_uc = OperationsProblemTemplate() - set_device_model!(template_uc, ThermalStandard, ThermalBasicUnitCommitment) - set_device_model!(template_uc, RenewableDispatch, RenewableFullDispatch) - set_device_model!(template_uc, PowerLoad, StaticPowerLoad) - set_device_model!(template_uc, RenewableNonDispatch, FixedOutput) - set_device_model!( - template_uc, - DeviceModel(HydroDispatch, HydroDispatchRunOfRiverBudget; - attributes = Dict("hydro_budget_interval" => Hour(hydro_budget))), - ) - set_service_model!(template_uc, VariableReserve{ReserveUp}, RangeReserve) - set_service_model!(template_uc, VariableReserve{ReserveDown}, RangeReserve) - model = DecisionModel( - template_uc, - c_sys5_hy; - optimizer = HiGHS_optimizer, - store_variable_names = true, - ) - - @test build!(model; output_dir = output_dir) == - ModelBuildStatus.BUILT - - @test solve!(model; output_dir = output_dir) == - IS.Simulation.RunStatus.SUCCESSFULLY_FINALIZED -end - -################################################ -####### Hydro PUMP ENERGY DISPATCH TEST ######## -################################################ -@testset "Test Hydro Pump Energy Dispatch Formulations " begin - device_model = DeviceModel( - HydroPumpTurbine, - HydroPumpEnergyDispatch; - attributes = Dict{String, Any}( - "reservation" => true, - "energy_target" => true, - ), - ) - - c_sys5_bat = - PSB.build_system( - PSITestSystems, - "c_sys5_hydro_pump_energy"; - add_reserves = true, - add_single_time_series = true, - ) - - hy_pump = first(PSY.get_components(HydroPumpTurbine, c_sys5_bat)) - transform_single_time_series!(c_sys5_bat, Hour(24), Hour(24)) - - model = DecisionModel(MockOperationProblem, CopperPlatePowerModel, c_sys5_bat) - mock_construct_device!(model, device_model) - moi_tests(model, 72, 0, 48, 24, 0, true) - psi_checkobjfun_test(model, GAEVF) -end - -@testset "Test Hydro Pump Energy Dispatch Formulations 2" begin - output_dir = mktempdir(; cleanup = true) - - c_sys5_bat = - PSB.build_system( - PSITestSystems, - "c_sys5_hydro_pump_energy"; - add_reserves = true, - add_single_time_series = true, - ) - - hy_pump = first(PSY.get_components(HydroPumpTurbine, c_sys5_bat)) - - transform_single_time_series!(c_sys5_bat, Hour(24), Hour(24)) - - template_uc = OperationsProblemTemplate() - set_device_model!(template_uc, ThermalStandard, ThermalBasicUnitCommitment) - set_device_model!(template_uc, RenewableDispatch, RenewableFullDispatch) - set_device_model!(template_uc, PowerLoad, StaticPowerLoad) - set_device_model!(template_uc, RenewableNonDispatch, FixedOutput) - set_device_model!( - template_uc, - DeviceModel( - HydroPumpTurbine, - HydroPumpEnergyDispatch; - attributes = Dict{String, Any}( - "reservation" => true, - "energy_target" => true, - ), - ), - ) - - model = DecisionModel( - template_uc, - c_sys5_bat; - optimizer = HiGHS_optimizer, - store_variable_names = true, - ) - - @test build!(model; output_dir = output_dir) == - ModelBuildStatus.BUILT - - @test solve!(model; output_dir = output_dir) == - IS.Simulation.RunStatus.SUCCESSFULLY_FINALIZED -end - -######################################### -######## HydroBlock model Tests ######### -######################################### - -@testset "Test Hydro Block Optimization Formulation" begin - output_dir = mktempdir(; cleanup = true) - modeling_horizon = 3 * 24 * 1 - - sys = PSB.build_system(PSITestSystems, "c_sys5_hy_turbine_energy") - res = first(PSY.get_components(HydroReservoir, sys)) - - set_head_to_volume_factor!(res, LinearCurve(1.0)) - set_storage_level_limits!(res, (min = 4000, max = 6000)) - set_level_targets!(res, 0.9) - template_ed = OperationsProblemTemplate( - NetworkModel( - CopperPlatePowerModel; - ), - ) - - set_device_model!(template_ed, ThermalStandard, ThermalBasicDispatch) - set_device_model!(template_ed, PowerLoad, StaticPowerLoad) - set_device_model!(template_ed, HydroReservoir, HydroWaterFactorModel) - set_device_model!(template_ed, HydroTurbine, HydroWaterFactorModel) - - model = DecisionModel( - template_ed, - sys; - name = "ED", - optimizer = ipopt_optimizer, - optimizer_solve_log_print = true, - store_variable_names = true, - horizon = Hour(24), - ) - - @test build!(model; output_dir = output_dir) == - ModelBuildStatus.BUILT - - @test solve!(model; optimizer = ipopt_optimizer, output_dir = output_dir) == - IS.Simulation.RunStatus.SUCCESSFULLY_FINALIZED - - outputs = OptimizationProblemOutputs(model) - power_load = read_parameter(outputs, "ActivePowerTimeSeriesParameter__PowerLoad") - reservoir_inflow = read_parameter(outputs, "InflowTimeSeriesParameter__HydroReservoir") - - water_spillage = read_variable(outputs, "WaterSpillageVariable__HydroReservoir") - thermal_power = read_variable(outputs, "ActivePowerVariable__ThermalStandard") - hydro_power = read_variable(outputs, "ActivePowerVariable__HydroTurbine") - - turbine_output = read_aux_variable(outputs, "HydroEnergyOutput__HydroTurbine") - reservoir_volume = - read_variable(outputs, "HydroReservoirVolumeVariable__HydroReservoir") - - var = read_variable( - outputs, - "HydroReservoirVolumeVariable__HydroReservoir"; - table_format = TableFormat.WIDE, - ) - - # check the second step is equal to the first step + dispatch -end - -################################################ -######## HydroWaterModelReservoir TEST ######### -################################################ - -@testset "Solve HydroWaterModelReservoir" begin +# @testset "Hydro DCPLossLess with HydroTurbineEnergyDispatch and HydroEnergyModelReservoir (with budget) Formulations" begin +# turbine_model = DeviceModel(HydroTurbine, HydroTurbineEnergyDispatch) +# reservoir_model = DeviceModel( +# HydroReservoir, +# HydroEnergyModelReservoir; +# attributes = Dict{String, Any}( +# "energy_target" => false, +# "hydro_budget" => true, +# ), +# ) + +# c_sys5_hyd = PSB.build_system(PSITestSystems, "c_sys5_hy_turbine_energy") + +# # No Parameters Testing +# model = DecisionModel(MockOperationProblem, DCPPowerModel, c_sys5_hyd) +# mock_construct_device!(model, turbine_model) +# mock_construct_device!(model, reservoir_model) +# moi_tests(model, 120, 0, 25, 24, 24, false) +# psi_checkobjfun_test(model, GAEVF) +# end + +# @testset "Hydro ACPPowerModel with HydroTurbineEnergyDispatch and HydroEnergyModelReservoir (with budget) Formulations" begin +# turbine_model = DeviceModel(HydroTurbine, HydroTurbineEnergyDispatch) +# reservoir_model = DeviceModel( +# HydroReservoir, +# HydroEnergyModelReservoir; +# use_slacks = true, +# attributes = Dict{String, Any}( +# "energy_target" => false, +# "hydro_budget" => true, +# ), +# ) + +# c_sys5_hyd = PSB.build_system(PSITestSystems, "c_sys5_hy_turbine_energy") + +# # No Parameters Testing +# model = DecisionModel(MockOperationProblem, ACPPowerModel, c_sys5_hyd) +# mock_construct_device!(model, turbine_model) +# mock_construct_device!(model, reservoir_model) +# moi_tests(model, 192, 0, 49, 48, 24, false) +# psi_checkobjfun_test(model, GAEVF) +# end + +# ########################################### +# #### RESERVOIR BUDGET COMMITMENT TESTS #### +# ########################################### + +# @testset "Hydro DCPLossLess with HydroTurbineEnergyCommitment and HydroEnergyModelReservoir (with budget) Formulations" begin +# turbine_model = DeviceModel(HydroTurbine, HydroTurbineEnergyCommitment) +# reservoir_model = DeviceModel( +# HydroReservoir, +# HydroEnergyModelReservoir; +# attributes = Dict{String, Any}( +# "energy_target" => false, +# "hydro_budget" => true, +# ), +# ) + +# c_sys5_hyd = PSB.build_system(PSITestSystems, "c_sys5_hy_turbine_energy") + +# # No Parameters Testing +# model = DecisionModel(MockOperationProblem, DCPPowerModel, c_sys5_hyd) +# mock_construct_device!(model, turbine_model) +# mock_construct_device!(model, reservoir_model) +# moi_tests(model, 144, 0, 25, 24, 24, true) +# psi_checkobjfun_test(model, GAEVF) +# end + +# @testset "Hydro ACPPowerModel with HydroTurbineEnergyCommitment and HydroEnergyModelReservoir (with budget) Formulations" begin +# turbine_model = DeviceModel(HydroTurbine, HydroTurbineEnergyCommitment) +# reservoir_model = DeviceModel( +# HydroReservoir, +# HydroEnergyModelReservoir; +# attributes = Dict{String, Any}( +# "energy_target" => false, +# "hydro_budget" => true, +# ), +# ) + +# c_sys5_hyd = PSB.build_system(PSITestSystems, "c_sys5_hy_turbine_energy") + +# # No Parameters Testing +# model = DecisionModel(MockOperationProblem, ACPPowerModel, c_sys5_hyd) +# mock_construct_device!(model, turbine_model) +# mock_construct_device!(model, reservoir_model) +# moi_tests(model, 168, 0, 49, 48, 24, true) +# psi_checkobjfun_test(model, GAEVF) +# end + +# ######################################### +# #### RESERVOIR TARGET DISPATCH TESTS #### +# ######################################### + +# @testset "Hydro DCPLossLess with HydroTurbineEnergyDispatch and HydroEnergyModelReservoir (with target) Formulations" begin +# turbine_model = DeviceModel(HydroTurbine, HydroTurbineEnergyDispatch) +# reservoir_model = DeviceModel( +# HydroReservoir, +# HydroEnergyModelReservoir; +# attributes = Dict{String, Any}( +# "energy_target" => true, +# "hydro_budget" => false, +# ), +# ) + +# c_sys5_hyd = PSB.build_system(PSITestSystems, "c_sys5_hy_turbine_energy") + +# # No Parameters Testing +# model = DecisionModel(MockOperationProblem, DCPPowerModel, c_sys5_hyd) +# mock_construct_device!(model, turbine_model) +# mock_construct_device!(model, reservoir_model) +# moi_tests(model, 120, 0, 24, 24, 25, false) +# psi_checkobjfun_test(model, GAEVF) +# end + +# @testset "Hydro ACPPowerModel with HydroTurbineEnergyDispatch and HydroEnergyModelReservoir (with target) Formulations" begin +# turbine_model = DeviceModel(HydroTurbine, HydroTurbineEnergyDispatch) +# reservoir_model = DeviceModel( +# HydroReservoir, +# HydroEnergyModelReservoir; +# attributes = Dict{String, Any}( +# "energy_target" => true, +# "hydro_budget" => false, +# ), +# ) + +# c_sys5_hyd = PSB.build_system(PSITestSystems, "c_sys5_hy_turbine_energy") + +# # No Parameters Testing +# model = DecisionModel(MockOperationProblem, ACPPowerModel, c_sys5_hyd) +# mock_construct_device!(model, turbine_model) +# mock_construct_device!(model, reservoir_model) +# moi_tests(model, 144, 0, 48, 48, 25, false) +# psi_checkobjfun_test(model, GAEVF) +# end + +# ######################################### +# ########### RUN OF RIVER TESTS ########## +# ######################################### + +# @testset "Solving ED Hydro System using Dispatch Run of River" begin +# sys = PSB.build_system(PSITestSystems, "c_sys5_hy") +# networks = [ACPPowerModel, DCPPowerModel] + +# test_outputs = +# Dict{Any, Float64}(ACPPowerModel => 136581.41, DCPPowerModel => 135382.37) + +# for net in networks +# @testset "HydroRoR ED model $(net)" begin +# template = get_thermal_dispatch_template_network(net) +# set_device_model!(template, HydroDispatch, HydroDispatchRunOfRiver) +# ED = DecisionModel( +# EconomicDispatchProblem, +# template, +# sys; +# optimizer = ipopt_optimizer, +# ) +# @test build!(ED; output_dir = mktempdir(; cleanup = true)) == +# ModelBuildStatus.BUILT +# psi_checksolve_test( +# ED, +# [MOI.OPTIMAL, MOI.LOCALLY_SOLVED], +# test_outputs[net], +# 1000, +# ) +# end +# end +# end + +# @testset "Solving ED Hydro System using Commitment Run of River" begin +# sys = PSB.build_system(PSITestSystems, "c_sys5_hy") +# net = CopperPlatePowerModel + +# template = get_thermal_dispatch_template_network(net) +# set_device_model!(template, HydroDispatch, HydroCommitmentRunOfRiver) + +# @testset "HydroRoR ED model $(net)" begin +# ED = +# DecisionModel(UnitCommitmentProblem, template, sys; optimizer = HiGHS_optimizer) +# @test build!(ED; output_dir = mktempdir(; cleanup = true)) == +# ModelBuildStatus.BUILT +# psi_checksolve_test(ED, [MOI.OPTIMAL, MOI.LOCALLY_SOLVED], 135382.38, 1000) +# end +# end + +# @testset "Test Reserves from Hydro with RunOfRiver" begin +# template = OperationsProblemTemplate(CopperPlatePowerModel) +# set_device_model!(template, PowerLoad, StaticPowerLoad) +# set_device_model!(template, HydroTurbine, HydroDispatchRunOfRiver) +# set_service_model!( +# template, +# ServiceModel(VariableReserve{ReserveUp}, RangeReserve, "Reserve5"), +# ) +# set_service_model!( +# template, +# ServiceModel(VariableReserve{ReserveDown}, RangeReserve, "Reserve6"), +# ) +# set_service_model!( +# template, +# ServiceModel(ReserveDemandCurve{ReserveUp}, StepwiseCostReserve, "ORDC1"), +# ) + +# c_sys5_hyd = PSB.build_system( +# PSITestSystems, +# "c_sys5_hyd"; +# add_reserves = true, +# force_build = true, +# ) +# model = DecisionModel(template, c_sys5_hyd) +# @test build!(model; output_dir = mktempdir(; cleanup = true)) == +# ModelBuildStatus.BUILT +# # The value of this test needs to be revised +# # moi_tests(model, 240, 0, 48, 96, 72, false) +# end + +# ######################################### +# ###### RESERVOIR SYSTEM TESTS ########### +# ######################################### + +# @testset "Solving ED Hydro System using Dispatch with Reservoir" begin +# sys = PSB.build_system(PSITestSystems, "c_sys5_hy_turbine_energy") +# networks = [ACPPowerModel, DCPPowerModel] +# turbine_model = DeviceModel(HydroTurbine, HydroTurbineEnergyDispatch) +# models = [ +# DeviceModel( +# HydroReservoir, +# HydroEnergyModelReservoir; +# attributes = Dict{String, Any}( +# "energy_target" => true, +# "hydro_budget" => false, +# ), +# ), +# DeviceModel( +# HydroReservoir, +# HydroEnergyModelReservoir; +# attributes = Dict{String, Any}( +# "energy_target" => false, +# "hydro_budget" => true, +# ), +# ), +# ] +# test_outputs = Dict{Any, Float64}( +# (ACPPowerModel, HydroEnergyModelReservoir, true) => 136296.0, +# (DCPPowerModel, HydroEnergyModelReservoir, true) => 135404.0, +# (ACPPowerModel, HydroEnergyModelReservoir, false) => 131305.0, +# (DCPPowerModel, HydroEnergyModelReservoir, false) => 130479.0, +# ) + +# for net in networks +# for reservoir_model in models +# formulation = get_formulation(reservoir_model) +# attrs = IOM.get_attributes(reservoir_model) +# energy_target = get(attrs, "energy_target", false) +# hydro_budget = get(attrs, "hydro_budget", false) +# @testset "$(formulation) with energy_target: $energy_target, and hydro_budget: $hydro_budget, ED model on $(net)" begin +# template = get_thermal_dispatch_template_network(net) +# set_device_model!(template, turbine_model) +# set_device_model!(template, reservoir_model) + +# ED = DecisionModel( +# EconomicDispatchProblem, +# template, +# sys; +# optimizer = ipopt_optimizer, +# ) +# @test build!(ED; output_dir = mktempdir(; cleanup = true)) == +# ModelBuildStatus.BUILT +# psi_checksolve_test( +# ED, +# [MOI.OPTIMAL, MOI.LOCALLY_SOLVED], +# test_outputs[(net, formulation, energy_target)], +# 1000, +# ) +# end +# end +# end +# end + +# @testset "Solving ED Hydro System using Commitment with Reservoir" begin +# sys = PSB.build_system(PSITestSystems, "c_sys5_hy_turbine_energy") +# net = DCPPowerModel +# turbine_model = DeviceModel(HydroTurbine, HydroTurbineEnergyCommitment) +# models = [ +# DeviceModel( +# HydroReservoir, +# HydroEnergyModelReservoir; +# attributes = Dict{String, Any}( +# "energy_target" => true, +# "hydro_budget" => false, +# ), +# ), +# DeviceModel( +# HydroReservoir, +# HydroEnergyModelReservoir; +# attributes = Dict{String, Any}( +# "energy_target" => false, +# "hydro_budget" => true, +# ), +# ), +# ] +# test_outputs = Dict{Any, Float64}( +# (DCPPowerModel, HydroEnergyModelReservoir, true) => 141061.0, +# (DCPPowerModel, HydroEnergyModelReservoir, false) => 144109.0, +# ) + +# for reservoir_model in models +# formulation = get_formulation(reservoir_model) +# attrs = IOM.get_attributes(reservoir_model) +# energy_target = get(attrs, "energy_target", false) +# hydro_budget = get(attrs, "hydro_budget", false) +# @testset "$(formulation) with energy_target: $energy_target, and hydro_budget: $hydro_budget, ED model on $(net)" begin +# template = get_thermal_dispatch_template_network(net) +# set_device_model!(template, turbine_model) +# set_device_model!(template, reservoir_model) + +# ED = DecisionModel( +# EconomicDispatchProblem, +# template, +# sys; +# optimizer = HiGHS_optimizer, +# ) +# @test build!(ED; output_dir = mktempdir(; cleanup = true)) == +# ModelBuildStatus.BUILT +# psi_checksolve_test( +# ED, +# [MOI.OPTIMAL, MOI.LOCALLY_SOLVED], +# test_outputs[(net, formulation, energy_target)], +# 10000, #update to 10k to handle the difference in Mac and Ubuntu. Likely a HiGHS issue. +# ) +# end +# end +# end + +# ######################################################## +# ####### Hydro DISPATCH RUN OF RIVER BUDGET TEST ######## +# ######################################################## +# @testset "Test Hydro Dispatch Run Of River Formulations " begin +# device_model = DeviceModel(HydroDispatch, HydroDispatchRunOfRiverBudget; +# use_slacks = true, attributes = Dict("hydro_budget_interval" => Hour(24))) + +# sys = PSB.build_system(PSITestSystems, "c_sys5_hy"; add_single_time_series = true) +# hy = only(get_components(HydroDispatch, sys)) +# max_power = get_max_active_power(hy) +# resolution = Dates.Hour(1) +# tstamp = range(DateTime("2024-01-01T00:00:00"); step = resolution, length = 48) +# data = ones(length(tstamp)) / (get_base_power(sys) * max_power) +# ts = SingleTimeSeries("hydro_budget", TimeArray(tstamp, data)) +# add_time_series!(sys, hy, ts) +# transform_single_time_series!(sys, Hour(24), Hour(24)) + +# model = DecisionModel(MockOperationProblem, CopperPlatePowerModel, sys) +# mock_construct_device!(model, device_model) +# moi_tests(model, 48, 0, 50, 24, 0, false) +# psi_checkobjfun_test(model, GAEVF) +# end + +# @testset "Solve Hydro Dispatch Run Of River" begin +# output_dir = mktempdir(; cleanup = true) + +# c_sys5_hy = PSB.build_system(PSITestSystems, "c_sys5_hy"; add_single_time_series = true) + +# hydro_budget = 24 +# eps = 1e-6 + +# hy = only(get_components(HydroDispatch, c_sys5_hy)) +# max_power = get_max_active_power(hy) + +# tstamp = range(DateTime("2024-01-01T00:00:00"); step = Dates.Hour(1), length = 48) +# data = ones(length(tstamp)) / (get_base_power(c_sys5_hy) * max_power) +# ts = SingleTimeSeries("hydro_budget", TimeArray(tstamp, data)) +# add_time_series!(c_sys5_hy, first(get_components(HydroDispatch, c_sys5_hy)), ts) +# #remove_time_series!(c_sys5_hy, Deterministic) +# transform_single_time_series!(c_sys5_hy, Hour(24), Hour(24)) + +# template_uc = OperationsProblemTemplate() +# set_device_model!(template_uc, ThermalStandard, ThermalBasicUnitCommitment) +# set_device_model!(template_uc, RenewableDispatch, RenewableFullDispatch) +# set_device_model!(template_uc, PowerLoad, StaticPowerLoad) +# set_device_model!(template_uc, RenewableNonDispatch, FixedOutput) +# set_device_model!( +# template_uc, +# DeviceModel(HydroDispatch, HydroDispatchRunOfRiverBudget; +# attributes = Dict("hydro_budget_interval" => Hour(hydro_budget))), +# ) +# model = DecisionModel( +# template_uc, +# c_sys5_hy; +# optimizer = HiGHS_optimizer, +# store_variable_names = true, +# ) + +# @test build!(model; output_dir = output_dir) == +# ModelBuildStatus.BUILT + +# @test solve!(model; output_dir = output_dir) == +# IS.Simulation.RunStatus.SUCCESSFULLY_FINALIZED + +# outputs = OptimizationProblemOutputs(model) +# df = read_variable(outputs, "ActivePowerVariable__HydroDispatch") +# hydro_power_sum = +# sum(df[!, :value]) + +# @test abs(hydro_power_sum - hydro_budget) <= eps +# end + +# @testset "Make Hydro Dispatch Run Of River with Reserves" begin +# output_dir = mktempdir(; cleanup = true) + +# c_sys5_hy = PSB.build_system( +# PSITestSystems, +# "c_sys5_hy"; +# add_single_time_series = true, +# add_reserves = true, +# ) + +# # Fix reserve parameters +# reg_up = only(get_components(VariableReserve{ReserveUp}, c_sys5_hy)) +# reg_dn = only(get_components(VariableReserve{ReserveDown}, c_sys5_hy)) +# set_deployed_fraction!(reg_up, 0.0) +# set_deployed_fraction!(reg_dn, 0.0) +# set_requirement!(reg_up, 0.01) +# set_requirement!(reg_dn, 0.01) + +# hydro_budget = 24 +# eps = 1e-6 + +# hy = only(get_components(HydroDispatch, c_sys5_hy)) + +# # Update Service allocation +# # Remove reg up from hydro, but leave reg dn +# remove_service!(hy, reg_up) + +# # Add reg up to thermals +# for th in get_components(ThermalStandard, c_sys5_hy) +# add_service!(th, reg_up, c_sys5_hy) +# end + +# max_power = get_max_active_power(hy) +# tstamp = range(DateTime("2024-01-01T00:00:00"); step = Dates.Hour(1), length = 48) +# data = ones(length(tstamp)) / (get_base_power(c_sys5_hy) * max_power) +# ts = SingleTimeSeries("hydro_budget", TimeArray(tstamp, data)) +# add_time_series!(c_sys5_hy, first(get_components(HydroDispatch, c_sys5_hy)), ts) + +# ## add extra hydro budget +# hy_copy = HydroDispatch(; +# name = "HydroDispatchCopy", +# available = get_available(hy), +# bus = get_bus(hy), +# active_power = get_active_power(hy), +# reactive_power = get_reactive_power(hy), +# rating = get_rating(hy), +# prime_mover_type = get_prime_mover_type(hy), +# active_power_limits = get_active_power_limits(hy), +# reactive_power_limits = get_reactive_power_limits(hy), +# ramp_limits = get_ramp_limits(hy), +# time_limits = get_time_limits(hy), +# base_power = get_base_power(hy), +# ) +# add_component!(c_sys5_hy, hy_copy) +# copy_time_series!(hy_copy, hy) + +# transform_single_time_series!(c_sys5_hy, Hour(24), Hour(24)) + +# template_uc = OperationsProblemTemplate() +# set_device_model!(template_uc, ThermalStandard, ThermalBasicUnitCommitment) +# set_device_model!(template_uc, RenewableDispatch, RenewableFullDispatch) +# set_device_model!(template_uc, PowerLoad, StaticPowerLoad) +# set_device_model!(template_uc, RenewableNonDispatch, FixedOutput) +# set_device_model!( +# template_uc, +# DeviceModel(HydroDispatch, HydroDispatchRunOfRiverBudget; +# attributes = Dict("hydro_budget_interval" => Hour(hydro_budget))), +# ) +# set_service_model!(template_uc, VariableReserve{ReserveUp}, RangeReserve) +# set_service_model!(template_uc, VariableReserve{ReserveDown}, RangeReserve) +# model = DecisionModel( +# template_uc, +# c_sys5_hy; +# optimizer = HiGHS_optimizer, +# store_variable_names = true, +# ) + +# @test build!(model; output_dir = output_dir) == +# ModelBuildStatus.BUILT + +# @test solve!(model; output_dir = output_dir) == +# IS.Simulation.RunStatus.SUCCESSFULLY_FINALIZED +# end + +# ################################################ +# ####### Hydro PUMP ENERGY DISPATCH TEST ######## +# ################################################ +# @testset "Test Hydro Pump Energy Dispatch Formulations " begin +# device_model = DeviceModel( +# HydroPumpTurbine, +# HydroPumpEnergyDispatch; +# attributes = Dict{String, Any}( +# "reservation" => true, +# "energy_target" => true, +# ), +# ) + +# c_sys5_bat = +# PSB.build_system( +# PSITestSystems, +# "c_sys5_hydro_pump_energy"; +# add_reserves = true, +# add_single_time_series = true, +# ) + +# hy_pump = first(PSY.get_components(HydroPumpTurbine, c_sys5_bat)) +# transform_single_time_series!(c_sys5_bat, Hour(24), Hour(24)) + +# model = DecisionModel(MockOperationProblem, CopperPlatePowerModel, c_sys5_bat) +# mock_construct_device!(model, device_model) +# moi_tests(model, 72, 0, 48, 24, 0, true) +# psi_checkobjfun_test(model, GAEVF) +# end + +# @testset "Test Hydro Pump Energy Dispatch Formulations 2" begin +# output_dir = mktempdir(; cleanup = true) + +# c_sys5_bat = +# PSB.build_system( +# PSITestSystems, +# "c_sys5_hydro_pump_energy"; +# add_reserves = true, +# add_single_time_series = true, +# ) + +# hy_pump = first(PSY.get_components(HydroPumpTurbine, c_sys5_bat)) + +# transform_single_time_series!(c_sys5_bat, Hour(24), Hour(24)) + +# template_uc = OperationsProblemTemplate() +# set_device_model!(template_uc, ThermalStandard, ThermalBasicUnitCommitment) +# set_device_model!(template_uc, RenewableDispatch, RenewableFullDispatch) +# set_device_model!(template_uc, PowerLoad, StaticPowerLoad) +# set_device_model!(template_uc, RenewableNonDispatch, FixedOutput) +# set_device_model!( +# template_uc, +# DeviceModel( +# HydroPumpTurbine, +# HydroPumpEnergyDispatch; +# attributes = Dict{String, Any}( +# "reservation" => true, +# "energy_target" => true, +# ), +# ), +# ) + +# model = DecisionModel( +# template_uc, +# c_sys5_bat; +# optimizer = HiGHS_optimizer, +# store_variable_names = true, +# ) + +# @test build!(model; output_dir = output_dir) == +# ModelBuildStatus.BUILT + +# @test solve!(model; output_dir = output_dir) == +# IS.Simulation.RunStatus.SUCCESSFULLY_FINALIZED +# end + +# ######################################### +# ######## HydroBlock model Tests ######### +# ######################################### + +# @testset "Test Hydro Block Optimization Formulation" begin +# output_dir = mktempdir(; cleanup = true) +# modeling_horizon = 3 * 24 * 1 + +# sys = PSB.build_system(PSITestSystems, "c_sys5_hy_turbine_energy") +# res = first(PSY.get_components(HydroReservoir, sys)) + +# set_head_to_volume_factor!(res, LinearCurve(1.0)) +# set_storage_level_limits!(res, (min = 4000, max = 6000)) +# set_level_targets!(res, 0.9) +# template_ed = OperationsProblemTemplate( +# NetworkModel( +# CopperPlatePowerModel; +# ), +# ) + +# set_device_model!(template_ed, ThermalStandard, ThermalBasicDispatch) +# set_device_model!(template_ed, PowerLoad, StaticPowerLoad) +# set_device_model!(template_ed, HydroReservoir, HydroWaterFactorModel) +# set_device_model!(template_ed, HydroTurbine, HydroWaterFactorModel) + +# model = DecisionModel( +# template_ed, +# sys; +# name = "ED", +# optimizer = ipopt_optimizer, +# optimizer_solve_log_print = true, +# store_variable_names = true, +# horizon = Hour(24), +# ) + +# @test build!(model; output_dir = output_dir) == +# ModelBuildStatus.BUILT + +# @test solve!(model; optimizer = ipopt_optimizer, output_dir = output_dir) == +# IS.Simulation.RunStatus.SUCCESSFULLY_FINALIZED + +# outputs = OptimizationProblemOutputs(model) +# power_load = read_parameter(outputs, "ActivePowerTimeSeriesParameter__PowerLoad") +# reservoir_inflow = read_parameter(outputs, "InflowTimeSeriesParameter__HydroReservoir") + +# water_spillage = read_variable(outputs, "WaterSpillageVariable__HydroReservoir") +# thermal_power = read_variable(outputs, "ActivePowerVariable__ThermalStandard") +# hydro_power = read_variable(outputs, "ActivePowerVariable__HydroTurbine") + +# turbine_output = read_aux_variable(outputs, "HydroEnergyOutput__HydroTurbine") +# reservoir_volume = +# read_variable(outputs, "HydroReservoirVolumeVariable__HydroReservoir") + +# var = read_variable( +# outputs, +# "HydroReservoirVolumeVariable__HydroReservoir"; +# table_format = TableFormat.WIDE, +# ) + +# # check the second step is equal to the first step + dispatch +# end + +# ################################################ +# ######## HydroWaterModelReservoir TEST ######### +# ################################################ + +# @testset "Solve HydroWaterModelReservoir" begin +# output_dir = mktempdir(; cleanup = true) + +# c_sys5_hy = PSB.build_system(PSITestSystems, "c_sys5_hy_turbine_head") +# reservoir = only(get_components(HydroReservoir, c_sys5_hy)) +# hydro_inflow_ts = get_time_series_array(Deterministic, reservoir, "inflow") + +# template = OperationsProblemTemplate() +# set_device_model!(template, HydroTurbine, HydroTurbineBilinearDispatch) +# set_device_model!(template, HydroReservoir, HydroWaterModelReservoir) + +# set_device_model!(template, ThermalStandard, ThermalDispatchNoMin) +# set_device_model!(template, PowerLoad, StaticPowerLoad) + +# model = DecisionModel( +# template, +# c_sys5_hy; +# optimizer = ipopt_optimizer, +# store_variable_names = true, +# ) + +# @test build!(model; output_dir = output_dir) == +# ModelBuildStatus.BUILT + +# @test solve!(model; output_dir = output_dir) == +# IS.Simulation.RunStatus.SUCCESSFULLY_FINALIZED + +# outputs = OptimizationProblemOutputs(model) + +# moi_tests(model, 288, 0, 168, 168, 72, false) +# psi_checkobjfun_test(model, AffExpr) + +# df_outflow = read_expression(outputs, "TotalHydroFlowRateTurbineOutgoing__HydroTurbine") +# hydro_vol_df = +# read_variables(outputs, [(HydroReservoirVolumeVariable, HydroReservoir)])["HydroReservoirVolumeVariable__HydroReservoir"] +# hydro_head_df = +# read_variables(outputs, [(HydroReservoirHeadVariable, HydroReservoir)])["HydroReservoirHeadVariable__HydroReservoir"] +# hydro_spillage_df = +# read_variables(outputs, [(WaterSpillageVariable, HydroReservoir)])["WaterSpillageVariable__HydroReservoir"] +# hydro_inflow_df = +# read_parameters(outputs, [(InflowTimeSeriesParameter, HydroReservoir)])["InflowTimeSeriesParameter__HydroReservoir"] + +# total_inflow = sum(values(hydro_inflow_ts)) +# total_outflow = sum(df_outflow[!, :value]) +# total_spillage = sum(hydro_spillage_df[!, :value]) + +# calculated_vf = +# (hydro_vol_df[1, :value]) + +# ((total_inflow - total_outflow - total_spillage) * 3600 * 1e-9) + +# @test abs(calculated_vf - hydro_vol_df[end, :value]) <= 1e-4 + +# psi_checksolve_test( +# model, +# [MOI.OPTIMAL, MOI.ALMOST_OPTIMAL, MOI.LOCALLY_SOLVED], +# 210949.49, +# 1000, +# ) +# end + +@testset "Solve HydroWaterModelReservoir with bilinear approximations" begin output_dir = mktempdir(; cleanup = true) c_sys5_hy = PSB.build_system(PSITestSystems, "c_sys5_hy_turbine_head") @@ -639,7 +699,7 @@ end hydro_inflow_ts = get_time_series_array(Deterministic, reservoir, "inflow") template = OperationsProblemTemplate() - set_device_model!(template, HydroTurbine, HydroTurbineBilinearDispatch) + set_device_model!(template, HydroTurbine, HydroTurbineBin2BilinearDispatch) set_device_model!(template, HydroReservoir, HydroWaterModelReservoir) set_device_model!(template, ThermalStandard, ThermalDispatchNoMin) @@ -648,7 +708,7 @@ end model = DecisionModel( template, c_sys5_hy; - optimizer = ipopt_optimizer, + optimizer = HiGHS_optimizer, store_variable_names = true, ) @@ -691,223 +751,223 @@ end ) end -@testset "Solve HydroWaterModelReservoir with Budget" begin - sys = PSB.build_system( - PSITestSystems, - "c_sys5_hy_turbine_head"; - force_build = true, - add_single_time_series = true, - ) - res = only(get_components(HydroReservoir, sys)) - inflow_array = get_time_series_array(SingleTimeSeries, res, "inflow") - tstamp = timestamp(inflow_array) - vals = values(inflow_array) - budget_array = TimeArray(tstamp, vals .* 0.5) - budget_ts = SingleTimeSeries("hydro_budget", budget_array) - add_time_series!(sys, res, budget_ts) - transform_single_time_series!(sys, Hour(24), Hour(24)) - - template = OperationsProblemTemplate(NetworkModel(CopperPlatePowerModel)) - set_device_model!(template, ThermalStandard, ThermalDispatchNoMin) - set_device_model!(template, PowerLoad, StaticPowerLoad) - set_device_model!(template, HydroTurbine, HydroTurbineWaterLinearDispatch) - reservoir_model = DeviceModel( - HydroReservoir, - HydroWaterModelReservoir; - attributes = Dict("hydro_target" => false, "hydro_budget" => true), - ) - set_device_model!(template, reservoir_model) - - model = DecisionModel( - template, - sys; - name = "UC", - optimizer = HiGHS_optimizer, - store_variable_names = true, - optimizer_solve_log_print = false, - ) - @test build!(model; output_dir = mktempdir()) == ModelBuildStatus.BUILT - @test solve!(model) == IS.Simulation.RunStatus.SUCCESSFULLY_FINALIZED - - sol = OptimizationProblemOutputs(model) - flow = read_expression(sol, "TotalHydroFlowRateReservoirOutgoing__HydroReservoir")[ - !, - "value", - ] - @test sum(flow) <= sum(vals) / 4.0 -end - -@testset "Solve HydroWaterModelReservoir with Target" begin - sys = PSB.build_system( - PSITestSystems, - "c_sys5_hy_turbine_head"; - force_build = true, - add_single_time_series = true, - ) - res = only(get_components(HydroReservoir, sys)) - hydro_cost = HydroReservoirCost(1e5, 0.0, 0.0) - set_operation_cost!(res, hydro_cost) - inflow_array = get_time_series_array(SingleTimeSeries, res, "inflow") - tstamp = timestamp(inflow_array) - vals = values(inflow_array) - head_array = TimeArray(tstamp, 490.0 * ones(length(vals))) - target_ts = SingleTimeSeries("hydro_target", head_array) - add_time_series!(sys, res, target_ts) - transform_single_time_series!(sys, Hour(24), Hour(24)) - - template = OperationsProblemTemplate(NetworkModel(CopperPlatePowerModel)) - set_device_model!(template, ThermalStandard, ThermalDispatchNoMin) - set_device_model!(template, PowerLoad, StaticPowerLoad) - set_device_model!(template, HydroTurbine, HydroTurbineWaterLinearDispatch) - reservoir_model = DeviceModel( - HydroReservoir, - HydroWaterModelReservoir; - attributes = Dict("hydro_target" => true, "hydro_budget" => false), - ) - set_device_model!(template, reservoir_model) - - model = DecisionModel( - template, - sys; - name = "UC", - optimizer = HiGHS_optimizer, - store_variable_names = true, - optimizer_solve_log_print = false, - ) - @test build!(model; output_dir = mktempdir()) == ModelBuildStatus.BUILT - @test solve!(model) == IS.Simulation.RunStatus.SUCCESSFULLY_FINALIZED - - sol = OptimizationProblemOutputs(model) - head = read_variable(sol, "HydroReservoirHeadVariable__HydroReservoir")[!, "value"] - @test head[24] >= 490 -end - -##################################################### -######## HydroWaterModelReservoir Cascading ######### -##################################################### - -@testset "Solve Cascading HydroWaterModelReservoir" begin - output_dir = mktempdir(; cleanup = true) - - c_sys5_hy = PSB.build_system(PSITestSystems, "c_sys5_hy_cascading_turbine_head") - - template = OperationsProblemTemplate() - set_device_model!(template, HydroTurbine, HydroTurbineBilinearDispatch) - set_device_model!(template, HydroReservoir, HydroWaterModelReservoir) - - set_device_model!(template, ThermalStandard, ThermalDispatchNoMin) - set_device_model!(template, PowerLoad, StaticPowerLoad) - - model = DecisionModel( - template, - c_sys5_hy; - optimizer = ipopt_optimizer, - store_variable_names = true, - ) - - @test build!(model; output_dir = output_dir) == - ModelBuildStatus.BUILT - - @test solve!(model; output_dir = output_dir) == - IS.Simulation.RunStatus.SUCCESSFULLY_FINALIZED - - outputs = OptimizationProblemOutputs(model) - - moi_tests(model, 504, 0, 216, 216, 120, false) - psi_checkobjfun_test(model, AffExpr) -end - -@testset "Solve Cascading HydroEnergyModelReservoir" begin - output_dir = mktempdir(; cleanup = true) - - c_sys5_hy = PSB.build_system(PSITestSystems, "c_sys5_hy_cascading_turbine_head") - - template = OperationsProblemTemplate() - set_device_model!(template, HydroTurbine, HydroTurbineEnergyDispatch) - set_device_model!(template, HydroReservoir, HydroEnergyModelReservoir) - - set_device_model!(template, ThermalStandard, ThermalDispatchNoMin) - set_device_model!(template, PowerLoad, StaticPowerLoad) - - model = DecisionModel( - template, - c_sys5_hy; - optimizer = HiGHS_optimizer, - store_variable_names = true, - ) - - @test build!(model; output_dir = output_dir) == - ModelBuildStatus.BUILT - - @test solve!(model; output_dir = output_dir) == - IS.Simulation.RunStatus.SUCCESSFULLY_FINALIZED - - outputs = OptimizationProblemOutputs(model) - - moi_tests(model, 360, 0, 168, 168, 72, false) - psi_checkobjfun_test(model, AffExpr) -end - -################################################################### -######## Energy HydroPump and Turbine in same Reservoir ######### -################################################################### - -@testset "Solve Energy model with both Turbine and Reservoir" begin - sys = build_hydro_with_both_pump_and_turbine() - template = OperationsProblemTemplate() - set_device_model!(template, HydroTurbine, HydroTurbineEnergyDispatch) - set_device_model!(template, HydroTurbine, HydroTurbineEnergyCommitment) - res_model = DeviceModel( - HydroReservoir, - HydroEnergyModelReservoir; - use_slacks = true, - attributes = Dict{String, Any}( - "energy_target" => false, - "hydro_budget" => true, - ), - ) - set_device_model!(template, res_model) - set_device_model!(template, HydroPumpTurbine, HydroPumpEnergyDispatch) - p_model = DeviceModel( - HydroPumpTurbine, - HydroPumpEnergyCommitment; - attributes = Dict{String, Any}( - "reservation" => true, - ), - ) - set_device_model!(template, p_model) - - set_device_model!(template, ThermalStandard, ThermalDispatchNoMin) - set_device_model!(template, PowerLoad, StaticPowerLoad) - - model = DecisionModel( - template, - sys; - optimizer = HiGHS_optimizer, - store_variable_names = true, - calculate_conflict = true, - ) - - @test build!(model; output_dir = mktempdir()) == ModelBuildStatus.BUILT - @test solve!(model) == IS.Simulation.RunStatus.SUCCESSFULLY_FINALIZED - - p_model = DeviceModel( - HydroPumpTurbine, - HydroPumpEnergyCommitment; - attributes = Dict{String, Any}( - "reservation" => false, - ), - ) - set_device_model!(template, p_model) - - model = DecisionModel( - template, - sys; - optimizer = HiGHS_optimizer, - store_variable_names = true, - calculate_conflict = true, - ) - - @test build!(model; output_dir = mktempdir()) == ModelBuildStatus.BUILT - @test solve!(model) == IS.Simulation.RunStatus.SUCCESSFULLY_FINALIZED -end +# @testset "Solve HydroWaterModelReservoir with Budget" begin +# sys = PSB.build_system( +# PSITestSystems, +# "c_sys5_hy_turbine_head"; +# force_build = true, +# add_single_time_series = true, +# ) +# res = only(get_components(HydroReservoir, sys)) +# inflow_array = get_time_series_array(SingleTimeSeries, res, "inflow") +# tstamp = timestamp(inflow_array) +# vals = values(inflow_array) +# budget_array = TimeArray(tstamp, vals .* 0.5) +# budget_ts = SingleTimeSeries("hydro_budget", budget_array) +# add_time_series!(sys, res, budget_ts) +# transform_single_time_series!(sys, Hour(24), Hour(24)) + +# template = OperationsProblemTemplate(NetworkModel(CopperPlatePowerModel)) +# set_device_model!(template, ThermalStandard, ThermalDispatchNoMin) +# set_device_model!(template, PowerLoad, StaticPowerLoad) +# set_device_model!(template, HydroTurbine, HydroTurbineWaterLinearDispatch) +# reservoir_model = DeviceModel( +# HydroReservoir, +# HydroWaterModelReservoir; +# attributes = Dict("hydro_target" => false, "hydro_budget" => true), +# ) +# set_device_model!(template, reservoir_model) + +# model = DecisionModel( +# template, +# sys; +# name = "UC", +# optimizer = HiGHS_optimizer, +# store_variable_names = true, +# optimizer_solve_log_print = false, +# ) +# @test build!(model; output_dir = mktempdir()) == ModelBuildStatus.BUILT +# @test solve!(model) == IS.Simulation.RunStatus.SUCCESSFULLY_FINALIZED + +# sol = OptimizationProblemOutputs(model) +# flow = read_expression(sol, "TotalHydroFlowRateReservoirOutgoing__HydroReservoir")[ +# !, +# "value", +# ] +# @test sum(flow) <= sum(vals) / 4.0 +# end + +# @testset "Solve HydroWaterModelReservoir with Target" begin +# sys = PSB.build_system( +# PSITestSystems, +# "c_sys5_hy_turbine_head"; +# force_build = true, +# add_single_time_series = true, +# ) +# res = only(get_components(HydroReservoir, sys)) +# hydro_cost = HydroReservoirCost(1e5, 0.0, 0.0) +# set_operation_cost!(res, hydro_cost) +# inflow_array = get_time_series_array(SingleTimeSeries, res, "inflow") +# tstamp = timestamp(inflow_array) +# vals = values(inflow_array) +# head_array = TimeArray(tstamp, 490.0 * ones(length(vals))) +# target_ts = SingleTimeSeries("hydro_target", head_array) +# add_time_series!(sys, res, target_ts) +# transform_single_time_series!(sys, Hour(24), Hour(24)) + +# template = OperationsProblemTemplate(NetworkModel(CopperPlatePowerModel)) +# set_device_model!(template, ThermalStandard, ThermalDispatchNoMin) +# set_device_model!(template, PowerLoad, StaticPowerLoad) +# set_device_model!(template, HydroTurbine, HydroTurbineWaterLinearDispatch) +# reservoir_model = DeviceModel( +# HydroReservoir, +# HydroWaterModelReservoir; +# attributes = Dict("hydro_target" => true, "hydro_budget" => false), +# ) +# set_device_model!(template, reservoir_model) + +# model = DecisionModel( +# template, +# sys; +# name = "UC", +# optimizer = HiGHS_optimizer, +# store_variable_names = true, +# optimizer_solve_log_print = false, +# ) +# @test build!(model; output_dir = mktempdir()) == ModelBuildStatus.BUILT +# @test solve!(model) == IS.Simulation.RunStatus.SUCCESSFULLY_FINALIZED + +# sol = OptimizationProblemOutputs(model) +# head = read_variable(sol, "HydroReservoirHeadVariable__HydroReservoir")[!, "value"] +# @test head[24] >= 490 +# end + +# ##################################################### +# ######## HydroWaterModelReservoir Cascading ######### +# ##################################################### + +# @testset "Solve Cascading HydroWaterModelReservoir" begin +# output_dir = mktempdir(; cleanup = true) + +# c_sys5_hy = PSB.build_system(PSITestSystems, "c_sys5_hy_cascading_turbine_head") + +# template = OperationsProblemTemplate() +# set_device_model!(template, HydroTurbine, HydroTurbineBilinearDispatch) +# set_device_model!(template, HydroReservoir, HydroWaterModelReservoir) + +# set_device_model!(template, ThermalStandard, ThermalDispatchNoMin) +# set_device_model!(template, PowerLoad, StaticPowerLoad) + +# model = DecisionModel( +# template, +# c_sys5_hy; +# optimizer = ipopt_optimizer, +# store_variable_names = true, +# ) + +# @test build!(model; output_dir = output_dir) == +# ModelBuildStatus.BUILT + +# @test solve!(model; output_dir = output_dir) == +# IS.Simulation.RunStatus.SUCCESSFULLY_FINALIZED + +# outputs = OptimizationProblemOutputs(model) + +# moi_tests(model, 504, 0, 216, 216, 120, false) +# psi_checkobjfun_test(model, AffExpr) +# end + +# @testset "Solve Cascading HydroEnergyModelReservoir" begin +# output_dir = mktempdir(; cleanup = true) + +# c_sys5_hy = PSB.build_system(PSITestSystems, "c_sys5_hy_cascading_turbine_head") + +# template = OperationsProblemTemplate() +# set_device_model!(template, HydroTurbine, HydroTurbineEnergyDispatch) +# set_device_model!(template, HydroReservoir, HydroEnergyModelReservoir) + +# set_device_model!(template, ThermalStandard, ThermalDispatchNoMin) +# set_device_model!(template, PowerLoad, StaticPowerLoad) + +# model = DecisionModel( +# template, +# c_sys5_hy; +# optimizer = HiGHS_optimizer, +# store_variable_names = true, +# ) + +# @test build!(model; output_dir = output_dir) == +# ModelBuildStatus.BUILT + +# @test solve!(model; output_dir = output_dir) == +# IS.Simulation.RunStatus.SUCCESSFULLY_FINALIZED + +# outputs = OptimizationProblemOutputs(model) + +# moi_tests(model, 360, 0, 168, 168, 72, false) +# psi_checkobjfun_test(model, AffExpr) +# end + +# ################################################################### +# ######## Energy HydroPump and Turbine in same Reservoir ######### +# ################################################################### + +# @testset "Solve Energy model with both Turbine and Reservoir" begin +# sys = build_hydro_with_both_pump_and_turbine() +# template = OperationsProblemTemplate() +# set_device_model!(template, HydroTurbine, HydroTurbineEnergyDispatch) +# set_device_model!(template, HydroTurbine, HydroTurbineEnergyCommitment) +# res_model = DeviceModel( +# HydroReservoir, +# HydroEnergyModelReservoir; +# use_slacks = true, +# attributes = Dict{String, Any}( +# "energy_target" => false, +# "hydro_budget" => true, +# ), +# ) +# set_device_model!(template, res_model) +# set_device_model!(template, HydroPumpTurbine, HydroPumpEnergyDispatch) +# p_model = DeviceModel( +# HydroPumpTurbine, +# HydroPumpEnergyCommitment; +# attributes = Dict{String, Any}( +# "reservation" => true, +# ), +# ) +# set_device_model!(template, p_model) + +# set_device_model!(template, ThermalStandard, ThermalDispatchNoMin) +# set_device_model!(template, PowerLoad, StaticPowerLoad) + +# model = DecisionModel( +# template, +# sys; +# optimizer = HiGHS_optimizer, +# store_variable_names = true, +# calculate_conflict = true, +# ) + +# @test build!(model; output_dir = mktempdir()) == ModelBuildStatus.BUILT +# @test solve!(model) == IS.Simulation.RunStatus.SUCCESSFULLY_FINALIZED + +# p_model = DeviceModel( +# HydroPumpTurbine, +# HydroPumpEnergyCommitment; +# attributes = Dict{String, Any}( +# "reservation" => false, +# ), +# ) +# set_device_model!(template, p_model) + +# model = DecisionModel( +# template, +# sys; +# optimizer = HiGHS_optimizer, +# store_variable_names = true, +# calculate_conflict = true, +# ) + +# @test build!(model; output_dir = mktempdir()) == ModelBuildStatus.BUILT +# @test solve!(model) == IS.Simulation.RunStatus.SUCCESSFULLY_FINALIZED +# end