-
Notifications
You must be signed in to change notification settings - Fork 10
Bc cvar updates #107
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: aa/multi_metrics
Are you sure you want to change the base?
Bc cvar updates #107
Changes from all commits
29b6be0
33d2a02
e0ae25e
9bc6424
6aec186
65fd43a
e41316f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,5 +1,6 @@ | ||
| #%% Imports | ||
| import ArgParse | ||
| import CSV | ||
| import DataFrames | ||
| import Logging | ||
| import LoggingExtras | ||
|
|
@@ -70,10 +71,20 @@ function parse_commandline() | |
| default = 0 | ||
| required = false | ||
| "--write_shortfall_samples" | ||
| help = "Write the sample-level shortfall" | ||
| help = "Write per-sample hourly shortfall by region" | ||
| arg_type = Int | ||
| default = 0 | ||
| required = false | ||
| "--write_shortfall_samples_totals" | ||
| help = "Write per-sample total shortfall by region over the full PRAS time period" | ||
| arg_type = Int | ||
| default = 0 | ||
| required = false | ||
| "--cvar_alpha" | ||
| help = "Alpha for CVaR (e.g., 0.95)" | ||
| arg_type = Float64 | ||
| default = 0.95 | ||
| required = false | ||
| "--write_availability_samples" | ||
| help = "Write the sample-level generator and storage availability" | ||
| arg_type = Int | ||
|
|
@@ -182,7 +193,7 @@ function run_pras(pras_system_path::String, args::Dict) | |
| if args["write_energy"] == 1 | ||
| resultspec["energy"] = PRAS.StorageEnergy() | ||
| end | ||
| if args["write_shortfall_samples"] == 1 | ||
| if args["write_shortfall_samples"] == 1 || args["write_shortfall_samples_totals"] == 1 | ||
| resultspec["short_samples"] = PRAS.ShortfallSamples() | ||
| end | ||
| if args["write_availability_samples"] == 1 | ||
|
|
@@ -208,6 +219,41 @@ function run_pras(pras_system_path::String, args::Dict) | |
| @info "$(PRAS.EUE(results["short"]))" | ||
| @info "$(PRAS.NEUE(results["short"]))" | ||
|
|
||
| # CVAR results | ||
| if haskey(results, "short_samples") | ||
| _,_,_,_,energyunit = PRAS.get_params(sys) | ||
| alpha = Float64(args["cvar_alpha"]) | ||
| cvar_obj = PRAS.CVAR(energyunit, results["short_samples"], alpha) | ||
|
|
||
| cvar_value = PRAS.val(cvar_obj.cvar) | ||
| cvar_stderr = PRAS.stderror(cvar_obj.cvar) | ||
| cvar_var = cvar_obj.var | ||
|
|
||
| ### Normalize CVaR by total load over the full PRAS time period and report NCVAR in ppm. | ||
| total_load = sum(sys.regions.load) | ||
| ncvar_value = cvar_value / total_load * 1e6 | ||
| ncvar_stderr = cvar_stderr / total_load * 1e6 | ||
| ncvar_var = cvar_var / total_load * 1e6 | ||
|
|
||
| @info "CVAR = $(cvar_value)±$(cvar_stderr) MWh; VaR = $(cvar_var) MWh; alpha = $(alpha)" | ||
| @info "NCVAR = $(ncvar_value)±$(ncvar_stderr) ppm; VaR = $(ncvar_var) ppm; alpha = $(alpha)" | ||
|
|
||
| ### Write risk metrics to CSV | ||
| base = replace(pras_system_path, ".pras"=>"") | ||
| riskfile = base * "-risk_metrics.csv" | ||
| dfrisk = DF.DataFrame( | ||
| metric = ["CVAR", "NCVAR"], | ||
| alpha = [alpha, alpha], | ||
| region = ["USA", "USA"], | ||
| unit = ["MWh", "ppm"], | ||
| value = [cvar_value, ncvar_value], | ||
| stderr = [cvar_stderr, ncvar_stderr], | ||
| var = [cvar_var, ncvar_var], | ||
| ) | ||
| CSV.write(riskfile, dfrisk) | ||
| @info("Wrote risk metrics to $(riskfile)") | ||
| end | ||
|
|
||
| ## Filter out DC regions used for VSC HVDC transmission | ||
| regions = [r for r in sys.regions.names if !(occursin("|", r))] | ||
|
|
||
|
|
@@ -285,6 +331,7 @@ function run_pras(pras_system_path::String, args::Dict) | |
| end | ||
| @info("Wrote PRAS surplus to $(surplusfile)") | ||
| end | ||
|
|
||
| ### Storage energy | ||
| if args["write_energy"] == 1 | ||
| dfenergy = DF.DataFrame() | ||
|
|
@@ -302,31 +349,41 @@ function run_pras(pras_system_path::String, args::Dict) | |
| @info("Wrote PRAS storage energy to $(energyfile)") | ||
| end | ||
|
|
||
| ### Sample-level shortfall | ||
| ### Per-sample hourly shortfall by region | ||
| if args["write_shortfall_samples"] == 1 | ||
| dictshort = Dict(s => DF.DataFrame() for s = 1:args["samples"]) | ||
| for s in range(1, args["samples"]) | ||
| dictshort[s] = DF.DataFrame( | ||
| transpose(getindex.(results["short_samples"][:, :], s)), | ||
| sys.regions.names | ||
| ) | ||
| # subset to regions (filter out DC regions) | ||
| dictshort[s] = dictshort[s][:,findall(regions .∈ Ref(sys.regions.names))] | ||
| end | ||
| ## Write it | ||
| sf = results["short_samples"] | ||
| ## Use filtered system regions to avoid DC converter pseudo-regions without load. | ||
| region_names = sf.regions.names | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. the previous code filtered using |
||
| idx = [findfirst(==(r), region_names) for r in regions] | ||
|
|
||
| shortfile = replace(outfile, ".h5"=>"-shortfall_samples.h5") | ||
| HDF5.h5open(shortfile, "w") do f | ||
| ## Create a group for each sample. Within each group, write an array for each region. | ||
| for s in range(1, args["samples"]) | ||
| for s in 1:args["samples"] | ||
| HDF5.create_group(f, "$s") | ||
| for column in DF._names(dictshort[s]) | ||
| f["$s"]["$column", compress=4] = convert(Array, dictshort[s][!, column]) | ||
| for (r, i_r) in zip(regions, idx) | ||
| arr = Float64.(sf.shortfall[i_r, :, s]) | ||
| f["$s"]["$r", compress=4] = arr | ||
| end | ||
| end | ||
| end | ||
| @info("Wrote PRAS shortfall by sample to $(shortfile)") | ||
| end | ||
|
|
||
| ### Per-sample total shortfall by region over the full PRAS time period, needed for CVaR | ||
| if args["write_shortfall_samples_totals"] == 1 | ||
| sf = results["short_samples"] | ||
| ## Use filtered system regions to avoid DC converter pseudo-regions without load. | ||
| totalsfile = replace(outfile, ".h5"=>"-shortfall_totals_by_sample.h5") | ||
| HDF5.h5open(totalsfile, "w") do f | ||
| f["sample", compress=4] = collect(1:args["samples"]) | ||
| f["USA", compress=4] = Float64.(sf[]) | ||
| for r in regions | ||
| f["$r", compress=4] = Float64.(sf[r]) | ||
| end | ||
| end | ||
| @info("Wrote PRAS shortfall totals by sample to $(totalsfile)") | ||
| end | ||
|
|
||
| ### Sample-level generator and storage availability | ||
| if args["write_availability_samples"] == 1 | ||
| dictavail = Dict(s => DF.DataFrame() for s = 1:args["samples"]) | ||
|
|
@@ -457,6 +514,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ | |
| # "write_surplus" => 0, | ||
| # "write_energy" => 0, | ||
| # "write_shortfall_samples" => 1, | ||
| # "write_shortfall_samples_totals" => 1, | ||
| # "write_availability_samples" => 0, | ||
| # "overwrite" => 1, | ||
| # "debug" => 0, | ||
|
|
@@ -466,6 +524,7 @@ if abspath(PROGRAM_FILE) == @__FILE__ | |
| # "pras_existing_unit_size" => 1, | ||
| # "pras_max_unitsize_prm" => 1, | ||
| # "pras_seed" => 1, | ||
| # "cvar_alpha" => 0.95, | ||
| # ) | ||
| # reedscase = args["reedscase"] | ||
| # solve_year = args["solve_year"] | ||
|
|
@@ -478,11 +537,11 @@ if abspath(PROGRAM_FILE) == @__FILE__ | |
|
|
||
| #%% Include ReEDS2PRAS | ||
| include(joinpath( | ||
| args["reedscase"], "reeds", "resource_adequacy", "reeds2pras", "src", "ReEDS2PRAS.jl" | ||
| args["reeds_path"], "reeds", "resource_adequacy", "reeds2pras", "src", "ReEDS2PRAS.jl" | ||
| )) | ||
|
|
||
| #%% Run it | ||
| main(args) | ||
|
|
||
| #%% | ||
| end | ||
| end | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you add a comment describing the calculation of these normalized metrics?