Bc cvar updates#107
Conversation
There was a problem hiding this comment.
Here are some initial thoughts from a quick look. Happy to touch base when you get back to discuss any of these. Let me know when you've taken another pass here and I can review again.
One high-level comment: can you organize the PR summary text a bit more? The technical details section has a lot of information but is a bit difficult to follow. Also, I didn't quite understand your test output GSw_PRM_StressThreshold = country_-1_NCVAR_cvar failed. What does it mean to have a -1 threshold? Ultimately for the PR we'll also want a comparison of default case from cases.csv from both this branch and main to show there weren't any changes.
There was a problem hiding this comment.
revert the changes to this file before merging
| required = false | ||
| "--write_shortfall_samples" | ||
| help = "Write the sample-level shortfall" | ||
| help = "Write the sample-level shortfall (hourly, large)" |
There was a problem hiding this comment.
What are the large and small designations? I might suggest just "Write per-sample hourly shortfall by region" for this one and "Write per-sample total shortfall by region" for the one below.
| default = 0 | ||
| required = false | ||
| "--write_shortfall_samples_totals" | ||
| help = "Write per-sample total shortfall by region (small)" |
There was a problem hiding this comment.
this is total over the entire PRAS time period and not annual, right?
| _,_,_,_,energyunit = PRAS.get_params(sys) | ||
| alpha = Float64(args["cvar_alpha"]) | ||
| cvar_obj = PRAS.CVAR(energyunit, results["short_samples"], alpha) | ||
| @info(cvar_obj) |
There was a problem hiding this comment.
I think PRAS is setup so that the above info statements provide context (e.g., @info "$(PRAS.EUE(results["short"]))" results in EUE = 851000±5000 MWh/131400h MWh) being printed). Does this do something similar?
| end | ||
| ## Write it | ||
| sf = results["short_samples"] | ||
| region_names = sf.regions.names |
There was a problem hiding this comment.
the previous code filtered using sys.regions.names. I think this was because there are some pseudo regions for DC converter stations that don't have load and thus don't need to be included here, so we may want to continue to drop them (@patrickbrown4 might be able to weigh in on this one).
| return x.sort_values(ascending=False).iloc[:n_tail].mean() | ||
|
|
||
|
|
||
| def get_annual_cvar_stress_metric(case, t, stress_metric='NCVAR', iteration=0, alpha=0.95): |
There was a problem hiding this comment.
this function and the next one get pretty long, so it would be good to add comments throughout to provide some documentation on what they are doing.
| x = pd.Series(samples).dropna().astype(float) | ||
| if x.empty: | ||
| return np.nan | ||
| if not (0 <= alpha < 1): |
There was a problem hiding this comment.
Feels like this check might fit better in get_cvar_alpha. An even better approach might be to add a check before the ReEDS run which would avoid having runs get this far and then running into an error (best place for that would probably be here:
Line 222 in 1a8ab63
|
|
||
| def get_annual_cvar_stress_metric(case, t, stress_metric='NCVAR', iteration=0, alpha=0.95): | ||
| stress_metric = stress_metric.upper() | ||
| if stress_metric not in CVAR_METRICS: |
There was a problem hiding this comment.
The multi-metrics PR (#99) now drops the metric tag in the switch value; for example, GSw_PRM_StressThresholdNEUE now just has a value of transgrp_1_sum instead of transgrp_1_EUE_sum (see here). I think we could do the same in this PR and then you could drop the CVAR_METRICS and the check and just iterate over whichever switches are activated.
| ) | ||
| for criterion in sw[f'GSw_PRM_StressThreshold{metric}'].split('/'): | ||
| print(f"Evaluating GSw_PRM_StressThreshold {metric} with criterion: {criterion}") | ||
| stress_criteria = _evaluate_stress_threshold_criterion(stress_criteria, criterion, sw, t, iteration, dfenergy_r, stressperiods_this_iteration, |
There was a problem hiding this comment.
This line looks like just a formatting change--I'd suggest keeping as the original had it.
| return pd.concat(_metric, names=['level','metric','region']).rename(stress_metric) | ||
|
|
||
|
|
||
| def evaluate_cvar_target_check(sw, t, iteration=0, stress_metrics=None): |
There was a problem hiding this comment.
Much of this function, including the core check of failed = this_test.loc[this_test > threshold], seems to replicate behavior in _evaluate_stress_threshold_criterion. Do you think we could merge the CVAR treatment into that existing function and just have some control statements to turn off adding new stress periods for now when using CVAR metrics? It's possible having a second function makes the most sense here, but I worry a little bit about maintaining two similar functions.
Summary
This PR adds CVAR/NCVAR reporting and target checks to the PRAS resource adequacy workflow.
Main idea is:
What changed
run_pras.jlAdded:
--cvar_alpha--write_shortfall_samples_totalsWhen
PRAS.ShortfallSamples()is available,run_pras.jlnow writes:PRAS_{t}i{iteration}-risk_metrics.csvPRAS_{t}i{iteration}-shortfall_totals_by_sample.h5risk_metrics.csvincludes:shortfall_totals_by_sample.h5includes total shortfall by sample for USA and regions. This is total over the full PRAS time period, not annual.stress_periods.pyAdded CVAR/NCVAR handling with:
get_cvar_alpha()get_shortfall_totals_by_sample()_sample_cvar()get_annual_cvar_stress_metric()evaluate_cvar_target_check()CVAR/NCVAR are calculated by hierarchy level.
NCVAR is normalized by total PRAS load and reported in ppm.
CVAR/NCVAR are intentionally kept out of the normal stress-period selection loop for now. They are only used for reporting and target checks.
ra_calcs.pyAdded logic so
write_shortfall_samples_totalsis turned on automatically when CVAR/NCVAR checks are requested.Also keeps it on for PRAS-informed PRM updates, since those need sample-level shortfall totals too.
cases.csvAdded/updated these switches:
GSw_PRM_CVARAlphaGSw_PRM_StressThresholdNCVARGSw_PRM_StressThresholdCVARThe standard metric switch format follows
aa/multi_metrics, for example: