Originally Contributed by: Sourabh Dalvi
PowerSimulations.jl supports linear PTDF optimal power flow formulation. This example shows a single multi-period optimization of economic dispatch with a linearized DC-OPF representation of using PTDF power flow and how to extract duals values or locational marginal prices for energy.
using SIIPExamples
using PowerSystems
using PowerSimulations
using PowerSystemCaseBuilder
using DataFrames
Since we'll be retrieving duals, we need a solver that returns duals values here we use Ipopt.
using Ipopt
solver = optimizer_with_attributes(Ipopt.Optimizer)
MathOptInterface.OptimizerWithAttributes(Ipopt.Optimizer, Pair{MathOptInterface.AbstractOptimizerAttribute,Any}[])
We can use the same RTS data and some of the initialization as in OperationsProblem example
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_i2adzQ ┌ 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/N2l8o/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/N2l8o/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/N2l8o/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/N2l8o/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/N2l8o/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/N2l8o/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/N2l8o/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/N2l8o/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/N2l8o/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/N2l8o/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/N2l8o/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/N2l8o/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/N2l8o/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/N2l8o/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/N2l8o/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/N2l8o/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/N2l8o/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/N2l8o/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/N2l8o/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/N2l8o/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/N2l8o/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/N2l8o/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/N2l8o/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/N2l8o/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/N2l8o/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/N2l8o/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/N2l8o/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/N2l8o/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/N2l8o/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/N2l8o/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/N2l8o/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/N2l8o/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/N2l8o/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/N2l8o/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/N2l8o/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/N2l8o/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/N2l8o/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/N2l8o/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/N2l8o/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/N2l8o/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/N2l8o/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/N2l8o/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/N2l8o/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/N2l8o/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/N2l8o/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/N2l8o/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/N2l8o/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/N2l8o/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/N2l8o/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/N2l8o/src/utils/IO/branchdata_checks.jl:148
Base Power: 100.0
Num components: 529
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 | GenericBattery | Storage <: StaticInjection <: Device <: Component <: InfrastructureSystemsComponent <: InfrastructureSystemsType <: Any | 1 |
5 | HVDCLine | DCBranch <: Branch <: Device <: Component <: InfrastructureSystemsComponent <: InfrastructureSystemsType <: Any | 1 |
6 | HydroDispatch | HydroGen <: Generator <: StaticInjection <: Device <: Component <: InfrastructureSystemsComponent <: InfrastructureSystemsType <: Any | 1 |
7 | HydroEnergyReservoir | HydroGen <: Generator <: StaticInjection <: Device <: Component <: InfrastructureSystemsComponent <: InfrastructureSystemsType <: Any | 19 |
8 | Line | ACBranch <: Branch <: Device <: Component <: InfrastructureSystemsComponent <: InfrastructureSystemsType <: Any | 105 |
9 | LoadZone | AggregationTopology <: Topology <: Component <: InfrastructureSystemsComponent <: InfrastructureSystemsType <: Any | 21 |
10 | PowerLoad | StaticLoad <: ElectricLoad <: StaticInjection <: Device <: Component <: InfrastructureSystemsComponent <: InfrastructureSystemsType <: Any | 51 |
11 | RenewableDispatch | RenewableGen <: Generator <: StaticInjection <: Device <: Component <: InfrastructureSystemsComponent <: InfrastructureSystemsType <: Any | 30 |
12 | RenewableFix | RenewableGen <: Generator <: StaticInjection <: Device <: Component <: InfrastructureSystemsComponent <: InfrastructureSystemsType <: Any | 31 |
13 | TapTransformer | ACBranch <: Branch <: Device <: Component <: InfrastructureSystemsComponent <: InfrastructureSystemsType <: Any | 15 |
14 | ThermalStandard | ThermalGen <: Generator <: StaticInjection <: Device <: Component <: InfrastructureSystemsComponent <: InfrastructureSystemsType <: Any | 64 |
15 | VariableReserve{ReserveDown} | Reserve{ReserveDown} <: Service <: Component <: InfrastructureSystemsComponent <: InfrastructureSystemsType <: Any | 1 |
16 | VariableReserve{ReserveUp} | Reserve{ReserveUp} <: Service <: Component <: InfrastructureSystemsComponent <: InfrastructureSystemsType <: Any | 4 |
Components with time series data: 140
Total StaticTimeSeries: 180
Total Forecasts: 180
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
Here, we want do define an economic dispatch (linear generation decisions) with linear DC-OPF using PTDF network representation. So, starting with the network, we can select from almost any of the endpoints on this tree:
print_tree(PowerSimulations.PM.AbstractPowerModel)
AbstractPowerModel ├─ AbstractACPModel │ └─ ACPPowerModel ├─ AbstractACRModel │ ├─ ACRPowerModel │ └─ AbstractIVRModel │ └─ IVRPowerModel ├─ AbstractACTModel │ └─ ACTPowerModel ├─ AbstractActivePowerModel │ ├─ AreaBalancePowerModel │ ├─ CopperPlatePowerModel │ └─ AbstractDCPModel │ ├─ DCPPowerModel │ ├─ AbstractDCMPPModel │ │ └─ DCMPPowerModel │ ├─ AbstractDCPLLModel │ │ └─ DCPLLPowerModel │ ├─ AbstractNFAModel │ │ └─ NFAPowerModel │ └─ AbstractPTDFModel │ ├─ PTDFPowerModel │ └─ StandardPTDFModel ├─ AbstractBFModel │ ├─ AbstractBFAModel │ │ └─ BFAPowerModel │ ├─ AbstractBFConicModel │ │ └─ AbstractSOCBFConicModel │ │ └─ SOCBFConicPowerModel │ └─ AbstractBFQPModel │ └─ AbstractSOCBFModel │ └─ SOCBFPowerModel ├─ AbstractConicModel │ ├─ AbstractWRConicModel │ │ └─ AbstractSOCWRConicModel │ │ └─ SOCWRConicPowerModel │ └─ AbstractWRMModel │ └─ AbstractSDPWRMModel │ ├─ AbstractSparseSDPWRMModel │ │ └─ SparseSDPWRMPowerModel │ │ ⋮ │ │ │ └─ SDPWRMPowerModel ├─ AbstractLPACModel │ └─ AbstractLPACCModel │ └─ LPACCPowerModel └─ AbstractWRModel ├─ AbstractQCWRModel │ ├─ AbstractQCLSModel │ │ └─ QCLSPowerModel │ └─ AbstractQCRMPowerModel │ └─ QCRMPowerModel └─ AbstractSOCWRModel └─ SOCWRPowerModel
For now, let's just choose a standard PTDF formulation.
ed_template = template_economic_dispatch(network = StandardPTDFModel)
Operations Problem Specification ============================================ Transmission: StandardPTDFModel ============================================ Devices Models: Type: ThermalStandard Formulation: ThermalDispatch Type: HydroDispatch Formulation: HydroDispatchRunOfRiver Type: PowerLoad Formulation: StaticPowerLoad Type: RenewableFix Formulation: FixedOutput Type: RenewableDispatch Formulation: RenewableFullDispatch Type: HydroEnergyReservoir Formulation: HydroDispatchRunOfRiver Type: InterruptibleLoad Formulation: InterruptiblePowerLoad ============================================ Branches Models: Type: Line Formulation: StaticBranch Type: TapTransformer Formulation: StaticBranch Type: Transformer2W Formulation: StaticBranch Type: HVDCLine Formulation: HVDCDispatch ============================================ Services Models: Type: VariableReserve{ReserveDown} Formulation: RangeReserve Type: VariableReserve{ReserveUp} Formulation: RangeReserve ============================================
Currently energy budget data isn't stored in the RTS-GMLC dataset.
set_device_model!(ed_template, HydroEnergyReservoir, HydroDispatchRunOfRiver)
[ Info: Overwriting HydroEnergyReservoir existing model
Calculate the PTDF matrix.
PTDF_matrix = PTDF(sys)
PowerNetworkMatrix : 0.436221 -0.506679 0.0955772 … 0.0139213 0.0168526 0.242695 0.220093 -0.199576 -0.0291078 -0.0352191 0.321083 0.286586 0.103999 0.0151865 0.0183664 0.240805 0.269317 0.029752 0.00430723 0.00522632 0.195416 0.224003 0.0658252 0.00961407 0.0116263 0.0884399 0.0729336 0.422869 … 0.0616101 0.0745751 0.154255 0.14716 0.377554 -0.0907179 -0.109794 0.240805 0.269317 0.029752 0.00430723 0.00522632 0.321083 0.286586 0.103999 0.0151865 0.0183664 0.195416 0.224003 0.0658252 0.00961407 0.0116263 ⋮ ⋱ -0.00640688 -0.00621149 -0.0125463 -0.170422 -0.0870865 -0.00640688 -0.00621149 -0.0125463 -0.170422 -0.0870865 -0.0101178 -0.00980923 -0.0198131 0.129136 -0.137527 -0.0101178 -0.00980923 -0.0198131 0.129136 -0.137527 -0.0101178 -0.00980923 -0.0198131 … 0.129136 -0.137527 -0.0101178 -0.00980923 -0.0198131 0.129136 -0.137527 -0.000301309 -0.00029212 -0.000590038 0.0124161 -0.00409559 -0.0284356 -0.0275684 -0.0556839 0.496086 -0.386515 -0.0284356 -0.0275684 -0.0556839 0.496086 0.613485
Now we can build a 4-hour economic dispatch / OPF problem with the RTS data.
Here, we have to pass the keyword argument constraint_duals
to OperationsProblem
with the name of the constraint for which duals are required for them to be returned in the results.
problem = OperationsProblem(
EconomicDispatchProblem,
ed_template,
sys,
horizon = 1,
optimizer = solver,
balance_slack_variables = true,
constraint_duals = [
:CopperPlateBalance,
:network_flow__Line,
:network_flow__TapTransformer,
],
PTDF = PTDF_matrix,
)
build!(problem, output_dir = mktempdir())
BuildStatus.BUILT = 0
And solve the problem and collect the results
solve!(problem)
RunStatus.SUCCESSFUL = 0
Here we collect the dual values from the results for the :CopperPlateBalance
and :network_flow
constraints. In the case of PTDF network formulation we need to compute the final LMP for each bus in the system by
subtracting the duals (μ) of :network_flow
constraints multiplied by the PTDF matrix
from the dual (λ) of :CopperPlateBalance
constraint.
res = ProblemResults(problem)
duals = get_duals(res)
λ = convert(Array, duals[:CopperPlateBalance][:, :var])
flow_duals = outerjoin(
[duals[k] for k in [:network_flow__Line, :network_flow__TapTransformer]]...,
on = :DateTime,
)
μ = Matrix(flow_duals[:, PTDF_matrix.axes[1]])
1×120 Array{Union{Missing, Float64},2}: -1.05088e-6 1.67219e-6 -3.46606e-6 … 3.33268e-7 1.12191e-6 2.53909e-6
Here we create Dict to store the calculate congestion component of the LMP which is a product of μ and the PTDF matrix.
buses = get_components(Bus, sys)
congestion_lmp = Dict()
for bus in buses
congestion_lmp[get_name(bus)] = μ * PTDF_matrix[:, get_number(bus)]
end
congestion_lmp = DataFrame(congestion_lmp)
Abel | Adams | Adler | Agricola | Aiken | Alber | Alder | |
---|---|---|---|---|---|---|---|
Float64? | Float64? | Float64? | Float64? | Float64? | Float64? | Float64? | |
1 | 2.28716e-6 | 3.4901e-6 | 2.51587e-7 | 5.34001e-6 | 4.97636e-6 | 8.22617e-6 | -9.02173e-6 |
Finally here we get the LMP for each node in a lossless DC-OPF using the PTDF formulation.
LMP = λ .- congestion_lmp
Abel | Adams | Adler | Agricola | Aiken | Alber | Alder | Alger | Ali | |
---|---|---|---|---|---|---|---|---|---|
Float64 | Float64 | Float64 | Float64 | Float64 | Float64 | Float64 | Float64 | Float64 | |
1 | -1.0e6 | -1.0e6 | -1.0e6 | -1.0e6 | -1.0e6 | -1.0e6 | -1.0e6 | -1.0e6 | -1.0e6 |
This notebook was generated using Literate.jl.