Read results
Once a DecisionModel
is solved via solve!(model)
or a Simulation is executed (and solved) via execute!(simulation)
, the results are stored and can be accessed directly in the REPL for result exploration and plotting.
Read results of a Decision Problem
Once a DecisionModel
is solved, results are accessed using OptimizationProblemResults(model)
as follows:
# The DecisionModel is already constructed
build!(model, output_dir = mktempdir())
solve!(model)
results = OptimizationProblemResults(model)
The output will showcase the available expressions, parameters and variables to read. For example it will look like:
Start: 2020-01-01T00:00:00
End: 2020-01-03T23:00:00
Resolution: 60 minutes
PowerSimulations Problem Auxiliary variables Results
┌──────────────────────────────────────────┐
│ CumulativeCyclingCharge__HybridSystem │
│ CumulativeCyclingDischarge__HybridSystem │
└──────────────────────────────────────────┘
PowerSimulations Problem Expressions Results
┌─────────────────────────────────────────────┐
│ ProductionCostExpression__RenewableDispatch │
│ ProductionCostExpression__ThermalStandard │
└─────────────────────────────────────────────┘
PowerSimulations Problem Duals Results
┌──────────────────────────────────────┐
│ CopperPlateBalanceConstraint__System │
└──────────────────────────────────────┘
PowerSimulations Problem Parameters Results
┌────────────────────────────────────────────────────────────────────────┐
│ ActivePowerTimeSeriesParameter__RenewableNonDispatch │
│ RenewablePowerTimeSeries__HybridSystem │
│ RequirementTimeSeriesParameter__VariableReserve__ReserveUp__Spin_Up_R3 │
│ RequirementTimeSeriesParameter__VariableReserve__ReserveUp__Reg_Up │
│ ActivePowerTimeSeriesParameter__PowerLoad │
│ ActivePowerTimeSeriesParameter__RenewableDispatch │
│ RequirementTimeSeriesParameter__VariableReserve__ReserveDown__Reg_Down │
│ ActivePowerTimeSeriesParameter__HydroDispatch │
│ RequirementTimeSeriesParameter__VariableReserve__ReserveUp__Spin_Up_R1 │
│ RequirementTimeSeriesParameter__VariableReserve__ReserveUp__Spin_Up_R2 │
└────────────────────────────────────────────────────────────────────────┘
PowerSimulations Problem Variables Results
┌────────────────────────────────────────────────────────────────────┐
│ ActivePowerOutVariable__HybridSystem │
│ ReservationVariable__HybridSystem │
│ RenewablePower__HybridSystem │
│ ActivePowerReserveVariable__VariableReserve__ReserveUp__Spin_Up_R1 │
│ SystemBalanceSlackUp__System │
│ BatteryEnergyShortageVariable__HybridSystem │
│ ActivePowerReserveVariable__VariableReserve__ReserveUp__Reg_Up │
│ StopVariable__ThermalStandard │
│ BatteryStatus__HybridSystem │
│ BatteryDischarge__HybridSystem │
│ ActivePowerInVariable__HybridSystem │
│ DischargeRegularizationVariable__HybridSystem │
│ BatteryCharge__HybridSystem │
│ ActivePowerVariable__RenewableDispatch │
│ ActivePowerReserveVariable__VariableReserve__ReserveDown__Reg_Down │
│ EnergyVariable__HybridSystem │
│ OnVariable__HybridSystem │
│ BatteryEnergySurplusVariable__HybridSystem │
│ SystemBalanceSlackDown__System │
│ ActivePowerReserveVariable__VariableReserve__ReserveUp__Spin_Up_R2 │
│ ThermalPower__HybridSystem │
│ ActivePowerVariable__ThermalStandard │
│ StartVariable__ThermalStandard │
│ ActivePowerReserveVariable__VariableReserve__ReserveUp__Spin_Up_R3 │
│ OnVariable__ThermalStandard │
│ ChargeRegularizationVariable__HybridSystem │
└────────────────────────────────────────────────────────────────────┘
Then the following code can be used to read results:
# Read active power of Thermal Standard
thermal_active_power = read_variable(results, "ActivePowerVariable__ThermalStandard")
# Read max active power parameter of RenewableDispatch
renewable_param = read_parameter(results, "ActivePowerTimeSeriesParameter__RenewableDispatch")
# Read cost expressions of ThermalStandard units
cost_thermal = read_expression(results, "ProductionCostExpression__ThermalStandard")
# Read dual variables
dual_balance_constraint = read_dual(results, "CopperPlateBalanceConstraint__System")
# Read auxiliary variables
aux_var_result = read_aux_variable(results, "CumulativeCyclingCharge__HybridSystem")
Results will be in the form of DataFrames that can be easily explored.
Read results of a Simulation
# The Simulation is already constructed
build!(sim)
execute!(sim; enable_progress_bar=true)
results_sim = SimulationResults(sim)
As an example, the SimulationResults
printing will look like:
Decision Problem Results
┌──────────────┬─────────────────────┬──────────────┬─────────────────────────┐
│ Problem Name │ Initial Time │ Resolution │ Last Solution Timestamp │
├──────────────┼─────────────────────┼──────────────┼─────────────────────────┤
│ ED │ 2020-10-02T00:00:00 │ 60 minutes │ 2020-10-09T23:00:00 │
│ UC │ 2020-10-02T00:00:00 │ 1440 minutes │ 2020-10-09T00:00:00 │
└──────────────┴─────────────────────┴──────────────┴─────────────────────────┘
Emulator Results
┌─────────────────┬───────────┐
│ Name │ Emulator │
│ Resolution │ 5 minutes │
│ Number of steps │ 2304 │
└─────────────────┴───────────┘
With this, it is possible to obtain results of each DecisionModel
and EmulationModel
as follows:
# Use the Problem Name for Decision Problems
results_uc = get_decision_problem_results(results_sim, "UC")
results_ed = get_decision_problem_results(results_sim, "ED")
results_emulator = get_emulation_problem_results(results_sim)
Once we have each decision (or emulation) problem results, we can explore directly using the approach for Decision Models, mentioned in the previous section.
Reading solutions for all simulation steps
In this case, using read_variable
(or read expression, parameter or dual), will return a dictionary of all steps (of that Decision Problem). For example, the following code:
thermal_active_power = read_variable(results_uc, "ActivePowerVariable__ThermalStandard")
will return:
DataStructures.SortedDict{Any, Any, Base.Order.ForwardOrdering} with 8 entries:
DateTime("2020-10-02T00:00:00") => 72×54 DataFrame…
DateTime("2020-10-03T00:00:00") => 72×54 DataFrame…
DateTime("2020-10-04T00:00:00") => 72×54 DataFrame…
DateTime("2020-10-05T00:00:00") => 72×54 DataFrame…
DateTime("2020-10-06T00:00:00") => 72×54 DataFrame…
DateTime("2020-10-07T00:00:00") => 72×54 DataFrame…
DateTime("2020-10-08T00:00:00") => 72×54 DataFrame…
DateTime("2020-10-09T00:00:00") => 72×54 DataFrame…
That is, a sorted dictionary for each simulation step, using as a key the initial timestamp for that specific simulation step.
Note that in this case, each DataFrame, has a dimension of $72 \times 54$, since the horizon is 72 hours (number of rows), but the interval is only 24 hours. Indeed, note the initial timestamp of each simulation step is the beginning of each day, i.e. 24 hours. Finally, there 54 columns, since this example system has 53 ThermalStandard
units (plus 1 column for the timestamps). The user is free to explore the solution of any simulation step as needed.
Reading the "realized" solution (i.e. the interval)
Using read_realized_variable
(or read realized expression, parameter or dual), will return the DataFrame of the realized solution of any specific variable. That is, it will concatenate the corresponding simulation step with the specified interval of that step, to construct a single DataFrame with the "realized solution" of the entire simulation.
For example, the code:
th_realized_power = read_realized_variable(results_uc, "ActivePowerVariable__ThermalStandard")
will return:
92×54 DataFrame
Row │ DateTime 322_CT_6 321_CC_1 202_STEAM_3 223_CT_4 123_STEAM_2 213_CT_1 223_CT_6 313_CC_1 101_STEAM_3 123_C ⋯
│ DateTime Float64 Float64 Float64 Float64 Float64 Float64 Float64 Float64 Float64 Float ⋯
─────┼───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
1 │ 2020-10-02T00:00:00 0.0 293.333 0.0 0.0 0.0 0.0 0.0 231.667 76.0 0.0 ⋯
2 │ 2020-10-02T01:00:00 0.0 267.552 0.0 0.0 0.0 0.0 0.0 231.667 76.0 0.0
3 │ 2020-10-02T02:00:00 0.0 234.255 0.0 0.0 -4.97544e-11 0.0 0.0 231.667 76.0 0.0
4 │ 2020-10-02T03:00:00 0.0 249.099 0.0 0.0 -4.97544e-11 0.0 0.0 231.667 76.0 0.0
5 │ 2020-10-02T04:00:00 0.0 293.333 0.0 0.0 -4.97544e-11 0.0 0.0 231.667 76.0 0.0 ⋯
6 │ 2020-10-02T05:00:00 0.0 293.333 1.27578e-11 0.0 -4.97544e-11 0.0 0.0 293.333 76.0 0.0
⋮ │ ⋮ ⋮ ⋮ ⋮ ⋮ ⋮ ⋮ ⋮ ⋮ ⋮ ⋱
187 │ 2020-10-09T18:00:00 0.0 293.333 76.0 0.0 155.0 0.0 0.0 318.843 76.0 0.0
188 │ 2020-10-09T19:00:00 0.0 293.333 76.0 0.0 124.0 0.0 0.0 293.333 76.0 0.0
189 │ 2020-10-09T20:00:00 0.0 293.333 60.6667 0.0 124.0 0.0 0.0 0.0 76.0 0.0 ⋯
190 │ 2020-10-09T21:00:00 -7.65965e-12 293.333 60.6667 0.0 124.0 0.0 0.0 0.0 76.0 0.0
191 │ 2020-10-09T22:00:00 0.0 0.0 60.6667 0.0 124.0 0.0 0.0 0.0 76.0 7.156
192 │ 2020-10-09T23:00:00 0.0 0.0 60.6667 0.0 117.81 0.0 0.0 0.0 76.0 0.0
44 columns and 180 rows omitted
In this case, the 8 simulation steps of 24 hours (192 hours), in a single DataFrame, to enable easy exploration of the realized results for the user.