Files
ResInsight/ApplicationLibCode/ProjectDataModel/Summary/RimEnsembleStatisticsCase.cpp
Magne Sjaastad 9b6e441386 Make it possible to exclude partial delta curves
Make sure statistics curves are recomputed on flag change

When toggling "Discard Missing or Incomplete Realizations", make sure the statistics is recomputed and the ensemble plots updated.
2024-11-18 07:45:20 +01:00

274 lines
11 KiB
C++

/////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2017- Statoil ASA
//
// ResInsight is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// ResInsight is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE.
//
// See the GNU General Public License at <http://www.gnu.org/licenses/gpl.html>
// for more details.
//
/////////////////////////////////////////////////////////////////////////////////
#include "RimEnsembleStatisticsCase.h"
#include "RiaCurveMerger.h"
#include "RiaHashTools.h"
#include "RiaTimeHistoryCurveResampler.h"
#include "Summary/RiaSummaryTools.h"
#include "RigStatisticsMath.h"
#include <limits>
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::vector<time_t> RimEnsembleStatisticsCase::timeSteps( const RifEclipseSummaryAddress& resultAddress ) const
{
return m_timeSteps;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
bool RimEnsembleStatisticsCase::hasP10Data() const
{
return !m_p10Data.empty();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
bool RimEnsembleStatisticsCase::hasP50Data() const
{
return !m_p50Data.empty();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
bool RimEnsembleStatisticsCase::hasP90Data() const
{
return !m_p90Data.empty();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
bool RimEnsembleStatisticsCase::hasMeanData() const
{
return !m_meanData.empty();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::pair<bool, std::vector<double>> RimEnsembleStatisticsCase::values( const RifEclipseSummaryAddress& resultAddress ) const
{
auto quantityName = resultAddress.ensembleStatisticsVectorName();
if ( quantityName == RifEclipseSummaryAddressDefines::statisticsNameP10() )
return { true, m_p10Data };
else if ( quantityName == RifEclipseSummaryAddressDefines::statisticsNameP50() )
return { true, m_p50Data };
else if ( quantityName == RifEclipseSummaryAddressDefines::statisticsNameP90() )
return { true, m_p90Data };
else if ( quantityName == RifEclipseSummaryAddressDefines::statisticsNameMean() )
return { true, m_meanData };
return { true, {} };
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::string RimEnsembleStatisticsCase::unitName( const RifEclipseSummaryAddress& resultAddress ) const
{
if ( m_firstSummaryCase && m_firstSummaryCase->summaryReader() )
{
return m_firstSummaryCase->summaryReader()->unitName( resultAddress );
}
return "Ensemble Statistics Case - Undefined Unit";
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
QString RimEnsembleStatisticsCase::caseName() const
{
return "Ensemble Statistics";
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimEnsembleStatisticsCase::createSummaryReaderInterface()
{
// Nothing to do here as RimEnsembleStatisticsCase inherits from RifSummaryReaderInterface
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RifSummaryReaderInterface* RimEnsembleStatisticsCase::summaryReader()
{
return this;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimEnsembleStatisticsCase::calculate( const std::vector<RimSummaryCase*>& summaryCases,
const RifEclipseSummaryAddress& inputAddress,
bool includeIncompleteCurves )
{
auto hash = RiaHashTools::hash( summaryCases, inputAddress.toEclipseTextAddress(), includeIncompleteCurves );
if ( hash == m_hash ) return;
m_hash = hash;
clearData();
if ( !inputAddress.isValid() ) return;
if ( summaryCases.empty() ) return;
// Use first summary case to get unit system and other meta data
m_firstSummaryCase = summaryCases.front();
const auto [minTime, maxTime] = findMinMaxTime( summaryCases, inputAddress );
// The last time step for the individual realizations in an ensemble is usually identical. Add a small threshold to improve robustness.
const auto timeThreshold = RiaSummaryTools::calculateTimeThreshold( minTime, maxTime );
RiaDefines::DateTimePeriod period = findBestResamplingPeriod( minTime, maxTime );
RiaTimeHistoryCurveMerger curveMerger;
for ( const auto& sumCase : summaryCases )
{
const auto& reader = sumCase->summaryReader();
if ( reader )
{
const std::vector<time_t>& timeSteps = reader->timeSteps( inputAddress );
const auto [isOk, values] = reader->values( inputAddress );
if ( values.empty() || timeSteps.empty() ) continue;
if ( !includeIncompleteCurves && ( timeSteps.back() < timeThreshold ) ) continue;
const auto [resampledTimeSteps, resampledValues] =
RiaSummaryTools::resampledValuesForPeriod( inputAddress, timeSteps, values, period );
curveMerger.addCurveData( resampledTimeSteps, resampledValues );
}
}
curveMerger.computeInterpolatedValues( includeIncompleteCurves );
std::vector<std::vector<double>> curveValues;
for ( size_t i = 0; i < curveMerger.curveCount(); i++ )
{
curveValues.push_back( curveMerger.interpolatedYValuesForAllXValues( i ) );
}
m_timeSteps = curveMerger.allXValues();
for ( size_t timeStepIndex = 0; timeStepIndex < m_timeSteps.size(); timeStepIndex++ )
{
std::vector<double> valuesAtTimeStep;
valuesAtTimeStep.reserve( curveMerger.curveCount() );
for ( size_t curveIdx = 0; curveIdx < curveMerger.curveCount(); curveIdx++ )
{
valuesAtTimeStep.push_back( curveValues[curveIdx][timeStepIndex] );
}
double p10, p50, p90, mean;
RigStatisticsMath::calculateStatisticsCurves( valuesAtTimeStep, &p10, &p50, &p90, &mean, RigStatisticsMath::PercentileStyle::SWITCHED );
m_p10Data.push_back( p10 );
m_p50Data.push_back( p50 );
m_p90Data.push_back( p90 );
m_meanData.push_back( mean );
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RiaDefines::EclipseUnitSystem RimEnsembleStatisticsCase::unitSystem() const
{
if ( m_firstSummaryCase && m_firstSummaryCase->summaryReader() )
{
return m_firstSummaryCase->summaryReader()->unitSystem();
}
return RiaDefines::EclipseUnitSystem::UNITS_UNKNOWN;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimEnsembleStatisticsCase::clearData()
{
m_timeSteps.clear();
m_p10Data.clear();
m_p50Data.clear();
m_p90Data.clear();
m_meanData.clear();
m_firstSummaryCase = nullptr;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::pair<time_t, time_t> RimEnsembleStatisticsCase::findMinMaxTime( const std::vector<RimSummaryCase*>& sumCases,
const RifEclipseSummaryAddress& inputAddress )
{
time_t minTime = std::numeric_limits<time_t>::max();
time_t maxTime = 0;
for ( const auto& sumCase : sumCases )
{
const auto& reader = sumCase->summaryReader();
if ( reader )
{
const std::vector<time_t>& timeSteps = reader->timeSteps( inputAddress );
if ( !timeSteps.empty() )
{
minTime = std::min( timeSteps.front(), minTime );
maxTime = std::max( timeSteps.back(), maxTime );
}
}
}
return std::make_pair( minTime, maxTime );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RiaDefines::DateTimePeriod RimEnsembleStatisticsCase::findBestResamplingPeriod( time_t minTimeStep, time_t maxTimeStep )
{
std::vector<RiaDefines::DateTimePeriod> periods = { RiaDefines::DateTimePeriod::DAY,
RiaDefines::DateTimePeriod::HOUR,
RiaDefines::DateTimePeriod::MINUTE };
for ( auto p : periods )
{
size_t numSamples = RiaTimeHistoryCurveResampler::timeStepsFromTimeRange( p, minTimeStep, maxTimeStep ).size();
// Resampled data should ideally have at least 100 samples to look good.
if ( numSamples > 100 )
{
return p;
}
}
return RiaDefines::DateTimePeriod::DAY;
}