-
Notifications
You must be signed in to change notification settings - Fork 16
Add CVAR and NCVAR resource adequacy reporting #201
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
Open
bcakire
wants to merge
13
commits into
main
Choose a base branch
from
bc_cvar_review_fixes
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
13 commits
Select commit
Hold shift + click to select a range
45b2ffd
Add CVAR and NCVAR resource adequacy reporting
3b05710
revert runreeds.py
bsergi 8e761fc
Merge remote-tracking branch 'origin/main' into bs/cvar
bsergi 7e560cd
revert cases_test.csv
bsergi 3a7626c
remove CVAR from GSw_PRM_StressThresholdMetrics
bsergi 48f41eb
Always calculate CVAR metric
bsergi c5995a9
revert back to new groupby syntax
bsergi 17089a7
move up CVAR switch compatability check
bsergi 3735a61
change to GSw_PRM_CVARalpha
bsergi 91730d2
Missed a change to GSw_PRM_CVARalpha
bsergi 081086f
Remove Julia-side CVAR dependency
fad7283
Merge remote-tracking branch 'origin/main' into bc_cvar_review_fixes
6a95ef2
Restore pandas 3 hourly frequency syntax
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -143,6 +143,51 @@ def calc_neue(dfeue_agg, dfload_agg): | |||||||||
| return neue | ||||||||||
|
|
||||||||||
|
|
||||||||||
| def get_shortfall_totals_by_sample(case, t, iteration=0): | ||||||||||
| filepath = os.path.join(case, 'handoff', 'PRAS', f'PRAS_{t}i{iteration}-shortfall_totals_by_sample.h5') | ||||||||||
| if not os.path.isfile(filepath): | ||||||||||
| raise FileNotFoundError(f"{filepath} not found. Re-run PRAS with --write_shortfall_samples_totals 1.") | ||||||||||
| df = reeds.io.read_pras_results(filepath) | ||||||||||
| df.columns = df.columns.astype(str) | ||||||||||
| if 'sample' in df.columns: | ||||||||||
| df = df.set_index('sample') | ||||||||||
| elif df.index.name != 'sample': | ||||||||||
| df.index = pd.RangeIndex(1, len(df) + 1, name='sample') | ||||||||||
| return df.apply(pd.to_numeric, errors='coerce').clip(lower=0) | ||||||||||
|
|
||||||||||
|
|
||||||||||
| def _sample_cvar(samples, alpha=0.95): | ||||||||||
| x = pd.Series(samples).dropna().astype(float) | ||||||||||
| if x.empty: | ||||||||||
| return np.nan | ||||||||||
|
|
||||||||||
| # Round before applying ceil to remove floating-point noise. | ||||||||||
| # For example, a mathematically exact tail size of 50 may be | ||||||||||
| # represented as 50.00000000000004, which would otherwise select 51 samples. | ||||||||||
| tail_size = round((1 - alpha) * len(x), 12) | ||||||||||
| n_tail = max(1, int(np.ceil(tail_size))) | ||||||||||
|
|
||||||||||
| return x.nlargest(n_tail).mean() | ||||||||||
|
|
||||||||||
| def calc_cvar(shortfall_samples_agg, alpha=0.95): | ||||||||||
| """ | ||||||||||
| CVAR from total shortfall by PRAS sample. | ||||||||||
| """ | ||||||||||
| cvar = shortfall_samples_agg.apply( | ||||||||||
| lambda s: _sample_cvar(s, alpha=alpha), | ||||||||||
| axis=0, | ||||||||||
| ) | ||||||||||
| return cvar | ||||||||||
|
|
||||||||||
|
|
||||||||||
| def calc_ncvar(cvar, dfload_agg): | ||||||||||
| """ | ||||||||||
| NCVAR = CVAR / total load, in ppm. | ||||||||||
| """ | ||||||||||
| ncvar = cvar / dfload_agg.sum().reindex(cvar.index) * 1e6 | ||||||||||
| return ncvar | ||||||||||
|
|
||||||||||
|
|
||||||||||
| def calc_peak_eue(dfeue_agg, dfload_agg, norm:Literal['peak','hourly','absolute']='peak'): | ||||||||||
| """ | ||||||||||
| Get the peak hourly outage magnitude | ||||||||||
|
|
@@ -192,6 +237,12 @@ def calc_ra_metrics( | |||||||||
| sw = reeds.io.get_switches(case) | ||||||||||
| numyears = len(sw.resource_adequacy_years_list) | ||||||||||
|
|
||||||||||
| ### Get total shortfall samples for CVAR calculation | ||||||||||
| shortfall_samples = ( | ||||||||||
| get_shortfall_totals_by_sample(case=case, t=t, iteration=iteration) | ||||||||||
| .drop(columns=['USA'], errors='ignore') | ||||||||||
| ) | ||||||||||
|
|
||||||||||
| ### Loop over aggregation levels and calculate all metrics | ||||||||||
| ra_metrics = {} | ||||||||||
| for level in levels: | ||||||||||
|
|
@@ -212,6 +263,18 @@ def calc_ra_metrics( | |||||||||
| ra_metrics[level, 'euemax_peakloadfrac'] = calc_peak_eue(dfeue_agg, dfload_agg, 'peak') | ||||||||||
| ra_metrics[level, 'euemax_hourlyloadfrac'] = calc_peak_eue(dfeue_agg, dfload_agg, 'hourly') | ||||||||||
| ra_metrics[level, 'euemax_mw'] = calc_peak_eue(dfeue_agg, dfload_agg, 'absolute') | ||||||||||
| ## Calculate tail-based metrics (CVAR and NCVAR) | ||||||||||
| regions = [c for c in shortfall_samples.columns if c in rmap.index] | ||||||||||
| if len(regions): | ||||||||||
| shortfall_samples_agg = ( | ||||||||||
| shortfall_samples[regions] | ||||||||||
| .rename(columns=rmap) | ||||||||||
| .T.groupby(level=0) | ||||||||||
| .sum().T | ||||||||||
| ) | ||||||||||
| cvar = calc_cvar(shortfall_samples_agg, alpha=float(sw.GSw_PRM_CVARalpha)) | ||||||||||
| ra_metrics[level, 'cvar_mwh_peryear'] = cvar / numyears | ||||||||||
| ra_metrics[level, 'ncvar_ppm'] = calc_ncvar(cvar, dfload_agg) | ||||||||||
|
|
||||||||||
| ### Combine it | ||||||||||
| dfout = pd.concat(ra_metrics, names=['level','metric','region']).rename('value') | ||||||||||
|
|
@@ -268,7 +331,7 @@ def get_longest_events( | |||||||||
| dates = [] | ||||||||||
| for i, row in eue_events.iterrows(): | ||||||||||
| dates.append( | ||||||||||
| pd.Series(index=pd.date_range(row.start, row.end, freq='h'), data=1) | ||||||||||
| pd.Series(index=pd.date_range(row.start, row.end, freq='H'), data=1) | ||||||||||
|
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. Looks like another one to revert
Suggested change
|
||||||||||
| .resample('D').count() | ||||||||||
| ) | ||||||||||
| if len(dates): | ||||||||||
|
|
@@ -511,13 +574,12 @@ def get_stress_periods(case, sw, t, iteration): | |||||||||
| for i,row in stressperiods_this_iteration.iterrows() | ||||||||||
| ] | ||||||||||
| covered_hours = [i for sublist in covered_hours for i in sublist] | ||||||||||
|
|
||||||||||
| ### Check all stress criteria; for regions that fail, add new stress periods | ||||||||||
|
Comment on lines
-514
to
-515
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. Let's keep this comment
Suggested change
|
||||||||||
| _failed = {} | ||||||||||
| _high_stress_periods = {} | ||||||||||
| _shoulder_periods = {} | ||||||||||
|
|
||||||||||
| stress_metrics = [i.lower() for i in sw.GSw_PRM_StressThresholdMetrics.split('/')] | ||||||||||
|
|
||||||||||
| for stress_metric in stress_metrics: | ||||||||||
| switch = RA_SWITCHES[stress_metric] | ||||||||||
| for criterion in sw[switch].split('/'): | ||||||||||
|
|
||||||||||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
It sounds like
PRAS.ShortfallSamples()increases memory use within PRAS, but NatLabRockies/PRAS#109 fixes it. Can we check the PRAS memory use on a full-US run on this branch and on main before merging (I think Kodi has done this before)? If it goes up a lot, we should probably wait to merge this PR until we can switch to a new PRAS release that includes this PRAS PR.