HydroEnergyBlock model tutorial
HydroPowerSimulations.jl is an extension library of PowerSimulations.jl for modeling hydro units. Users are encouraged to review the single-step tutorial in PowerSimulations.jl before this tutorial.
Load packages
julia> using PowerSystemsjulia> using PowerSimulationsjulia> using HydroPowerSimulationsjulia> using PowerSystemCaseBuilderjulia> using Ipopt # solver
Data
PowerSystemCaseBuilder.jl is a helper library that makes it easier to reproduce examples in the documentation and tutorials. Normally you would pass your local files to create the system data instead of calling the function build_system. For more details visit PowerSystemCaseBuilder README
julia> sys = build_system(PSITestSystems, "c_sys5_hy_turbine_energy")[ Info: Deserializing with InMemoryTimeSeriesStorage is currently not supported. Using HDF [ Info: Loaded time series from storage file existing=/home/runner/.julia/packages/PowerSystemCaseBuilder/zW01F/data/serialized_system/4e67b70ea6977dbe21c7731d72cdc1494adf072a7f3f08d921db740cf264ce79/c_sys5_hy_turbine_energy_time_series_storage.h5 new=/tmp/jl_Xti2Jx compression=InfrastructureSystems.CompressionSettings(false, InfrastructureSystems.CompressionTypesModule.CompressionTypes.DEFLATE = 1, 3, true) System ┌───────────────────┬─────────────┐ │ Property │ Value │ ├───────────────────┼─────────────┤ │ Name │ │ │ Description │ │ │ System Units Base │ SYSTEM_BASE │ │ Base Power │ 100.0 │ │ Base Frequency │ 60.0 │ │ Num Components │ 27 │ └───────────────────┴─────────────┘ Static Components ┌─────────────────┬───────┐ │ Type │ Count │ ├─────────────────┼───────┤ │ ACBus │ 5 │ │ Arc │ 6 │ │ HydroReservoir │ 1 │ │ HydroTurbine │ 1 │ │ Line │ 6 │ │ PowerLoad │ 3 │ │ ThermalStandard │ 5 │ └─────────────────┴───────┘ Forecast Summary ┌────────────────┬────────────────┬──────────────────┬──────────────────┬─────── │ owner_type │ owner_category │ name │ time_series_type │ init ⋯ │ String │ String │ String │ String │ Stri ⋯ ├────────────────┼────────────────┼──────────────────┼──────────────────┼─────── │ HydroReservoir │ Component │ hydro_budget │ Deterministic │ 2024 ⋯ │ HydroReservoir │ Component │ inflow │ Deterministic │ 2024 ⋯ │ HydroReservoir │ Component │ storage_target │ Deterministic │ 2024 ⋯ │ HydroTurbine │ Component │ max_active_power │ Deterministic │ 2024 ⋯ │ PowerLoad │ Component │ max_active_power │ Deterministic │ 2024 ⋯ └────────────────┴────────────────┴──────────────────┴──────────────────┴─────── 6 columns omitted
With a single PowerSystems.HydroTurbine connected downstream to a PowerSystems.HydroReservoir:
julia> reservoir = only(get_components(HydroReservoir, sys))HydroReservoir: HydroEnergyReservoir__reservoir: name: HydroEnergyReservoir__reservoir available: true storage_level_limits: (min = 0.0, max = 5000.0) initial_level: 0.5 spillage_limits: nothing inflow: 4.0 outflow: 0.0 level_targets: 1.0 intake_elevation: 0.0 head_to_volume_factor: InfrastructureSystems.LinearCurve(0.0, 0.0) upstream_turbines: 0-element Vector{PowerSystems.HydroUnit} downstream_turbines: 1-element Vector{PowerSystems.HydroUnit} upstream_reservoirs: 0-element Vector{PowerSystems.Device} operation_cost: level_data_type: PowerSystems.ReservoirDataTypeModule.ReservoirDataType.ENERGY = 4 ext: Dict{String, Any}() InfrastructureSystems.SystemUnitsSettings: base_value: 100.0 unit_system: InfrastructureSystems.UnitSystemModule.UnitSystem.SYSTEM_BASE = 0 has_supplemental_attributes: false has_time_series: true
Set the storage level limits using set_storage_level_limits!. Here the limits are set between $4000 and 6000 m^3$ .
julia> set_storage_level_limits!(reservoir, (min = 4000, max = 6000))(min = 4000, max = 6000)
Set the lower bound of reservoir volume using set_level_targets!. Here the level target is set at $0.9 \cdot 6000 = 5400 m^3$.
julia> set_level_targets!(reservoir, 0.9)0.9
Decision Model
Setting up the formulations based on PowerSimulations.jl:
julia> template = ProblemTemplate(CopperPlatePowerModel)Network Model ┌────────────────────┬────────────────────────────────────────┐ │ Network Model │ PowerSimulations.CopperPlatePowerModel │ │ Slacks │ false │ │ PTDF │ false │ │ Duals │ None │ │ HVDC Network Model │ None │ └────────────────────┴────────────────────────────────────────┘ Device Models ┌─────────────┬─────────────┬────────┐ │ Device Type │ Formulation │ Slacks │ └─────────────┴─────────────┴────────┘julia> set_device_model!(template, ThermalStandard, ThermalBasicDispatch)julia> set_device_model!(template, PowerLoad, StaticPowerLoad)
but, now we also include the HydroReservoir and HydroTurbine using HydroWaterFactorModel:
julia> set_device_model!(template, HydroReservoir, HydroWaterFactorModel)julia> set_device_model!(template, HydroTurbine, HydroWaterFactorModel)
With the template properly set-up, we construct, build and solve the optimization problem:
julia> model = DecisionModel(template, sys; optimizer = Ipopt.Optimizer)Network Model ┌────────────────────┬────────────────────────────────────────┐ │ Network Model │ PowerSimulations.CopperPlatePowerModel │ │ Slacks │ false │ │ PTDF │ false │ │ Duals │ None │ │ HVDC Network Model │ None │ └────────────────────┴────────────────────────────────────────┘ Device Models ┌──────────────────────────────┬───────────────────────────────────────┬──────── │ Device Type │ Formulation │ Slack ⋯ ├──────────────────────────────┼───────────────────────────────────────┼──────── │ PowerSystems.ThermalStandard │ PowerSimulations.ThermalBasicDispatch │ false ⋯ │ PowerSystems.HydroReservoir │ HydroWaterFactorModel │ false ⋯ │ PowerSystems.PowerLoad │ PowerSimulations.StaticPowerLoad │ false ⋯ │ PowerSystems.HydroTurbine │ HydroWaterFactorModel │ false ⋯ └──────────────────────────────┴───────────────────────────────────────┴──────── 1 column omittedjulia> build!(model; output_dir = mktempdir())┌ Error: DecisionModel Build Failed │ exception = │ Invalid coefficient Inf on variable _[168]. │ Stacktrace: │ [1] error(s::String) │ @ Base ./error.jl:44 │ [2] _assert_isfinite(a::JuMP.AffExpr) │ @ JuMP ~/.julia/packages/JuMP/0tD10/src/aff_expr.jl:713 │ [3] MathOptInterface.ScalarAffineFunction(a::JuMP.AffExpr) │ @ JuMP ~/.julia/packages/JuMP/0tD10/src/aff_expr.jl:752 │ [4] moi_function │ @ ~/.julia/packages/JuMP/0tD10/src/aff_expr.jl:861 [inlined] │ [5] moi_function │ @ ~/.julia/packages/JuMP/0tD10/src/constraints.jl:760 [inlined] │ [6] add_constraint(model::JuMP.Model, con::JuMP.ScalarConstraint{JuMP.AffExpr, MathOptInterface.Interval{Float64}}, name::String) │ @ JuMP ~/.julia/packages/JuMP/0tD10/src/constraints.jl:1038 │ [7] macro expansion │ @ ~/.julia/packages/JuMP/0tD10/src/macros/@constraint.jl:173 [inlined] │ [8] macro expansion │ @ ~/.julia/packages/JuMP/0tD10/src/macros.jl:419 [inlined] │ [9] add_constraints!(container::PowerSimulations.OptimizationContainer, ::Type{ReservoirLevelTargetConstraint}, devices::InfrastructureSystems.FlattenIteratorWrapper{PowerSystems.HydroReservoir, Vector{Base.ValueIterator}}, model::PowerSimulations.DeviceModel{PowerSystems.HydroReservoir, HydroWaterFactorModel}, ::PowerSimulations.NetworkModel{PowerSimulations.CopperPlatePowerModel}) │ @ HydroPowerSimulations ~/work/HydroPowerSimulations.jl/HydroPowerSimulations.jl/src/hydro_generation.jl:1663 │ [10] construct_device!(container::PowerSimulations.OptimizationContainer, sys::PowerSystems.System, ::InfrastructureSystems.Optimization.ModelConstructStage, model::PowerSimulations.DeviceModel{PowerSystems.HydroReservoir, HydroWaterFactorModel}, network_model::PowerSimulations.NetworkModel{PowerSimulations.CopperPlatePowerModel}) │ @ HydroPowerSimulations ~/work/HydroPowerSimulations.jl/HydroPowerSimulations.jl/src/hydrogeneration_constructor.jl:1505 │ [11] build_impl!(container::PowerSimulations.OptimizationContainer, template::PowerSimulations.ProblemTemplate, sys::PowerSystems.System) │ @ PowerSimulations ~/.julia/packages/PowerSimulations/lEkH9/src/core/optimization_container.jl:776 │ [12] build_model!(model::PowerSimulations.DecisionModel{PowerSimulations.GenericOpProblem}) │ @ PowerSimulations ~/.julia/packages/PowerSimulations/lEkH9/src/operation/decision_model.jl:408 │ [13] build_impl!(model::PowerSimulations.DecisionModel{PowerSimulations.GenericOpProblem}) │ @ PowerSimulations ~/.julia/packages/PowerSimulations/lEkH9/src/operation/decision_model.jl:344 │ [14] (::PowerSimulations.var"#83#84"{PowerSimulations.DecisionModel{PowerSimulations.GenericOpProblem}})() │ @ PowerSimulations ~/.julia/packages/PowerSimulations/lEkH9/src/operation/decision_model.jl:386 │ [15] with_logstate(f::PowerSimulations.var"#83#84"{PowerSimulations.DecisionModel{PowerSimulations.GenericOpProblem}}, logstate::Base.CoreLogging.LogState) │ @ Base.CoreLogging ./logging/logging.jl:542 │ [16] build!(model::PowerSimulations.DecisionModel{PowerSimulations.GenericOpProblem}; output_dir::String, recorders::Vector{Any}, console_level::Base.CoreLogging.LogLevel, file_level::Base.CoreLogging.LogLevel, disable_timer_outputs::Bool) │ @ PowerSimulations ~/.julia/packages/PowerSimulations/lEkH9/src/operation/decision_model.jl:364 │ [17] top-level scope │ @ REPL[2]:1 │ [18] eval(m::Module, e::Any) │ @ Core ./boot.jl:489 │ [19] #67 │ @ ~/.julia/packages/Documenter/AXNMp/src/expander_pipeline.jl:978 [inlined] │ [20] cd(f::Documenter.var"#67#68"{Module}, dir::String) │ @ Base.Filesystem ./file.jl:112 │ [21] (::Documenter.var"#65#66"{Documenter.Page, Module})() │ @ Documenter ~/.julia/packages/Documenter/AXNMp/src/expander_pipeline.jl:977 │ [22] (::IOCapture.var"#12#13"{Type{InterruptException}, Documenter.var"#65#66"{Documenter.Page, Module}, IOContext{Base.PipeEndpoint}, IOContext{Base.PipeEndpoint}, IOContext{Base.PipeEndpoint}, IOContext{Base.PipeEndpoint}})() │ @ IOCapture ~/.julia/packages/IOCapture/MR051/src/IOCapture.jl:170 │ [23] with_logstate(f::IOCapture.var"#12#13"{Type{InterruptException}, Documenter.var"#65#66"{Documenter.Page, Module}, IOContext{Base.PipeEndpoint}, IOContext{Base.PipeEndpoint}, IOContext{Base.PipeEndpoint}, IOContext{Base.PipeEndpoint}}, logstate::Base.CoreLogging.LogState) │ @ Base.CoreLogging ./logging/logging.jl:542 │ [24] with_logger(f::Function, logger::Base.CoreLogging.ConsoleLogger) │ @ Base.CoreLogging ./logging/logging.jl:653 │ [25] capture(f::Documenter.var"#65#66"{Documenter.Page, Module}; rethrow::Type, color::Bool, passthrough::Bool, capture_buffer::IOBuffer, io_context::Vector{Any}) │ @ IOCapture ~/.julia/packages/IOCapture/MR051/src/IOCapture.jl:167 │ [26] runner(::Type{Documenter.Expanders.REPLBlocks}, node::MarkdownAST.Node{Nothing}, page::Documenter.Page, doc::Documenter.Document) │ @ Documenter ~/.julia/packages/Documenter/AXNMp/src/expander_pipeline.jl:976 │ [27] dispatch(::Type{Documenter.Expanders.ExpanderPipeline}, ::MarkdownAST.Node{Nothing}, ::Vararg{Any}) │ @ Documenter.Selectors ~/.julia/packages/Documenter/AXNMp/src/utilities/Selectors.jl:170 │ [28] expand(doc::Documenter.Document) │ @ Documenter ~/.julia/packages/Documenter/AXNMp/src/expander_pipeline.jl:60 │ [29] runner(::Type{Documenter.Builder.ExpandTemplates}, doc::Documenter.Document) │ @ Documenter ~/.julia/packages/Documenter/AXNMp/src/builder_pipeline.jl:224 │ [30] dispatch(::Type{Documenter.Builder.DocumentPipeline}, x::Documenter.Document) │ @ Documenter.Selectors ~/.julia/packages/Documenter/AXNMp/src/utilities/Selectors.jl:170 │ [31] #95 │ @ ~/.julia/packages/Documenter/AXNMp/src/makedocs.jl:283 [inlined] │ [32] withenv(::Documenter.var"#95#96"{Documenter.Document}, ::Pair{String, Nothing}, ::Vararg{Pair{String, Nothing}}) │ @ Base ./env.jl:265 │ [33] #93 │ @ ~/.julia/packages/Documenter/AXNMp/src/makedocs.jl:282 [inlined] │ [34] cd(f::Documenter.var"#93#94"{Documenter.Document}, dir::String) │ @ Base.Filesystem ./file.jl:112 │ [35] makedocs(; debug::Bool, format::Documenter.HTMLWriter.HTML, kwargs::@Kwargs{modules::Vector{Module}, sitename::String, pages::Vector{Any}, plugins::Vector{InterLinks}}) │ @ Documenter ~/.julia/packages/Documenter/AXNMp/src/makedocs.jl:281 │ [36] top-level scope │ @ ~/work/HydroPowerSimulations.jl/HydroPowerSimulations.jl/docs/make.jl:35 │ [37] include(mod::Module, _path::String) │ @ Base ./Base.jl:306 │ [38] exec_options(opts::Base.JLOptions) │ @ Base ./client.jl:317 │ [39] _start() │ @ Base ./client.jl:550 └ @ PowerSimulations ~/.julia/packages/PowerSimulations/lEkH9/src/operation/decision_model.jl:393 InfrastructureSystems.Optimization.ModelBuildStatusModule.ModelBuildStatus.FAILED = 1julia> solve!(model)ERROR: build! of the PowerSimulations.DecisionModel{PowerSimulations.GenericOpProblem} GenericOpProblem failed: InfrastructureSystems.Optimization.ModelBuildStatusModule.ModelBuildStatus.FAILED = 1
Exploring Results
Results can be explored using:
julia> results = OptimizationProblemResults(model)ERROR: problem was not solved successfully: InfrastructureSystems.Simulation.RunStatusModule.RunStatus.INITIALIZED = -1
Use read_variable to read in the dispatch variable results for the hydro:
julia> power = read_variable( results, "ActivePowerVariable__HydroTurbine"; table_format = TableFormat.WIDE, )ERROR: UndefVarError: `results` not defined in `Main` Suggestion: add an appropriate import or assignment. This global was declared but not assigned.
or the volume level of the reservoir
julia> volume = read_variable( results, "HydroReservoirVolumeVariable__HydroReservoir"; table_format = TableFormat.WIDE, )ERROR: UndefVarError: `results` not defined in `Main` Suggestion: add an appropriate import or assignment. This global was declared but not assigned.
Note that the final reservoir level is between the set level target and the maximum storage level.