#13430: Exclude zero values from ensemble statistics for derived vectors

Add m_includeZeroValues option to RimEnsembleStatistics (default on, preserving existing behavior). When disabled, exact zero values (e.g. from shut-in wells/groups) are excluded from the mean, P10/P50/P90 and custom percentile calculations in RimEnsembleStatisticsCase::calculate(). This is useful for derived vectors such as WCT, GOR and PI, where zeros caused by a shut well/group should not skew the statistical curves.

Exact equality (not an epsilon) is used, since shut-in values are reported as exact 0.0 by the simulator and non-accumulated vectors use STEP_RIGHT interpolation, which does not introduce floating-point noise.
This commit is contained in:
Magne Sjaastad committed 2026-10-22 13:34:34 +02:00
1 parent 1d331f6cc1
commit 7b580694ed
7 files changed
+164 -4

No files matched your search

@@ -2233,7 +2233,11 @@ void RimEnsembleCurveSet::updateStatisticsCurves( const std::vector<RimSummaryCa
}
else
{
m_ensembleStatCaseY->calculate( statCases, summaryAddressY(), m_statistics->includeIncompleteCurves(), percentiles );
m_ensembleStatCaseY->calculate( statCases,
summaryAddressY(),
m_statistics->includeIncompleteCurves(),
percentiles,
m_statistics->includeZeroValues() );
}
}
@@ -61,6 +61,7 @@ RimEnsembleStatistics::RimEnsembleStatistics( RimEnsembleCurveSetInterface* pare
m_customPercentiles.setCustomValidationCallback( [this]() -> QString { return validateCustomPercentiles(); } );
CAF_PDM_InitField( &m_showCurveLabels, "ShowCurveLabels", true, "Show Curve Labels" );
CAF_PDM_InitField( &m_includeIncompleteCurves, "IncludeIncompleteCurves", false, "Include Incomplete Curves" );
CAF_PDM_InitField( &m_includeZeroValues, "IncludeZeroValues", true, "Include Zero Values" );
CAF_PDM_InitField( &m_crossPlotCurvesBinCount, "CrossPlotCurvesBinCount", 100, "Bin Count" );
CAF_PDM_InitField( &m_crossPlotCurvesStatisticsRealizationCountThresholdPerBin,
@@ -241,6 +242,14 @@ bool RimEnsembleStatistics::includeIncompleteCurves() const
return m_includeIncompleteCurves;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
bool RimEnsembleStatistics::includeZeroValues() const
{
return m_includeZeroValues;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
@@ -302,6 +311,7 @@ void RimEnsembleStatistics::defaultUiOrdering( bool showCrossPlotGroup, caf::Pdm
uiOrdering.add( &m_showEnsembleCurves );
uiOrdering.add( &m_basedOnFilteredCases );
uiOrdering.add( &m_includeIncompleteCurves );
uiOrdering.add( &m_includeZeroValues );
uiOrdering.add( &m_showCurveLabels );
if ( showCrossPlotGroup )
@@ -55,6 +55,8 @@ public:
void enableIncludeIncompleteCurves( bool enable );
bool includeZeroValues() const;
cvf::Color3f color() const { return m_color; }
void setColor( const cvf::Color3f& color );
bool customColor() const;
@@ -93,6 +95,7 @@ private:
caf::PdmField<QString> m_customPercentiles;
caf::PdmField<bool> m_showCurveLabels;
caf::PdmField<bool> m_includeIncompleteCurves;
caf::PdmField<bool> m_includeZeroValues;
caf::PdmField<bool> m_customColor;
// Ensemble cross plot settings
@@ -168,9 +168,11 @@ RifSummaryReaderInterface* RimEnsembleStatisticsCase::summaryReader()
void RimEnsembleStatisticsCase::calculate( const std::vector<RimSummaryCase*>& summaryCases,
const RifEclipseSummaryAddress& inputAddress,
bool includeIncompleteCurves,
const std::vector<int>& percentiles )
const std::vector<int>& percentiles,
bool includeZeroValues )
{
auto hash = RiaHashTools::hash( summaryCases, inputAddress.toEclipseTextAddress(), includeIncompleteCurves, percentiles );
auto hash =
RiaHashTools::hash( summaryCases, inputAddress.toEclipseTextAddress(), includeIncompleteCurves, percentiles, includeZeroValues );
if ( hash == m_hash ) return;
auto startTime = RiaLogging::currentTime();
@@ -266,6 +268,15 @@ void RimEnsembleStatisticsCase::calculate( const std::vector<RimSummaryCase*>& s
valuesAtTimeStep.push_back( curveValues[curveIdx][timeStepIndex] );
}
if ( !includeZeroValues )
{
// Shut-in wells/groups report exact 0.0 for rate/ratio vectors (e.g. WCT, GOR, PI) from the simulator, and non-accumulated
// vectors use STEP_RIGHT interpolation above, which copies recorded values without introducing floating-point noise. An
// epsilon comparison is therefore not needed, and could incorrectly exclude small-but-valid values for low-magnitude
// vectors such as PI.
valuesAtTimeStep.erase( std::remove( valuesAtTimeStep.begin(), valuesAtTimeStep.end(), 0.0 ), valuesAtTimeStep.end() );
}
double mean = RigStatisticsMath::calculateMean( valuesAtTimeStep );
m_meanData.push_back( mean );
@@ -49,7 +49,8 @@ public:
void calculate( const std::vector<RimSummaryCase*>& summaryCases,
const RifEclipseSummaryAddress& inputAddress,
bool includeIncompleteCurves,
const std::vector<int>& percentiles = { 10, 50, 90 } );
const std::vector<int>& percentiles = { 10, 50, 90 },
bool includeZeroValues = true );
std::vector<time_t> timeSteps( const RifEclipseSummaryAddress& resultAddress ) const override;
std::pair<bool, std::vector<double>> values( const RifEclipseSummaryAddress& resultAddress ) const override;