mirror of
https://github.com/OPM/ResInsight.git
synced 2025-02-25 18:55:39 -06:00
* Remove use of RifEnsembleStatisticsReader * Add RimSummaryAddressSelector and RimEnsembleCrossPlotStatisticsCase * Remove RifEnsembleStatisticsReader * Add support for cross plot x-axis selection * Support source stepping on ensemble cross plot curves * Add isXAxisSummaryVector
332 lines
13 KiB
C++
332 lines
13 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 "RiaSummaryTools.h"
|
|
#include "RiaTimeHistoryCurveResampler.h"
|
|
|
|
#include "RigStatisticsMath.h"
|
|
|
|
#include "RimEnsembleCurveSet.h"
|
|
#include "RimSummaryCaseCollection.h"
|
|
|
|
#include <limits>
|
|
#include <set>
|
|
#include <vector>
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
/// Internal constants
|
|
//--------------------------------------------------------------------------------------------------
|
|
#define DOUBLE_INF std::numeric_limits<double>::infinity()
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
RimEnsembleStatisticsCase::RimEnsembleStatisticsCase()
|
|
{
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
const std::vector<time_t>& RimEnsembleStatisticsCase::timeSteps() const
|
|
{
|
|
return m_timeSteps;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
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();
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
bool RimEnsembleStatisticsCase::values( const RifEclipseSummaryAddress& resultAddress, std::vector<double>* values ) const
|
|
{
|
|
auto quantityName = resultAddress.ensembleStatisticsVectorName();
|
|
|
|
if ( quantityName == ENSEMBLE_STAT_P10_QUANTITY_NAME )
|
|
*values = m_p10Data;
|
|
else if ( quantityName == ENSEMBLE_STAT_P50_QUANTITY_NAME )
|
|
*values = m_p50Data;
|
|
else if ( quantityName == ENSEMBLE_STAT_P90_QUANTITY_NAME )
|
|
*values = m_p90Data;
|
|
else if ( quantityName == ENSEMBLE_STAT_MEAN_QUANTITY_NAME )
|
|
*values = 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*>& sumCases,
|
|
const RifEclipseSummaryAddress& inputAddress,
|
|
bool includeIncompleteCurves )
|
|
{
|
|
clearData();
|
|
if ( !inputAddress.isValid() ) return;
|
|
if ( sumCases.empty() ) return;
|
|
|
|
// Use first summary case to get unit system and other meta data
|
|
m_firstSummaryCase = sumCases.front();
|
|
|
|
auto [minTimeStep, maxTimeStep] = findMinMaxTimeStep( sumCases, inputAddress );
|
|
RiaDefines::DateTimePeriod period = findBestResamplingPeriod( minTimeStep, maxTimeStep );
|
|
|
|
std::vector<time_t> allTimeSteps;
|
|
std::vector<std::vector<double>> caseAndTimeStepValues;
|
|
caseAndTimeStepValues.reserve( sumCases.size() );
|
|
for ( const auto& sumCase : sumCases )
|
|
{
|
|
const auto& reader = sumCase->summaryReader();
|
|
if ( reader )
|
|
{
|
|
const std::vector<time_t>& timeSteps = reader->timeSteps( inputAddress );
|
|
std::vector<double> values;
|
|
reader->values( inputAddress, &values );
|
|
|
|
if ( values.empty() ) continue;
|
|
|
|
if ( !includeIncompleteCurves && timeSteps.size() != values.size() ) continue;
|
|
|
|
auto [resampledTimeSteps, resampledValues] = RiaSummaryTools::resampledValuesForPeriod( inputAddress, timeSteps, values, period );
|
|
|
|
if ( allTimeSteps.empty() ) allTimeSteps = resampledTimeSteps;
|
|
caseAndTimeStepValues.push_back( resampledValues );
|
|
}
|
|
}
|
|
|
|
m_timeSteps = allTimeSteps;
|
|
|
|
for ( size_t timeStepIndex = 0; timeStepIndex < allTimeSteps.size(); timeStepIndex++ )
|
|
{
|
|
std::vector<double> valuesAtTimeStep;
|
|
valuesAtTimeStep.reserve( sumCases.size() );
|
|
|
|
for ( const std::vector<double>& caseValues : caseAndTimeStepValues )
|
|
{
|
|
if ( timeStepIndex < caseValues.size() )
|
|
{
|
|
valuesAtTimeStep.push_back( caseValues[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::vector<RimSummaryCase*> RimEnsembleStatisticsCase::validSummaryCases( const std::vector<RimSummaryCase*>& allSumCases,
|
|
const RifEclipseSummaryAddress& inputAddress,
|
|
bool includeIncompleteCurves )
|
|
{
|
|
std::vector<RimSummaryCase*> validCases;
|
|
std::vector<std::pair<RimSummaryCase*, time_t>> times;
|
|
|
|
time_t maxTimeStep = 0;
|
|
|
|
for ( auto& sumCase : allSumCases )
|
|
{
|
|
const auto& reader = sumCase->summaryReader();
|
|
if ( reader )
|
|
{
|
|
const std::vector<time_t>& timeSteps = reader->timeSteps( inputAddress );
|
|
if ( !timeSteps.empty() )
|
|
{
|
|
time_t lastTimeStep = timeSteps.back();
|
|
|
|
maxTimeStep = std::max( lastTimeStep, maxTimeStep );
|
|
times.push_back( std::make_pair( sumCase, lastTimeStep ) );
|
|
}
|
|
}
|
|
}
|
|
|
|
for ( const auto& [sumCase, lastTimeStep] : times )
|
|
{
|
|
// Previous versions tested on identical first time step, this test is now removed. For large simulations with
|
|
// numerical issues the first time step can be slightly different
|
|
//
|
|
// https://github.com/OPM/ResInsight/issues/7774
|
|
//
|
|
if ( !includeIncompleteCurves && lastTimeStep != maxTimeStep )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
validCases.push_back( sumCase );
|
|
}
|
|
|
|
return validCases;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
std::pair<time_t, time_t> RimEnsembleStatisticsCase::findMinMaxTimeStep( const std::vector<RimSummaryCase*>& sumCases,
|
|
const RifEclipseSummaryAddress& inputAddress )
|
|
{
|
|
time_t minTimeStep = std::numeric_limits<time_t>::max();
|
|
time_t maxTimeStep = 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() )
|
|
{
|
|
minTimeStep = std::min( timeSteps.front(), minTimeStep );
|
|
maxTimeStep = std::max( timeSteps.back(), maxTimeStep );
|
|
}
|
|
}
|
|
}
|
|
|
|
return std::make_pair( minTimeStep, maxTimeStep );
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
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;
|
|
}
|