Add Unserved energy Conditional Value at Risk (CVAR) metric#100
Add Unserved energy Conditional Value at Risk (CVAR) metric#100abdelrahman-ayad wants to merge 51 commits into
Conversation
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #100 +/- ##
==========================================
+ Coverage 83.13% 83.21% +0.07%
==========================================
Files 45 45
Lines 2325 2431 +106
==========================================
+ Hits 1933 2023 +90
- Misses 392 408 +16 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
…capacity calculations
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 13 out of 13 changed files in this pull request and generated 8 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Agent-Logs-Url: https://github.com/NatLabRockies/PRAS/sessions/012d8c7b-67f5-45ad-82ee-c3db6a6e2379 Co-authored-by: abdelrahman-ayad <70848794+abdelrahman-ayad@users.noreply.github.com>
|
Just as a heads up, I was blocked by some firewall rules while working on your feedback. Expand below for details. Warning Firewall rules blocked me from connecting to one or more addresses (expand for details)I tried to connect to the following addresses, but was blocked by firewall rules:
If you need me to access, download, or install something from one of these locations, you can either:
|
| function MeanEstimate(xs::AbstractArray{<:Real}) | ||
| est = mean(xs) | ||
| return MeanEstimate(est, std(xs, mean=est), length(xs)) | ||
| if length(xs) > 1 |
There was a problem hiding this comment.
Why do we need this? When is MeanEstimate calculated with a sample size of 1?
There was a problem hiding this comment.
This is meant if the tail contains one sample beyond the alpha, which may happen in theory with a very high alpha
|
|
||
| end | ||
|
|
||
| function CVAR(unit::Type{U}, x::ShortfallResult{N,L,T,E}, alpha::Float64) where {N,L,T,E,U<:EnergyUnit} |
There was a problem hiding this comment.
As we discussed at the meeting, I think using a symbol, rather than the unit here will be better.
There was a problem hiding this comment.
Addressed in the PR revision.
| var = div(cvar.var, demand/1e6) | ||
| else | ||
| ncvar = MeanEstimate(0.) | ||
| var = MeanEstimate(0.) |
There was a problem hiding this comment.
Just 0. right? Since var is a number and not an estimate, and the signature calls for MeanEstimate.
|
|
||
| estimate = x.shortfall_samples | ||
| var = quantile(estimate, alpha) | ||
| tail_losses = estimate[estimate .>= var] |
There was a problem hiding this comment.
From claude.. do we need to think about this?
every CVAR method does estimate[estimate .>= quantile(estimate, alpha)]. UE samples are mostly zero in healthy systems, so the 95th-percentile threshold is often 0 and the "tail" silently becomes the whole sample (CVAR ≈ EUE, not a tail metric). Use > and handle the empty case, or compute the tail by index after sorting.
There was a problem hiding this comment.
Updated in the PR revision.
|
|
||
| if demand > 0 | ||
| ncvar = div(cvar.cvar, demand/1e6) | ||
| var = div(cvar.var, demand/1e6) |
There was a problem hiding this comment.
From claude again
But cvar.var::Float64, so div(::Float64, ::Float64) is Julia's floor-division. NCVAR's VaR component silently truncates to integers.
Do you intend for this to be an integer?
There was a problem hiding this comment.
I wanted to follow similar approach to NEUE calculations here.
| """ | ||
| struct CVAR{N,L,T<:Period,E<:EnergyUnit} <: ReliabilityMetric | ||
|
|
||
| unit::Type{<:EnergyUnit} |
There was a problem hiding this comment.
Why do we have this in the CVAR type? Just to distinguish energy CVAR vs power CVAR? If we change the symbol, can we do away with this and infer from NLTPE?
There was a problem hiding this comment.
Addressed in the PR revision.
|
|
||
| end | ||
|
|
||
| function CVAR(unit::Type{U}, x::ShortfallSamplesResult{N,L,T,P,E}, alpha::Float64) where {N,L,T,P,E,U<:EnergyUnit} |
There was a problem hiding this comment.
There are six near-identical CVAR bodies here and in Shortfall.jl (4 methods in this file + 2 in Shortfall.jl). Every one is:
var = quantile(estimate, alpha)
tail = estimate[estimate .>= var]
cvar = isempty(tail) ? MeanEstimate(0.) : MeanEstimate(tail)
return CVAR{N,L,T,E}(unit, cvar, alpha, var)Consider pulling this into a single helper, e.g.
function _cvar(estimate::AbstractVector{<:Real}, alpha::Float64)
var = quantile(estimate, alpha)
tail = estimate[estimate .>= var]
cvar = isempty(tail) ? MeanEstimate(0.) : MeanEstimate(tail)
return cvar, var
endThe dispatched CVAR(...) methods then only differ in how estimate is sliced (x[], x[r], x[t], x[r, t], x.shortfall_samples, x.shortfall_region_samples[i_r, :]) and become a couple of lines each. Same pattern applies to the 4 NCVAR methods — they're identical across Shortfall.jl and ShortfallSamples.jl and could collapse to a single NCVAR(::AbstractShortfallResult, ::CVAR, ...) defined once in Results.jl.
This also makes it much easier to fix the tail-selection / empty-tail behavior in one place rather than six.
There was a problem hiding this comment.
Thanks for the suggestion, helper functions are added.
Summary
This PR adds the Conditional Value at Risk (CVAR) metric to quantify tail-risk shortfalls in resource adequacy assessments. CVAR measures the expected unserved energy across the worst (1−α) simulation samples, where α is a user-defined confidence level (e.g., 0.95).
This PR implements CVAR for unserved energy on both
ShortfallResultandShortfallSamplesResult. CVAR for other dimensions (duration, magnitude) is out of scope and left for a follow-up.Methodology
CVAR is computed as the weighted average of total unserved energy samples that exceed the α-th percentile (VAR). For
ShortfallResult, an additional vector of per-sample total unserved energy is accumulated during simulation and discarded after CVAR computation (Type 1 in figure below).implementation
Performance testing
@benchmarktesting forShortfallandShortfallSamples. There is a slight increase in memory use in theShortfalldue to the additional vector storing the total unserved energy across all samples.