Skip to content

Commit

Permalink
add neg rev to results and rename vpi/vpf to vpl/vpi
Browse files Browse the repository at this point in the history
  • Loading branch information
prakaa committed Jun 19, 2024
1 parent 4c560b7 commit 12e1d11
Showing 1 changed file with 49 additions and 44 deletions.
93 changes: 49 additions & 44 deletions src/results.jl
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,8 @@ Columns in summary output include:
4. `data_type`, which is forecast or actual (data)
5. `lookahead` of the simulation in minutes
6. `revenue`, which is the total annual revenue of the simulated device in AUD
7. `mean_rel_gap`, which is the mean relative gap across all decision times
7. `negative_revenue`, which is the total annual negative revenue (i.e. losses) of the simulated device in AUD
8. `mean_rel_gap`, which is the mean relative gap across all decision times
# Arguments
Expand All @@ -64,6 +65,7 @@ function _summarise_simulations(
data::Dict{String,Any}, formulation::String, energy_capacity::Float64
)
revenues = Float64[]
negative_revenues = Float64[]
bess_powers = Float64[]
data_types = String[]
lookaheads = Int64[]
Expand All @@ -74,10 +76,12 @@ function _summarise_simulations(
lookahead_df = df[df.lookahead_minutes .== lookahead, :]
lookahead_df = lookahead_df[lookahead_df.status .== "binding", :]
revenue = sum(lookahead_df.revenue)
negative_revenue = sum(lookahead_df[lookahead_df.revenue .< 0.0, :revenue])
average_gap = mean(unique(lookahead_df, "decision_time").relative_gap)
bess_power = string(match(r"([0-9\.]*)MW", split(key, "/")[2]).captures[])
data_type = split(key, "/")[1]
push!(revenues, float(revenue))
push!(negative_revenues, negative_revenue)
push!(lookaheads, lookahead)
push!(average_gaps, average_gap)
push!(bess_powers, parse(Float64, bess_power))
Expand All @@ -91,6 +95,7 @@ function _summarise_simulations(
:data_type => data_types,
:lookahead => string.(lookaheads),
:revenue => revenues,
:negative_revenue => negative_revenues,
:mean_rel_gap => average_gaps,
)
summary_data = sort(summary_data, "power_capacity")
Expand All @@ -100,18 +105,18 @@ function _summarise_simulations(
end

@doc raw"""
Calculates values of perfect information and foresight as absolute values (in AUD) and as
Calculates values of perfect lookahead and information as absolute values (in AUD) and as
a percentage of perfect foresight revenue.
**Value of perfect information**: What is the additional benefit (revenue) that a participant
**Value of perfect lookahead**: What is the additional benefit (revenue) that a participant
could gain if they were to know exactly what the market prices will be in the *lookahead
horizon*.
* ``VPI = \textrm{Revenue}_\textrm{Actual Data Simulation} - \textrm{Revenue}_\textrm{Forecast Data Simulation}``
* ``VPL = \textrm{Revenue}_\textrm{Actual Data Simulation} - \textrm{Revenue}_\textrm{Forecast Data Simulation}``
**Value of perfect foresight**: What is the additional benefit (revenue) that a participant
could gain if they were to know exactly what the market prices will be *over the entire
year*
* ``VPF = \textrm{Revenue}_\textrm{Perfect Foresight} - \textrm{Revenue}_\textrm{Forecast Data Simulation}``
**Value of perfect information**: What is the additional benefit (revenue) that a
participant could gain if they were to know exactly what the market prices will be
*over the entire year*
* ``VPI = \textrm{Revenue}_\textrm{Perfect Foresight} - \textrm{Revenue}_\textrm{Forecast Data Simulation}``
N.B. This function assumes that the input `df` only has data that corresponds to a device
of a particular `energy_capacity`.
Expand All @@ -122,12 +127,12 @@ of a particular `energy_capacity`.
# Returns
`DataFrame` with absolute values of perfect information and foresight, and the same values
`DataFrame` with absolute values of perfect lookahead and information, and the same values
as a percentage of perfect foresight revenue.
"""
function calculate_vpi_vpf(df::DataFrame)
(v_pi_abs, v_pf_abs) = (Float64[], Float64[])
(v_pi_percentage, v_pf_percentage) = (Float64[], Float64[])
function calculate_vpl_vpi(df::DataFrame)
(v_pl_abs, v_pi_abs) = (Float64[], Float64[])
(v_pl_percentage, v_pi_percentage) = (Float64[], Float64[])
(power_caps, data) = (Float64[], String[])
actual_caps = unique(df[df.data_type .== "actual", :power_capacity])
forecast_caps = unique(df[df.data_type .== "forecast", :power_capacity])
Expand All @@ -139,45 +144,45 @@ function calculate_vpi_vpf(df::DataFrame)
cap_mask = df.power_capacity .== cap
actual_mask = df.data_type .== "actual"
forecast_mask = df.data_type .== "forecast"
pf_rev = df[
pi_rev = df[
cap_mask .& forecast_mask .& (df.lookahead .== "Perfect Foresight"),
:revenue,
]
pi_rev = df[cap_mask .& actual_mask .& lk_mask, :revenue]
pl_rev = df[cap_mask .& actual_mask .& lk_mask, :revenue]
v_pl = pl_rev - df[cap_mask .& forecast_mask .& lk_mask, :revenue]
v_pi = pi_rev - df[cap_mask .& forecast_mask .& lk_mask, :revenue]
v_pf = pf_rev - df[cap_mask .& forecast_mask .& lk_mask, :revenue]
v_pi_percentage_pf = @. v_pi / pf_rev * 100
v_pf_percentage_pf = @. v_pf / pf_rev * 100
v_pl_percentage_pi = @. v_pl / pi_rev * 100
v_pi_percentage_pi = @. v_pi / pi_rev * 100
push!(power_caps, cap)
push!(data, lookahead)
push!(v_pl_abs, v_pl[])
push!(v_pi_abs, v_pi[])
push!(v_pf_abs, v_pf[])
push!(v_pi_percentage, v_pi_percentage_pf[])
push!(v_pf_percentage, v_pf_percentage_pf[])
push!(v_pl_percentage, v_pl_percentage_pi[])
push!(v_pi_percentage, v_pi_percentage_pi[])
end
end
return DataFrame(
:formulation => fill(unique(df.formulation)[], length(power_caps)),
:energy_capacity => fill(unique(df.energy_capacity)[], length(power_caps)),
:power_capacity => power_caps,
:lookahead => data,
:vpl_abs => v_pl_abs,
:vpi_abs => v_pi_abs,
:vpf_abs => v_pf_abs,
:vpl_per => v_pl_percentage,
:vpi_per => v_pi_percentage,
:vpf_per => v_pf_percentage,
)
end

"""
Summarises results for each simulated formulation and then calculates values of perfect
information and foresight.
lookahead and information.
For each state, this function cycles through each simulated formulation and:
1. Calculates summary results (i.e. annual revenue and mean relative gap)
2. Calculates the value of perfect information and value of perfect foresight
2. Calculates the value of perfect lookahead and value of perfect information
For a single state, the VPIs and VPFs across simulated formulations are then released
For a single state, the VPLs and VPIs across simulated formulations are then released
information a JLD2 file in the `results` folder, along with summary results for each
simulated formulation.
Expand All @@ -186,11 +191,11 @@ simulated formulation.
# Returns
`Dict` mapping each state to `DataFrame` with VPI and VPF for each formulation and
`Dict` mapping each state to `DataFrame` with VPL and VPI for each formulation and
lookahead.
"""
function calculate_summaries_and_vpi_vpf_across_scenarios(sim_folder::String)
function _calculate_summary_vpi_vpf_for_formulation(
function calculate_summaries_and_vpl_vpi_across_scenarios(sim_folder::String)
function _calculate_summary_vpl_vpi_for_formulation(
sim_folder::String,
formulation::String,
file::String,
Expand All @@ -201,31 +206,31 @@ function calculate_summaries_and_vpi_vpf_across_scenarios(sim_folder::String)
data = load(joinpath(results_path, file))
@info "Calculating summary information for $state $formulation"
summary = _summarise_simulations(data, formulation, energy)
@info "Calculating VPI and VPF for $state $formulation"
return summary, calculate_vpi_vpf(summary)
@info "Calculating VPL and VPI for $state $formulation"
return summary, calculate_vpl_vpi(summary)
end

save_path = joinpath("results", "data")
if !isdir(save_path)
mkpath(save_path)
end
categorisation = _categorise_simulation_results(sim_folder)
states_vpi_vpf = Dict{String,DataFrame}()
states_vpl_vpi = Dict{String,DataFrame}()
for state in keys(categorisation)
summary_data = Dict{String,DataFrame}()
vpi_vpf_data = DataFrame[]
vpl_vpi_data = DataFrame[]
formulation_results = categorisation[state]
for (formulation, results) in pairs(formulation_results)
if length(results) == 1
file = results[]
energy = parse(
Float64, match(r"[A-Z]{2,3}_([0-9\.]*)MWh_.*", file).captures[]
)
(summary, vpi_vpf) = _calculate_summary_vpi_vpf_for_formulation(
(summary, vpl_vpi) = _calculate_summary_vpl_vpi_for_formulation(
sim_folder, formulation, file, energy, state
)
vpi_vpf[!, :param] = fill(missing, size(vpi_vpf, 1))
push!(vpi_vpf_data, vpi_vpf)
vpl_vpi[!, :param] = fill(missing, size(vpl_vpi, 1))
push!(vpl_vpi_data, vpl_vpi)
summary_data["$(formulation)"] = summary
else
for file in results
Expand All @@ -234,11 +239,11 @@ function calculate_summaries_and_vpi_vpf_across_scenarios(sim_folder::String)
)
energy = parse(Float64, energy_param_capture.captures[1])
param = string(energy_param_capture.captures[2])
(summary, vpi_vpf) = _calculate_summary_vpi_vpf_for_formulation(
(summary, vpl_vpi) = _calculate_summary_vpl_vpi_for_formulation(
sim_folder, formulation, file, energy, state
)
vpi_vpf[!, :param] = fill(param, size(vpi_vpf, 1))
push!(vpi_vpf_data, vpi_vpf)
vpl_vpi[!, :param] = fill(param, size(vpl_vpi, 1))
push!(vpl_vpi_data, vpl_vpi)
summary_data["$(formulation)/$(param)"] = summary
end
end
Expand All @@ -249,12 +254,12 @@ function calculate_summaries_and_vpi_vpf_across_scenarios(sim_folder::String)
f[key] = value
end
end
state_vpi_vpf = vcat(vpi_vpf_data...)
@info "Saving VPI and VPF data for $state"
jldopen(joinpath(save_path, "vpi_vpf.jld2"), "w"; compress=true) do f
f["$(state)"] = state_vpi_vpf
state_vpl_vpi = vcat(vpl_vpi_data...)
@info "Saving VPL and VPI data for $state"
jldopen(joinpath(save_path, "vpl_vpi.jld2"), "w"; compress=true) do f
f["$(state)"] = state_vpl_vpi
end
states_vpi_vpf[state] = state_vpi_vpf
states_vpl_vpi[state] = state_vpl_vpi
end
return states_vpi_vpf
return states_vpl_vpi
end

0 comments on commit 12e1d11

Please sign in to comment.