From 4d2a86807926199e6f7e6e565ba1b58a7be2a6cb Mon Sep 17 00:00:00 2001 From: Kristian Bendiksen Date: Wed, 16 Sep 2026 13:44:11 +0200 Subject: [PATCH] #14766 Fix potentially uninitialised object used for accumulation RigStatisticsCalculator::mobileVolumeWeightedMean() accumulated into a sum from an out-parameter that callees were not required to write. Every implementation of the per time step overload has early-return paths that leave the value untouched, as does RigWeightedMeanCalc::weightedMeanOverCells, so indeterminate stack memory could be summed and presented in the UI. Return std::optional from the computation layer instead of using an out-parameter, making "value not available" explicit and impossible to ignore: - RigWeightedMeanCalc::weightedMeanOverCells - RigStatisticsCalculator::mobileVolumeWeightedMean and all overrides Time steps without a computable mean no longer contribute to the accumulated sum, and are excluded from the divisor. Previously they would either add garbage or, with a naive zero initialisation, bias the average towards zero. RigStatisticsDataCache keeps the existing HUGE_VAL marker on its public API via value_or(), so behaviour seen by RigHistogramData and the overlay info is unchanged apart from the fix. Also: - Set m_isVolumeWeightedMeanCalculated in the per time step cache, which was never assigned, so the cached value was recomputed on every call. - Remove a duplicated ensureKnownResultLoaded() call in RigEclipseNativeVisibleCellsStatCalc. Fixes #14766 --- .../RigEclipseAllanFaultsStatCalc.cpp | 3 +- .../RigEclipseAllanFaultsStatCalc.h | 14 ++++---- .../RigEclipseNativeStatCalc.cpp | 17 +++++----- .../RigEclipseNativeStatCalc.h | 20 ++++++------ .../RigEclipseNativeVisibleCellsStatCalc.cpp | 25 +++++++-------- .../RigEclipseNativeVisibleCellsStatCalc.h | 20 ++++++------ .../RigFlowDiagStatCalc.cpp | 6 ++-- .../ReservoirDataModel/RigFlowDiagStatCalc.h | 14 ++++---- .../RigFlowDiagVisibleCellsStatCalc.cpp | 6 ++-- .../RigFlowDiagVisibleCellsStatCalc.h | 14 ++++---- .../RigWeightedMeanCalc.cpp | 32 ++++++++----------- .../ReservoirDataModel/RigWeightedMeanCalc.h | 16 ++++++---- .../RigStatisticsCalculator.cpp | 29 ++++++++++------- .../RigStatisticsCalculator.h | 7 ++-- .../RigStatisticsDataCache.cpp | 8 +++-- .../UnitTests/RigStatisticsMath-Test.cpp | 32 +++++++++++++++++-- 16 files changed, 149 insertions(+), 114 deletions(-) diff --git a/ApplicationLibCode/ReservoirDataModel/RigEclipseAllanFaultsStatCalc.cpp b/ApplicationLibCode/ReservoirDataModel/RigEclipseAllanFaultsStatCalc.cpp index 78efaba4eb..c0b4d9e4ac 100644 --- a/ApplicationLibCode/ReservoirDataModel/RigEclipseAllanFaultsStatCalc.cpp +++ b/ApplicationLibCode/ReservoirDataModel/RigEclipseAllanFaultsStatCalc.cpp @@ -98,6 +98,7 @@ size_t RigEclipseAllanFaultsStatCalc::timeStepCount() //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- -void RigEclipseAllanFaultsStatCalc::mobileVolumeWeightedMean( size_t timeStepIndex, double& result ) +std::optional RigEclipseAllanFaultsStatCalc::mobileVolumeWeightedMean( size_t timeStepIndex ) { + return {}; } diff --git a/ApplicationLibCode/ReservoirDataModel/RigEclipseAllanFaultsStatCalc.h b/ApplicationLibCode/ReservoirDataModel/RigEclipseAllanFaultsStatCalc.h index 10d4bac4da..b1a32b4cd6 100644 --- a/ApplicationLibCode/ReservoirDataModel/RigEclipseAllanFaultsStatCalc.h +++ b/ApplicationLibCode/ReservoirDataModel/RigEclipseAllanFaultsStatCalc.h @@ -34,13 +34,13 @@ class RigEclipseAllanFaultsStatCalc : public RigStatisticsCalculator public: RigEclipseAllanFaultsStatCalc( RigNNCData* cellResultsData, const RigEclipseResultAddress& scalarResultIndex ); - void minMaxCellScalarValues( size_t timeStepIndex, double& min, double& max ) override; - void posNegClosestToZero( size_t timeStepIndex, double& pos, double& neg ) override; - void valueSumAndSampleCount( size_t timeStepIndex, double& valueSum, size_t& sampleCount ) override; - void addDataToHistogramCalculator( size_t timeStepIndex, RigHistogramCalculator& histogramCalculator ) override; - void uniqueValues( size_t timeStepIndex, std::set& values ) override; - size_t timeStepCount() override; - void mobileVolumeWeightedMean( size_t timeStepIndex, double& result ) override; + void minMaxCellScalarValues( size_t timeStepIndex, double& min, double& max ) override; + void posNegClosestToZero( size_t timeStepIndex, double& pos, double& neg ) override; + void valueSumAndSampleCount( size_t timeStepIndex, double& valueSum, size_t& sampleCount ) override; + void addDataToHistogramCalculator( size_t timeStepIndex, RigHistogramCalculator& histogramCalculator ) override; + void uniqueValues( size_t timeStepIndex, std::set& values ) override; + size_t timeStepCount() override; + std::optional mobileVolumeWeightedMean( size_t timeStepIndex ) override; private: RigNNCData* m_caseData; diff --git a/ApplicationLibCode/ReservoirDataModel/RigEclipseNativeStatCalc.cpp b/ApplicationLibCode/ReservoirDataModel/RigEclipseNativeStatCalc.cpp index 8a69716502..c79b2d5b37 100644 --- a/ApplicationLibCode/ReservoirDataModel/RigEclipseNativeStatCalc.cpp +++ b/ApplicationLibCode/ReservoirDataModel/RigEclipseNativeStatCalc.cpp @@ -160,7 +160,7 @@ size_t RigEclipseNativeStatCalc::timeStepCount() //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- -void RigEclipseNativeStatCalc::mobileVolumeWeightedMean( size_t timeStepIndex, double& mean ) +std::optional RigEclipseNativeStatCalc::mobileVolumeWeightedMean( size_t timeStepIndex ) { RigEclipseResultAddress mobPorvAddress( RiaDefines::ResultCatType::STATIC_NATIVE, RiaResultNames::mobilePoreVolumeName() ); @@ -168,7 +168,7 @@ void RigEclipseNativeStatCalc::mobileVolumeWeightedMean( size_t timeStepIndex, d // RigCaseCellResultsData::createPlaceholderResultEntries has not been executed if ( !m_resultsData->ensureKnownResultLoaded( mobPorvAddress ) ) { - return; + return {}; } const std::vector& weights = m_resultsData->cellScalarResults( mobPorvAddress, 0 ); @@ -176,11 +176,10 @@ void RigEclipseNativeStatCalc::mobileVolumeWeightedMean( size_t timeStepIndex, d const RigActiveCellInfo* actCellInfo = m_resultsData->activeCellInfo(); - RigWeightedMeanCalc::weightedMeanOverCells( &weights, - &values, - nullptr, - false, - actCellInfo, - m_resultsData->isUsingGlobalActiveIndex( m_eclipseResultAddress ), - &mean ); + return RigWeightedMeanCalc::weightedMeanOverCells( &weights, + &values, + nullptr, + false, + actCellInfo, + m_resultsData->isUsingGlobalActiveIndex( m_eclipseResultAddress ) ); } diff --git a/ApplicationLibCode/ReservoirDataModel/RigEclipseNativeStatCalc.h b/ApplicationLibCode/ReservoirDataModel/RigEclipseNativeStatCalc.h index 5e1dd8edf8..b050f46d36 100644 --- a/ApplicationLibCode/ReservoirDataModel/RigEclipseNativeStatCalc.h +++ b/ApplicationLibCode/ReservoirDataModel/RigEclipseNativeStatCalc.h @@ -37,16 +37,16 @@ class RigEclipseNativeStatCalc : public RigStatisticsCalculator public: RigEclipseNativeStatCalc( RigCaseCellResultsData* cellResultsData, const RigEclipseResultAddress& eclipseResultAddress ); - bool hasPreciseP10p90() const override; - void p10p90CellScalarValues( double& min, double& max ) override; - void p10p90CellScalarValues( size_t timeStepIndex, double& min, double& max ) override; - void minMaxCellScalarValues( size_t timeStepIndex, double& min, double& max ) override; - void posNegClosestToZero( size_t timeStepIndex, double& pos, double& neg ) override; - void valueSumAndSampleCount( size_t timeStepIndex, double& valueSum, size_t& sampleCount ) override; - void addDataToHistogramCalculator( size_t timeStepIndex, RigHistogramCalculator& histogramCalculator ) override; - void uniqueValues( size_t timeStepIndex, std::set& values ) override; - size_t timeStepCount() override; - void mobileVolumeWeightedMean( size_t timeStepIndex, double& mean ) override; + bool hasPreciseP10p90() const override; + void p10p90CellScalarValues( double& min, double& max ) override; + void p10p90CellScalarValues( size_t timeStepIndex, double& min, double& max ) override; + void minMaxCellScalarValues( size_t timeStepIndex, double& min, double& max ) override; + void posNegClosestToZero( size_t timeStepIndex, double& pos, double& neg ) override; + void valueSumAndSampleCount( size_t timeStepIndex, double& valueSum, size_t& sampleCount ) override; + void addDataToHistogramCalculator( size_t timeStepIndex, RigHistogramCalculator& histogramCalculator ) override; + void uniqueValues( size_t timeStepIndex, std::set& values ) override; + size_t timeStepCount() override; + std::optional mobileVolumeWeightedMean( size_t timeStepIndex ) override; private: RigCaseCellResultsData* m_resultsData; diff --git a/ApplicationLibCode/ReservoirDataModel/RigEclipseNativeVisibleCellsStatCalc.cpp b/ApplicationLibCode/ReservoirDataModel/RigEclipseNativeVisibleCellsStatCalc.cpp index 94a515a6b7..3f50d50e78 100644 --- a/ApplicationLibCode/ReservoirDataModel/RigEclipseNativeVisibleCellsStatCalc.cpp +++ b/ApplicationLibCode/ReservoirDataModel/RigEclipseNativeVisibleCellsStatCalc.cpp @@ -145,9 +145,9 @@ size_t RigEclipseNativeVisibleCellsStatCalc::timeStepCount() //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- -void RigEclipseNativeVisibleCellsStatCalc::mobileVolumeWeightedMean( size_t timeStepIndex, double& result ) +std::optional RigEclipseNativeVisibleCellsStatCalc::mobileVolumeWeightedMean( size_t timeStepIndex ) { - if ( !m_caseData ) return; + if ( !m_caseData ) return {}; RigEclipseResultAddress mobPorvAddress( RiaDefines::ResultCatType::STATIC_NATIVE, RiaResultNames::mobilePoreVolumeName() ); @@ -155,24 +155,21 @@ void RigEclipseNativeVisibleCellsStatCalc::mobileVolumeWeightedMean( size_t time // RigCaseCellResultsData::createPlaceholderResultEntries has not been executed if ( !m_caseData->ensureKnownResultLoaded( mobPorvAddress ) ) { - return; + return {}; } - m_caseData->ensureKnownResultLoaded( mobPorvAddress ); - - if ( !m_caseData->hasResultEntry( m_resultAddress ) ) return; - if ( m_caseData->timeStepCount( m_resultAddress ) == 0 ) return; + if ( !m_caseData->hasResultEntry( m_resultAddress ) ) return {}; + if ( m_caseData->timeStepCount( m_resultAddress ) == 0 ) return {}; const std::vector& weights = m_caseData->cellScalarResults( mobPorvAddress, 0 ); const std::vector& values = m_caseData->cellScalarResults( m_resultAddress, timeStepIndex ); const RigActiveCellInfo* actCellInfo = m_caseData->activeCellInfo(); - RigWeightedMeanCalc::weightedMeanOverCells( &weights, - &values, - m_cellVisibilities.p(), - true, - actCellInfo, - m_caseData->isUsingGlobalActiveIndex( m_resultAddress ), - &result ); + return RigWeightedMeanCalc::weightedMeanOverCells( &weights, + &values, + m_cellVisibilities.p(), + true, + actCellInfo, + m_caseData->isUsingGlobalActiveIndex( m_resultAddress ) ); } diff --git a/ApplicationLibCode/ReservoirDataModel/RigEclipseNativeVisibleCellsStatCalc.h b/ApplicationLibCode/ReservoirDataModel/RigEclipseNativeVisibleCellsStatCalc.h index f0d1fca56e..94efd0f9da 100644 --- a/ApplicationLibCode/ReservoirDataModel/RigEclipseNativeVisibleCellsStatCalc.h +++ b/ApplicationLibCode/ReservoirDataModel/RigEclipseNativeVisibleCellsStatCalc.h @@ -39,16 +39,16 @@ public: const RigEclipseResultAddress& scalarResultIndex, const cvf::UByteArray* cellVisibilities ); - bool hasPreciseP10p90() const override; - void p10p90CellScalarValues( double& min, double& max ) override; - void p10p90CellScalarValues( size_t timeStepIndex, double& min, double& max ) override; - void minMaxCellScalarValues( size_t timeStepIndex, double& min, double& max ) override; - void posNegClosestToZero( size_t timeStepIndex, double& pos, double& neg ) override; - void valueSumAndSampleCount( size_t timeStepIndex, double& valueSum, size_t& sampleCount ) override; - void addDataToHistogramCalculator( size_t timeStepIndex, RigHistogramCalculator& histogramCalculator ) override; - void uniqueValues( size_t timeStepIndex, std::set& values ) override; - size_t timeStepCount() override; - void mobileVolumeWeightedMean( size_t timeStepIndex, double& result ) override; + bool hasPreciseP10p90() const override; + void p10p90CellScalarValues( double& min, double& max ) override; + void p10p90CellScalarValues( size_t timeStepIndex, double& min, double& max ) override; + void minMaxCellScalarValues( size_t timeStepIndex, double& min, double& max ) override; + void posNegClosestToZero( size_t timeStepIndex, double& pos, double& neg ) override; + void valueSumAndSampleCount( size_t timeStepIndex, double& valueSum, size_t& sampleCount ) override; + void addDataToHistogramCalculator( size_t timeStepIndex, RigHistogramCalculator& histogramCalculator ) override; + void uniqueValues( size_t timeStepIndex, std::set& values ) override; + size_t timeStepCount() override; + std::optional mobileVolumeWeightedMean( size_t timeStepIndex ) override; private: RigCaseCellResultsData* m_caseData; diff --git a/ApplicationLibCode/ReservoirDataModel/RigFlowDiagStatCalc.cpp b/ApplicationLibCode/ReservoirDataModel/RigFlowDiagStatCalc.cpp index 5b2bc4ec65..0634134faa 100644 --- a/ApplicationLibCode/ReservoirDataModel/RigFlowDiagStatCalc.cpp +++ b/ApplicationLibCode/ReservoirDataModel/RigFlowDiagStatCalc.cpp @@ -115,10 +115,10 @@ size_t RigFlowDiagStatCalc::timeStepCount() //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- -void RigFlowDiagStatCalc::mobileVolumeWeightedMean( size_t timeStepIndex, double& mean ) +std::optional RigFlowDiagStatCalc::mobileVolumeWeightedMean( size_t timeStepIndex ) { auto eclCase = m_resultsData->flowDiagSolution()->firstAncestorOrThisOfType(); - if ( !eclCase ) return; + if ( !eclCase ) return {}; RigCaseCellResultsData* caseCellResultsData = eclCase->results( RiaDefines::PorosityModelType::MATRIX_MODEL ); RigEclipseResultAddress mobPoreVolResAddr( RiaDefines::ResultCatType::STATIC_NATIVE, RiaResultNames::mobilePoreVolumeName() ); @@ -130,5 +130,5 @@ void RigFlowDiagStatCalc::mobileVolumeWeightedMean( size_t timeStepIndex, double const RigActiveCellInfo* actCellInfo = m_resultsData->activeCellInfo( m_resVarAddr ); - RigWeightedMeanCalc::weightedMeanOverCells( &weights, values, nullptr, false, actCellInfo, true, &mean ); + return RigWeightedMeanCalc::weightedMeanOverCells( &weights, values, nullptr, false, actCellInfo, true ); } diff --git a/ApplicationLibCode/ReservoirDataModel/RigFlowDiagStatCalc.h b/ApplicationLibCode/ReservoirDataModel/RigFlowDiagStatCalc.h index c77068a015..426cee6fb6 100644 --- a/ApplicationLibCode/ReservoirDataModel/RigFlowDiagStatCalc.h +++ b/ApplicationLibCode/ReservoirDataModel/RigFlowDiagStatCalc.h @@ -34,13 +34,13 @@ class RigFlowDiagStatCalc : public RigStatisticsCalculator public: RigFlowDiagStatCalc( RigFlowDiagResults* flowDiagResults, const RigFlowDiagResultAddress& resVarAddr ); - void minMaxCellScalarValues( size_t timeStepIndex, double& min, double& max ) override; - void posNegClosestToZero( size_t timeStepIndex, double& pos, double& neg ) override; - void valueSumAndSampleCount( size_t timeStepIndex, double& valueSum, size_t& sampleCount ) override; - void addDataToHistogramCalculator( size_t timeStepIndex, RigHistogramCalculator& histogramCalculator ) override; - void uniqueValues( size_t timeStepIndex, std::set& values ) override; - size_t timeStepCount() override; - void mobileVolumeWeightedMean( size_t timeStepIndex, double& mean ) override; + void minMaxCellScalarValues( size_t timeStepIndex, double& min, double& max ) override; + void posNegClosestToZero( size_t timeStepIndex, double& pos, double& neg ) override; + void valueSumAndSampleCount( size_t timeStepIndex, double& valueSum, size_t& sampleCount ) override; + void addDataToHistogramCalculator( size_t timeStepIndex, RigHistogramCalculator& histogramCalculator ) override; + void uniqueValues( size_t timeStepIndex, std::set& values ) override; + size_t timeStepCount() override; + std::optional mobileVolumeWeightedMean( size_t timeStepIndex ) override; private: RigFlowDiagResults* m_resultsData; diff --git a/ApplicationLibCode/ReservoirDataModel/RigFlowDiagVisibleCellsStatCalc.cpp b/ApplicationLibCode/ReservoirDataModel/RigFlowDiagVisibleCellsStatCalc.cpp index b4b148c6ef..d1249d5025 100644 --- a/ApplicationLibCode/ReservoirDataModel/RigFlowDiagVisibleCellsStatCalc.cpp +++ b/ApplicationLibCode/ReservoirDataModel/RigFlowDiagVisibleCellsStatCalc.cpp @@ -105,10 +105,10 @@ size_t RigFlowDiagVisibleCellsStatCalc::timeStepCount() //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- -void RigFlowDiagVisibleCellsStatCalc::mobileVolumeWeightedMean( size_t timeStepIndex, double& result ) +std::optional RigFlowDiagVisibleCellsStatCalc::mobileVolumeWeightedMean( size_t timeStepIndex ) { auto eclCase = m_resultsData->flowDiagSolution()->firstAncestorOrThisOfType(); - if ( !eclCase ) return; + if ( !eclCase ) return {}; RigCaseCellResultsData* caseCellResultsData = eclCase->results( RiaDefines::PorosityModelType::MATRIX_MODEL ); @@ -120,5 +120,5 @@ void RigFlowDiagVisibleCellsStatCalc::mobileVolumeWeightedMean( size_t timeStepI const RigActiveCellInfo* actCellInfo = m_resultsData->activeCellInfo( m_resVarAddr ); - RigWeightedMeanCalc::weightedMeanOverCells( &weights, values, m_cellVisibilities.p(), true, actCellInfo, true, &result ); + return RigWeightedMeanCalc::weightedMeanOverCells( &weights, values, m_cellVisibilities.p(), true, actCellInfo, true ); } diff --git a/ApplicationLibCode/ReservoirDataModel/RigFlowDiagVisibleCellsStatCalc.h b/ApplicationLibCode/ReservoirDataModel/RigFlowDiagVisibleCellsStatCalc.h index c93e66939e..5cc5dc9986 100644 --- a/ApplicationLibCode/ReservoirDataModel/RigFlowDiagVisibleCellsStatCalc.h +++ b/ApplicationLibCode/ReservoirDataModel/RigFlowDiagVisibleCellsStatCalc.h @@ -41,13 +41,13 @@ public: const RigFlowDiagResultAddress& resVarAddr, const cvf::UByteArray* cellVisibilities ); - void minMaxCellScalarValues( size_t timeStepIndex, double& min, double& max ) override; - void posNegClosestToZero( size_t timeStepIndex, double& pos, double& neg ) override; - void valueSumAndSampleCount( size_t timeStepIndex, double& valueSum, size_t& sampleCount ) override; - void addDataToHistogramCalculator( size_t timeStepIndex, RigHistogramCalculator& histogramCalculator ) override; - void uniqueValues( size_t timeStepIndex, std::set& values ) override; - size_t timeStepCount() override; - void mobileVolumeWeightedMean( size_t timeStepIndex, double& result ) override; + void minMaxCellScalarValues( size_t timeStepIndex, double& min, double& max ) override; + void posNegClosestToZero( size_t timeStepIndex, double& pos, double& neg ) override; + void valueSumAndSampleCount( size_t timeStepIndex, double& valueSum, size_t& sampleCount ) override; + void addDataToHistogramCalculator( size_t timeStepIndex, RigHistogramCalculator& histogramCalculator ) override; + void uniqueValues( size_t timeStepIndex, std::set& values ) override; + size_t timeStepCount() override; + std::optional mobileVolumeWeightedMean( size_t timeStepIndex ) override; private: RigFlowDiagResults* m_resultsData; diff --git a/ApplicationLibCode/ReservoirDataModel/RigWeightedMeanCalc.cpp b/ApplicationLibCode/ReservoirDataModel/RigWeightedMeanCalc.cpp index e1d3c29ef6..14416a2ac7 100644 --- a/ApplicationLibCode/ReservoirDataModel/RigWeightedMeanCalc.cpp +++ b/ApplicationLibCode/ReservoirDataModel/RigWeightedMeanCalc.cpp @@ -28,19 +28,18 @@ //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- -void RigWeightedMeanCalc::weightedMeanOverCells( const std::vector* weights, - const std::vector* values, - const cvf::UByteArray* cellVisibilities, - bool isUsingVisibleCells, - const RigActiveCellInfo* actCellInfo, - bool isUsingActiveIndex, - double* result ) +std::optional RigWeightedMeanCalc::weightedMeanOverCells( const std::vector* weights, + const std::vector* values, + const cvf::UByteArray* cellVisibilities, + bool isUsingVisibleCells, + const RigActiveCellInfo* actCellInfo, + bool isUsingActiveIndex ) { - if ( !( weights && values && result ) ) return; - if ( !cellVisibilities && isUsingVisibleCells ) return; - if ( !actCellInfo && isUsingActiveIndex ) return; + if ( !( weights && values ) ) return {}; + if ( !cellVisibilities && isUsingVisibleCells ) return {}; + if ( !actCellInfo && isUsingActiveIndex ) return {}; - if ( weights->empty() || values->empty() ) return; + if ( weights->empty() || values->empty() ) return {}; double weightedSum = 0.0; double weightSum = 0.0; @@ -80,12 +79,7 @@ void RigWeightedMeanCalc::weightedMeanOverCells( const std::vector* weig weightSum += weight; } - if ( weightSum != 0 ) - { - *result = weightedSum / weightSum; - } - else - { - *result = HUGE_VAL; - } + if ( weightSum == 0.0 ) return {}; + + return weightedSum / weightSum; } diff --git a/ApplicationLibCode/ReservoirDataModel/RigWeightedMeanCalc.h b/ApplicationLibCode/ReservoirDataModel/RigWeightedMeanCalc.h index c76d2565d5..e3c79fb291 100644 --- a/ApplicationLibCode/ReservoirDataModel/RigWeightedMeanCalc.h +++ b/ApplicationLibCode/ReservoirDataModel/RigWeightedMeanCalc.h @@ -20,6 +20,7 @@ #include "cvfArray.h" +#include #include class RigActiveCellInfo; @@ -27,11 +28,12 @@ class RigActiveCellInfo; class RigWeightedMeanCalc { public: - static void weightedMeanOverCells( const std::vector* weights, - const std::vector* values, - const cvf::UByteArray* cellVisibilities, - bool isUsingVisibleCells, - const RigActiveCellInfo* actCellInfo, - bool isUsingActiveIndex, - double* result ); + // Returns the weighted mean over the given cells, or an empty optional if the mean can not be computed, + // ie. when required input data is missing or the sum of weights is zero. + static std::optional weightedMeanOverCells( const std::vector* weights, + const std::vector* values, + const cvf::UByteArray* cellVisibilities, + bool isUsingVisibleCells, + const RigActiveCellInfo* actCellInfo, + bool isUsingActiveIndex ); }; diff --git a/ApplicationLibCode/ResultStatisticsCache/RigStatisticsCalculator.cpp b/ApplicationLibCode/ResultStatisticsCache/RigStatisticsCalculator.cpp index b5c0b2a264..2c42432d30 100644 --- a/ApplicationLibCode/ResultStatisticsCache/RigStatisticsCalculator.cpp +++ b/ApplicationLibCode/ResultStatisticsCache/RigStatisticsCalculator.cpp @@ -89,27 +89,34 @@ void RigStatisticsCalculator::addDataToHistogramCalculator( RigHistogramCalculat //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- -void RigStatisticsCalculator::mobileVolumeWeightedMean( size_t timeStepIndex, double& mean ) +std::optional RigStatisticsCalculator::mobileVolumeWeightedMean( size_t timeStepIndex ) { + return {}; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- -void RigStatisticsCalculator::mobileVolumeWeightedMean( double& mean ) +std::optional RigStatisticsCalculator::mobileVolumeWeightedMean() { - double sum = 0.0; - size_t tsCount = timeStepCount(); + double sum = 0.0; + size_t contributingTsCount = 0; + + const size_t tsCount = timeStepCount(); for ( size_t tIdx = 0; tIdx < tsCount; tIdx++ ) { - double meanForTimeStep; - mobileVolumeWeightedMean( tIdx, meanForTimeStep ); - sum += meanForTimeStep; - } - if ( tsCount != 0 ) - { - mean = sum / tsCount; + // Time steps without a computable mean, ie. when the mobile pore volume is missing, are not + // allowed to contribute to the accumulated sum + if ( const auto meanForTimeStep = mobileVolumeWeightedMean( tIdx ) ) + { + sum += *meanForTimeStep; + contributingTsCount++; + } } + + if ( contributingTsCount == 0 ) return {}; + + return sum / contributingTsCount; } //-------------------------------------------------------------------------------------------------- diff --git a/ApplicationLibCode/ResultStatisticsCache/RigStatisticsCalculator.h b/ApplicationLibCode/ResultStatisticsCache/RigStatisticsCalculator.h index 0c37ed37ea..349a22e570 100644 --- a/ApplicationLibCode/ResultStatisticsCache/RigStatisticsCalculator.h +++ b/ApplicationLibCode/ResultStatisticsCache/RigStatisticsCalculator.h @@ -22,6 +22,7 @@ #include "cvfCollection.h" #include "cvfObject.h" +#include #include class RigHistogramCalculator; @@ -52,8 +53,10 @@ public: virtual size_t timeStepCount() = 0; - void mobileVolumeWeightedMean( double& mean ); - virtual void mobileVolumeWeightedMean( size_t timeStepIndex, double& mean ); + // Returns the mobile volume weighted mean, or an empty optional if it can not be computed, ie. when the + // mobile pore volume result is not available. Implementations must not return a value in that case. + std::optional mobileVolumeWeightedMean(); + virtual std::optional mobileVolumeWeightedMean( size_t timeStepIndex ); static void posNegClosestToZero( const std::vector& values, double& pos, double& neg ); }; diff --git a/ApplicationLibCode/ResultStatisticsCache/RigStatisticsDataCache.cpp b/ApplicationLibCode/ResultStatisticsCache/RigStatisticsDataCache.cpp index 9fe143686e..35ae0d3fbb 100644 --- a/ApplicationLibCode/ResultStatisticsCache/RigStatisticsDataCache.cpp +++ b/ApplicationLibCode/ResultStatisticsCache/RigStatisticsDataCache.cpp @@ -331,7 +331,11 @@ void RigStatisticsDataCache::mobileVolumeWeightedMean( size_t timeStepIndex, dou if ( !m_statsPrTs[timeStepIndex].m_isVolumeWeightedMeanCalculated ) { - m_statisticsCalculator->mobileVolumeWeightedMean( timeStepIndex, m_statsPrTs[timeStepIndex].m_volumeWeightedMean ); + // HUGE_VAL is the "not available" marker used by the public statistics API + m_statsPrTs[timeStepIndex].m_volumeWeightedMean = + m_statisticsCalculator->mobileVolumeWeightedMean( timeStepIndex ).value_or( HUGE_VAL ); + + m_statsPrTs[timeStepIndex].m_isVolumeWeightedMeanCalculated = true; } mean = m_statsPrTs[timeStepIndex].m_volumeWeightedMean; @@ -344,7 +348,7 @@ void RigStatisticsDataCache::mobileVolumeWeightedMean( double& mean ) { if ( !m_statsAllTimesteps.m_isVolumeWeightedMeanCalculated ) { - m_statisticsCalculator->mobileVolumeWeightedMean( m_statsAllTimesteps.m_volumeWeightedMean ); + m_statsAllTimesteps.m_volumeWeightedMean = m_statisticsCalculator->mobileVolumeWeightedMean().value_or( HUGE_VAL ); m_statsAllTimesteps.m_isVolumeWeightedMeanCalculated = true; } diff --git a/ApplicationLibCode/UnitTests/RigStatisticsMath-Test.cpp b/ApplicationLibCode/UnitTests/RigStatisticsMath-Test.cpp index 9c30ed9f70..fc1444b134 100644 --- a/ApplicationLibCode/UnitTests/RigStatisticsMath-Test.cpp +++ b/ApplicationLibCode/UnitTests/RigStatisticsMath-Test.cpp @@ -28,6 +28,7 @@ #include #include #include +#include //-------------------------------------------------------------------------------------------------- /// @@ -586,11 +587,15 @@ namespace class VectorBackedStatisticsCalculator : public RigStatisticsCalculator { public: - explicit VectorBackedStatisticsCalculator( const std::vector>& valuesPerTimeStep ) + explicit VectorBackedStatisticsCalculator( const std::vector>& valuesPerTimeStep, + const std::vector>& mobileVolumeWeightedMeans = {} ) : m_valuesPerTimeStep( valuesPerTimeStep ) + , m_mobileVolumeWeightedMeans( mobileVolumeWeightedMeans ) { } + using RigStatisticsCalculator::mobileVolumeWeightedMean; + void minMaxCellScalarValues( size_t timeStepIndex, double& min, double& max ) override { MinMaxAccumulator accumulator( min, max ); @@ -624,11 +629,34 @@ public: size_t timeStepCount() override { return m_valuesPerTimeStep.size(); } + std::optional mobileVolumeWeightedMean( size_t timeStepIndex ) override + { + if ( timeStepIndex >= m_mobileVolumeWeightedMeans.size() ) return {}; + + return m_mobileVolumeWeightedMeans[timeStepIndex]; + } + private: - std::vector> m_valuesPerTimeStep; + std::vector> m_valuesPerTimeStep; + std::vector> m_mobileVolumeWeightedMeans; }; } // namespace +//-------------------------------------------------------------------------------------------------- +/// Unavailable time steps must not contribute to either the accumulated sum or the divisor. +//-------------------------------------------------------------------------------------------------- +TEST( RigStatisticsCalculator, MobileVolumeWeightedMeanSkipsUnavailableTimeSteps ) +{ + VectorBackedStatisticsCalculator partlyAvailable( { {}, {}, {} }, { 10.0, std::nullopt, 20.0 } ); + + const auto mean = partlyAvailable.mobileVolumeWeightedMean(); + ASSERT_TRUE( mean.has_value() ); + EXPECT_DOUBLE_EQ( 15.0, *mean ); + + VectorBackedStatisticsCalculator unavailable( { {}, {} }, { std::nullopt, std::nullopt } ); + EXPECT_FALSE( unavailable.mobileVolumeWeightedMean().has_value() ); +} + //-------------------------------------------------------------------------------------------------- /// computeHistogram() fills a custom-configured histogram calculator without disturbing the /// statistics cached for other consumers