Originally Contributed by: Clayton Barrows
PowerSimulations.jl supports the construction and solution of optimal power system
scheduling problems (Operations Problems). Operations problems form the fundamental
building blocks for sequential simulations.
This example shows how to specify and customize a the mathematics that will be applied to the data with
an OperationsProblemTemplate
, build and execute an OperationsProblem
, and access the results.
using SIIPExamples
using PowerSystems
using PowerSimulations
const PSI = PowerSimulations
using PowerSystemCaseBuilder
using Dates
using DataFrames
using Cbc #solver
This data depends upon the RTS-GMLC dataset. Let's
use PowerSystemCaseBuilder.jl to download and build a System
.
sys = build_system(PSITestSystems, "modified_RTS_GMLC_DA_sys")
[ Info: Loaded time series from storage file existing=modified_RTS_GMLC_DA_sys_time_series_storage.h5 new=/var/folders/27/2jr8c7gn4j72fvrg4qt81zrw8w_711/T/jl_6r0qgJ compression=CompressionSettings(false, CompressionTypes.DEFLATE = 1, 3, true) ┌ Warning: Rate 500.0 MW for C31-2 is larger than the max expected in the range of (min = 134.0, max = 145.0). └ @ PowerSystems ~/.julia/packages/PowerSystems/61h6O/src/utils/IO/branchdata_checks.jl:148 ┌ Warning: Rate 175.0 MW for B8 is larger than the max expected in the range of (min = 47.0, max = 52.0). └ @ PowerSystems ~/.julia/packages/PowerSystems/61h6O/src/utils/IO/branchdata_checks.jl:148 ┌ Warning: Rate 500.0 MW for B26 is larger than the max expected in the range of (min = 134.0, max = 145.0). └ @ PowerSystems ~/.julia/packages/PowerSystems/61h6O/src/utils/IO/branchdata_checks.jl:148 ┌ Warning: Rate 500.0 MW for A32-2 is larger than the max expected in the range of (min = 134.0, max = 145.0). └ @ PowerSystems ~/.julia/packages/PowerSystems/61h6O/src/utils/IO/branchdata_checks.jl:148 ┌ Warning: Rate 500.0 MW for CA-1 is larger than the max expected in the range of (min = 134.0, max = 145.0). └ @ PowerSystems ~/.julia/packages/PowerSystems/61h6O/src/utils/IO/branchdata_checks.jl:148 ┌ Warning: Rate 175.0 MW for A5 is larger than the max expected in the range of (min = 47.0, max = 52.0). └ @ PowerSystems ~/.julia/packages/PowerSystems/61h6O/src/utils/IO/branchdata_checks.jl:148 ┌ Warning: Rate 500.0 MW for B34 is larger than the max expected in the range of (min = 134.0, max = 145.0). └ @ PowerSystems ~/.julia/packages/PowerSystems/61h6O/src/utils/IO/branchdata_checks.jl:148 ┌ Warning: Rate 500.0 MW for B19 is larger than the max expected in the range of (min = 134.0, max = 145.0). └ @ PowerSystems ~/.julia/packages/PowerSystems/61h6O/src/utils/IO/branchdata_checks.jl:148 ┌ Warning: Rate 500.0 MW for B31-2 is larger than the max expected in the range of (min = 134.0, max = 145.0). └ @ PowerSystems ~/.julia/packages/PowerSystems/61h6O/src/utils/IO/branchdata_checks.jl:148 ┌ Warning: Rate 500.0 MW for C30 is larger than the max expected in the range of (min = 134.0, max = 145.0). └ @ PowerSystems ~/.julia/packages/PowerSystems/61h6O/src/utils/IO/branchdata_checks.jl:148 ┌ Warning: Rate 500.0 MW for B27 is larger than the max expected in the range of (min = 134.0, max = 145.0). └ @ PowerSystems ~/.julia/packages/PowerSystems/61h6O/src/utils/IO/branchdata_checks.jl:148 ┌ Warning: Rate 500.0 MW for A21 is larger than the max expected in the range of (min = 134.0, max = 145.0). └ @ PowerSystems ~/.julia/packages/PowerSystems/61h6O/src/utils/IO/branchdata_checks.jl:148 ┌ Warning: Rate 500.0 MW for A32-1 is larger than the max expected in the range of (min = 134.0, max = 145.0). └ @ PowerSystems ~/.julia/packages/PowerSystems/61h6O/src/utils/IO/branchdata_checks.jl:148 ┌ Warning: Rate 500.0 MW for B29 is larger than the max expected in the range of (min = 134.0, max = 145.0). └ @ PowerSystems ~/.julia/packages/PowerSystems/61h6O/src/utils/IO/branchdata_checks.jl:148 ┌ Warning: Rate 500.0 MW for A18 is larger than the max expected in the range of (min = 134.0, max = 145.0). └ @ PowerSystems ~/.julia/packages/PowerSystems/61h6O/src/utils/IO/branchdata_checks.jl:148 ┌ Warning: Rate 175.0 MW for C5 is larger than the max expected in the range of (min = 47.0, max = 52.0). └ @ PowerSystems ~/.julia/packages/PowerSystems/61h6O/src/utils/IO/branchdata_checks.jl:148 ┌ Warning: Rate 175.0 MW for C13-2 is larger than the max expected in the range of (min = 47.0, max = 52.0). └ @ PowerSystems ~/.julia/packages/PowerSystems/61h6O/src/utils/IO/branchdata_checks.jl:148 ┌ Warning: Rate 500.0 MW for C24 is larger than the max expected in the range of (min = 134.0, max = 145.0). └ @ PowerSystems ~/.julia/packages/PowerSystems/61h6O/src/utils/IO/branchdata_checks.jl:148 ┌ Warning: Rate 500.0 MW for C28 is larger than the max expected in the range of (min = 134.0, max = 145.0). └ @ PowerSystems ~/.julia/packages/PowerSystems/61h6O/src/utils/IO/branchdata_checks.jl:148 ┌ Warning: Rate 500.0 MW for A29 is larger than the max expected in the range of (min = 134.0, max = 145.0). └ @ PowerSystems ~/.julia/packages/PowerSystems/61h6O/src/utils/IO/branchdata_checks.jl:148 ┌ Warning: Rate 175.0 MW for A3 is larger than the max expected in the range of (min = 47.0, max = 52.0). └ @ PowerSystems ~/.julia/packages/PowerSystems/61h6O/src/utils/IO/branchdata_checks.jl:148 ┌ Warning: Rate 175.0 MW for AB1 is larger than the max expected in the range of (min = 47.0, max = 52.0). └ @ PowerSystems ~/.julia/packages/PowerSystems/61h6O/src/utils/IO/branchdata_checks.jl:148 ┌ Warning: Rate 500.0 MW for AB2 is larger than the max expected in the range of (min = 134.0, max = 145.0). └ @ PowerSystems ~/.julia/packages/PowerSystems/61h6O/src/utils/IO/branchdata_checks.jl:148 ┌ Warning: Rate 500.0 MW for C27 is larger than the max expected in the range of (min = 134.0, max = 145.0). └ @ PowerSystems ~/.julia/packages/PowerSystems/61h6O/src/utils/IO/branchdata_checks.jl:148 ┌ Warning: Rate 722.0 MW for C35 is larger than the max expected in the range of (min = 134.0, max = 145.0). └ @ PowerSystems ~/.julia/packages/PowerSystems/61h6O/src/utils/IO/branchdata_checks.jl:148 ┌ Warning: Rate 500.0 MW for A25-1 is larger than the max expected in the range of (min = 134.0, max = 145.0). └ @ PowerSystems ~/.julia/packages/PowerSystems/61h6O/src/utils/IO/branchdata_checks.jl:148 ┌ Warning: Rate 175.0 MW for B5 is larger than the max expected in the range of (min = 47.0, max = 52.0). └ @ PowerSystems ~/.julia/packages/PowerSystems/61h6O/src/utils/IO/branchdata_checks.jl:148 ┌ Warning: Rate 175.0 MW for B2 is larger than the max expected in the range of (min = 47.0, max = 52.0). └ @ PowerSystems ~/.julia/packages/PowerSystems/61h6O/src/utils/IO/branchdata_checks.jl:148 ┌ Warning: Rate 500.0 MW for B31-1 is larger than the max expected in the range of (min = 134.0, max = 145.0). └ @ PowerSystems ~/.julia/packages/PowerSystems/61h6O/src/utils/IO/branchdata_checks.jl:148 ┌ Warning: Rate 500.0 MW for C20 is larger than the max expected in the range of (min = 134.0, max = 145.0). └ @ PowerSystems ~/.julia/packages/PowerSystems/61h6O/src/utils/IO/branchdata_checks.jl:148 ┌ Warning: Rate 500.0 MW for B23 is larger than the max expected in the range of (min = 134.0, max = 145.0). └ @ PowerSystems ~/.julia/packages/PowerSystems/61h6O/src/utils/IO/branchdata_checks.jl:148 ┌ Warning: Rate 500.0 MW for A28 is larger than the max expected in the range of (min = 134.0, max = 145.0). └ @ PowerSystems ~/.julia/packages/PowerSystems/61h6O/src/utils/IO/branchdata_checks.jl:148 ┌ Warning: Rate 500.0 MW for A19 is larger than the max expected in the range of (min = 134.0, max = 145.0). └ @ PowerSystems ~/.julia/packages/PowerSystems/61h6O/src/utils/IO/branchdata_checks.jl:148 ┌ Warning: Rate 175.0 MW for C9 is larger than the max expected in the range of (min = 47.0, max = 52.0). └ @ PowerSystems ~/.julia/packages/PowerSystems/61h6O/src/utils/IO/branchdata_checks.jl:148 ┌ Warning: Rate 175.0 MW for C1 is larger than the max expected in the range of (min = 47.0, max = 52.0). └ @ PowerSystems ~/.julia/packages/PowerSystems/61h6O/src/utils/IO/branchdata_checks.jl:148 ┌ Warning: Rate 175.0 MW for C2 is larger than the max expected in the range of (min = 47.0, max = 52.0). └ @ PowerSystems ~/.julia/packages/PowerSystems/61h6O/src/utils/IO/branchdata_checks.jl:148 ┌ Warning: Rate 500.0 MW for B25-1 is larger than the max expected in the range of (min = 134.0, max = 145.0). └ @ PowerSystems ~/.julia/packages/PowerSystems/61h6O/src/utils/IO/branchdata_checks.jl:148 ┌ Warning: Rate 175.0 MW for A9 is larger than the max expected in the range of (min = 47.0, max = 52.0). └ @ PowerSystems ~/.julia/packages/PowerSystems/61h6O/src/utils/IO/branchdata_checks.jl:148 ┌ Warning: Rate 175.0 MW for C11 is larger than the max expected in the range of (min = 47.0, max = 52.0). └ @ PowerSystems ~/.julia/packages/PowerSystems/61h6O/src/utils/IO/branchdata_checks.jl:148 ┌ Warning: Rate 500.0 MW for C19 is larger than the max expected in the range of (min = 134.0, max = 145.0). └ @ PowerSystems ~/.julia/packages/PowerSystems/61h6O/src/utils/IO/branchdata_checks.jl:148 ┌ Warning: Rate 500.0 MW for C23 is larger than the max expected in the range of (min = 134.0, max = 145.0). └ @ PowerSystems ~/.julia/packages/PowerSystems/61h6O/src/utils/IO/branchdata_checks.jl:148 ┌ Warning: Rate 500.0 MW for C25-1 is larger than the max expected in the range of (min = 134.0, max = 145.0). └ @ PowerSystems ~/.julia/packages/PowerSystems/61h6O/src/utils/IO/branchdata_checks.jl:148 ┌ Warning: Rate 500.0 MW for C32-2 is larger than the max expected in the range of (min = 134.0, max = 145.0). └ @ PowerSystems ~/.julia/packages/PowerSystems/61h6O/src/utils/IO/branchdata_checks.jl:148 ┌ Warning: Rate 500.0 MW for B20 is larger than the max expected in the range of (min = 134.0, max = 145.0). └ @ PowerSystems ~/.julia/packages/PowerSystems/61h6O/src/utils/IO/branchdata_checks.jl:148 ┌ Warning: Rate 175.0 MW for B9 is larger than the max expected in the range of (min = 47.0, max = 52.0). └ @ PowerSystems ~/.julia/packages/PowerSystems/61h6O/src/utils/IO/branchdata_checks.jl:148 ┌ Warning: Rate 500.0 MW for C25-2 is larger than the max expected in the range of (min = 134.0, max = 145.0). └ @ PowerSystems ~/.julia/packages/PowerSystems/61h6O/src/utils/IO/branchdata_checks.jl:148 ┌ Warning: Rate 175.0 MW for C12-1 is larger than the max expected in the range of (min = 47.0, max = 52.0). └ @ PowerSystems ~/.julia/packages/PowerSystems/61h6O/src/utils/IO/branchdata_checks.jl:148 ┌ Warning: Rate 500.0 MW for A27 is larger than the max expected in the range of (min = 134.0, max = 145.0). └ @ PowerSystems ~/.julia/packages/PowerSystems/61h6O/src/utils/IO/branchdata_checks.jl:148 ┌ Warning: Rate 500.0 MW for C21 is larger than the max expected in the range of (min = 134.0, max = 145.0). └ @ PowerSystems ~/.julia/packages/PowerSystems/61h6O/src/utils/IO/branchdata_checks.jl:148 ┌ Warning: Rate 500.0 MW for A34 is larger than the max expected in the range of (min = 134.0, max = 145.0). └ @ PowerSystems ~/.julia/packages/PowerSystems/61h6O/src/utils/IO/branchdata_checks.jl:148
Base Power: 100.0
Num components: 497
13 rows × 3 columns
ConcreteType | SuperTypes | Count | |
---|---|---|---|
String | String | Int64 | |
1 | Arc | Topology <: Component <: InfrastructureSystemsComponent <: InfrastructureSystemsType <: Any | 109 |
2 | Area | AggregationTopology <: Topology <: Component <: InfrastructureSystemsComponent <: InfrastructureSystemsType <: Any | 3 |
3 | Bus | Topology <: Component <: InfrastructureSystemsComponent <: InfrastructureSystemsType <: Any | 73 |
4 | HydroDispatch | HydroGen <: Generator <: StaticInjection <: Device <: Component <: InfrastructureSystemsComponent <: InfrastructureSystemsType <: Any | 1 |
5 | Line | ACBranch <: Branch <: Device <: Component <: InfrastructureSystemsComponent <: InfrastructureSystemsType <: Any | 105 |
6 | LoadZone | AggregationTopology <: Topology <: Component <: InfrastructureSystemsComponent <: InfrastructureSystemsType <: Any | 21 |
7 | PowerLoad | StaticLoad <: ElectricLoad <: StaticInjection <: Device <: Component <: InfrastructureSystemsComponent <: InfrastructureSystemsType <: Any | 51 |
8 | RenewableDispatch | RenewableGen <: Generator <: StaticInjection <: Device <: Component <: InfrastructureSystemsComponent <: InfrastructureSystemsType <: Any | 29 |
9 | RenewableFix | RenewableGen <: Generator <: StaticInjection <: Device <: Component <: InfrastructureSystemsComponent <: InfrastructureSystemsType <: Any | 31 |
10 | TapTransformer | ACBranch <: Branch <: Device <: Component <: InfrastructureSystemsComponent <: InfrastructureSystemsType <: Any | 15 |
11 | ThermalStandard | ThermalGen <: Generator <: StaticInjection <: Device <: Component <: InfrastructureSystemsComponent <: InfrastructureSystemsType <: Any | 54 |
12 | VariableReserve{ReserveDown} | Reserve{ReserveDown} <: Service <: Component <: InfrastructureSystemsComponent <: InfrastructureSystemsType <: Any | 1 |
13 | VariableReserve{ReserveUp} | Reserve{ReserveUp} <: Service <: Component <: InfrastructureSystemsComponent <: InfrastructureSystemsType <: Any | 4 |
Components with time series data: 120
Total StaticTimeSeries: 122
Total Forecasts: 122
Resolution: 60 minutes
First initial time: 2020-01-01T00:00:00
Last initial time: 2020-12-30T00:00:00
Horizon: 48
Interval: 1440 minutes
Forecast window count: 365
OperationsProblemTemplate
¶You can create an empty template with:
template_uc = OperationsProblemTemplate()
Operations Problem Specification ============================================ Transmission: CopperPlatePowerModel ============================================ Devices Models: ============================================ Branches Models: ============================================ Services Models: ============================================
Now, you can add a DeviceModel
for each device type to create an assignment between PowerSystems device types
and the subtypes of AbstractDeviceFormulation
. PowerSimulations has a variety of different
AbstractDeviceFormulation
subtypes that can be applied to different PowerSystems device types,
each dispatching to different methods for populating optimization problem objectives, variables,
and constraints.
print_tree(PSI.AbstractDeviceFormulation)
AbstractDeviceFormulation ├─ FixedOutput ├─ AbstractBranchFormulation │ ├─ AbstractDCLineFormulation │ │ ├─ HVDCDispatch │ │ ├─ HVDCLossless │ │ └─ HVDCUnbounded │ ├─ StaticBranch │ ├─ StaticBranchBounds │ └─ StaticBranchUnbounded ├─ AbstractHydroFormulation │ ├─ AbstractHydroDispatchFormulation │ │ ├─ HydroDispatchRunOfRiver │ │ └─ AbstractHydroReservoirFormulation │ │ ├─ HydroDispatchPumpedStorage │ │ ├─ HydroDispatchPumpedStoragewReservation │ │ ├─ HydroDispatchReservoirBudget │ │ └─ HydroDispatchReservoirStorage │ └─ AbstractHydroUnitCommitment │ ├─ HydroCommitmentReservoirBudget │ ├─ HydroCommitmentReservoirStorage │ └─ HydroCommitmentRunOfRiver ├─ AbstractLoadFormulation │ ├─ AbstractControllablePowerLoadFormulation │ │ ├─ DispatchablePowerLoad │ │ └─ InterruptiblePowerLoad │ └─ StaticPowerLoad ├─ AbstractRegulationFormulation │ ├─ DeviceLimitedRegulation │ └─ ReserveLimitedRegulation ├─ AbstractRenewableFormulation │ └─ AbstractRenewableDispatchFormulation │ ├─ RenewableConstantPowerFactor │ └─ RenewableFullDispatch ├─ AbstractStorageFormulation │ ├─ BatteryAncillaryServices │ ├─ BookKeeping │ ├─ BookKeepingwReservation │ └─ AbstractEnergyManagement │ └─ EnergyTarget └─ AbstractThermalFormulation ├─ AbstractThermalDispatchFormulation │ ├─ ThermalCompactDispatch │ ├─ ThermalDispatch │ ├─ ThermalDispatchNoMin │ └─ ThermalRampLimited └─ AbstractThermalUnitCommitment ├─ AbstractCompactUnitCommitment │ ├─ ThermalCompactUnitCommitment │ └─ ThermalMultiStartUnitCommitment └─ AbstractStandardUnitCommitment ├─ ThermalBasicUnitCommitment └─ ThermalStandardUnitCommitment
Here is an example of relatively standard branch formulations. Other formulations allow for selective enforcement of transmission limits and greater control on transformer settings.
set_device_model!(template_uc, Line, StaticBranch)
set_device_model!(template_uc, Transformer2W, StaticBranch)
set_device_model!(template_uc, TapTransformer, StaticBranch)
Here we define template entries for all devices that inject or withdraw power on the
network. For each device type, we can define a distinct AbstractDeviceFormulation
. In
this case, we're defining a basic unit commitment model for thermal generators,
curtailable renewable generators, and fixed dispatch (net-load reduction) formulations
for HydroDispatch
and RenewableFix
devices.
set_device_model!(template_uc, ThermalStandard, ThermalStandardUnitCommitment)
set_device_model!(template_uc, RenewableDispatch, RenewableFullDispatch)
set_device_model!(template_uc, PowerLoad, StaticPowerLoad)
set_device_model!(template_uc, HydroDispatch, FixedOutput)
set_device_model!(template_uc, HydroEnergyReservoir, HydroDispatchRunOfRiver)
set_device_model!(template_uc, RenewableFix, FixedOutput)
We have two VariableReserve
types, parameterized by their direction. So, similar to
creating DeviceModel
s, we can create ServiceModel
s. The primary difference being
that DeviceModel
objects define how constraints get created, while ServiceModel
objects
define how constraints get modified.
set_service_model!(template_uc, VariableReserve{ReserveUp}, RangeReserve)
set_service_model!(template_uc, VariableReserve{ReserveDown}, RangeReserve)
Finally, we can define the transmission network specification that we'd like to model. For simplicity, we'll choose a copper plate formulation. But there are dozens of specifications available through an integration with PowerModels.jl. Note that many formulations will require appropriate data and may be computationally intractable
set_transmission_model!(template_uc, CopperPlatePowerModel)
OperationsProblem
¶Now that we have a System
and an OperationsProblemTemplate
, we can put the two together
to create an OperationsProblem
that we solve.
It's most convenient to define an optimizer instance upfront and pass it into the
OperationsProblem
constructor. For this example, we can use the free Cbc solver with a
relatively relaxed MIP gap (ratioGap
) setting to improve speed.
solver = optimizer_with_attributes(Cbc.Optimizer, "logLevel" => 1, "ratioGap" => 0.5)
MathOptInterface.OptimizerWithAttributes(Cbc.Optimizer, Pair{MathOptInterface.AbstractOptimizerAttribute, Any}[MathOptInterface.RawParameter("logLevel") => 1, MathOptInterface.RawParameter("ratioGap") => 0.5])
OperationsProblem
¶The construction of an OperationsProblem
essentially applies an OperationsProblemTemplate
to System
data to create a JuMP model.
op_problem = OperationsProblem(template_uc, sys; optimizer = solver, horizon = 24)
build!(op_problem, output_dir = mktempdir())
BuildStatus.BUILT = 0
The principal component of the OperationsProblem
is the JuMP model. For small problems,
you can inspect it by simply printing it to the screen:
op_problem.internal.optimization_container.JuMPmodel
For anything of reasonable size, that will be unmanageable. But you can print to a file:
f = open("testmodel.txt","w"); print(f,op_problem.internal.optimization_container.JuMPmodel); close(f)
In addition to the JuMP model, an OperationsProblem
keeps track of a bunch of metadata
about the problem and some references to pretty names for constraints and variables.
All of these details are contained within the optimization_container
field.
print_struct(typeof(op_problem.internal.optimization_container))
mutable struct PowerSimulations.OptimizationContainer JuMPmodel::JuMP.Model time_steps::UnitRange{Int64} resolution::TimePeriod settings::PowerSimulations.Settings settings_copy::PowerSimulations.Settings variables::Dict{Symbol, AbstractArray} aux_variables::Dict{PowerSimulations.AuxVarKey, AbstractArray} constraints::Dict{Symbol, AbstractArray} cost_function::JuMP.AbstractJuMPScalar expressions::Dict{Symbol, JuMP.Containers.DenseAxisArray} parameters::Dict{Symbol, PowerSimulations.ParameterContainer} initial_conditions::PowerSimulations.InitialConditions pm::Union{Nothing, PowerModels.AbstractPowerModel} base_power::Float64 solve_timed_log::Dict{Symbol, Any} end
OperationsProblem
¶solve!(op_problem)
RunStatus.SUCCESSFUL = 0
PowerSimulations collects the OperationsProblem
results into a struct:
print_struct(PSI.ProblemResults)
struct ProblemResults base_power::Float64 timestamps::StepRange{DateTime, Millisecond} system::Union{Nothing, System} variable_values::Dict{Symbol, DataFrame} dual_values::Dict{Symbol, DataFrame} parameter_values::Dict{Symbol, DataFrame} optimizer_stats::PowerSimulations.OptimizerStats output_dir::String end
res = ProblemResults(op_problem);
The optimizer summary is included
get_optimizer_stats(res)
PowerSimulations.OptimizerStats(0, 0, 1.5869977732513344e6, 1, 1, 0, 6.011106014251709, 6.052711885, 3.72044e7, 0.0)
get_objective_value(res)
1.5869977732513344e6
The solution value data frames for variables can be accessed by:
variable_values = get_variables(res)
Dict{Symbol, DataFrame} with 10 entries: :Spin_Up_R2__VariableRes… => 24×19 DataFrame… :P__ThermalStandard => 24×55 DataFrame… :start__ThermalStandard => 24×55 DataFrame… :P__RenewableDispatch => 24×30 DataFrame… :stop__ThermalStandard => 24×55 DataFrame… :Reg_Down__VariableReser… => 24×52 DataFrame… :Spin_Up_R1__VariableRes… => 24×17 DataFrame… :Reg_Up__VariableReserve… => 24×52 DataFrame… :Spin_Up_R3__VariableRes… => 24×18 DataFrame… :On__ThermalStandard => 24×55 DataFrame…
Take a look at the examples in the plotting folder.
This notebook was generated using Literate.jl.