HydroEnergyBlock model tutorial

Note

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 PowerSystems
julia> using PowerSimulations
julia> using HydroPowerSimulations
julia> using PowerSystemCaseBuilder
julia> using Ipopt # solver

Data

Note

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 omitted
julia> 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 = 1
julia> 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.