mirror of
https://github.com/OPM/ResInsight.git
synced 2026-10-24 22:27:55 -05:00
1351 lines
51 KiB
C++
1351 lines
51 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 "RimSummaryEnsemble.h"
|
|
|
|
#include "RiaEnsembleNameTools.h"
|
|
#include "RiaFieldHandleTools.h"
|
|
#include "RiaFilePathTools.h"
|
|
#include "RiaLogging.h"
|
|
#include "RiaQStringFormatter.h"
|
|
#include "RiaStdStringTools.h"
|
|
#include "RiaTextStringTools.h"
|
|
#include "RigStatisticsTools.h"
|
|
#include "RiuMessageDialog.h"
|
|
#include "Summary/Ensemble/RimSummaryEnsembleParameterCollection.h"
|
|
#include "Summary/RiaSummaryAddressAnalyzer.h"
|
|
#include "Summary/RiaSummaryTools.h"
|
|
|
|
#include "RifSummaryReaderInterface.h"
|
|
|
|
#include "RimDeltaSummaryEnsemble.h"
|
|
#include "RimEnsembleCurveSet.h"
|
|
#include "RimProject.h"
|
|
#include "RimSummaryAddressCollection.h"
|
|
#include "RimSummaryCase.h"
|
|
#include "RimSummaryEnsembleTools.h"
|
|
#include "RimSummaryPlot.h"
|
|
|
|
#include "cafCmdFeatureMenuBuilder.h"
|
|
#include "cafPdmFieldScriptingCapability.h"
|
|
#include "cafPdmObjectScriptingCapability.h"
|
|
#include "cafPdmUiCheckBoxEditor.h"
|
|
#include "cafPdmUiLineEditor.h"
|
|
#include "cafPdmUiTextEditor.h"
|
|
#include "cafPdmUiTreeAttributes.h"
|
|
#include "cafPdmUiTreeOrdering.h"
|
|
#include "cafPdmUiTreeViewEditor.h"
|
|
|
|
#include <QFileInfo>
|
|
|
|
#include <cmath>
|
|
#include <set>
|
|
|
|
CAF_PDM_SOURCE_INIT( RimSummaryEnsemble, "SummaryCaseSubCollection" );
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
RimSummaryEnsemble::RimSummaryEnsemble()
|
|
: caseNameChanged( this )
|
|
, caseRemoved( this )
|
|
{
|
|
CAF_PDM_InitScriptableObjectWithNameAndComment( "Summary Case Group",
|
|
":/SummaryGroup16x16.png",
|
|
"",
|
|
"",
|
|
"SummaryEnsemble",
|
|
"A group of summary cases, typically an ensemble" );
|
|
|
|
CAF_PDM_InitScriptableFieldNoDefault( &m_cases, "SummaryCases", "" );
|
|
|
|
CAF_PDM_InitScriptableField( &m_name, "SummaryCollectionName", QString( "Group" ), "Name" );
|
|
CAF_PDM_InitScriptableField( &m_autoName, "CreateAutoName", true, "Auto Name" );
|
|
CAF_PDM_InitScriptableField( &m_useKey1, "UseKey1", false, "Use First Path Part" );
|
|
CAF_PDM_InitScriptableField( &m_useKey2, "UseKey2", false, "Use Second Path Part" );
|
|
|
|
QString defaultText = RiaDefines::key1VariableName() + "-" + RiaDefines::key2VariableName();
|
|
QString tooltipText = QString( "Variables in template is supported, and will be replaced to create name. Example '%1'" ).arg( defaultText );
|
|
CAF_PDM_InitField( &m_nameTemplateString, "NameTemplateString", defaultText, "Name Template", "", tooltipText );
|
|
m_nameTemplateString.uiCapability()->setAttribute( caf::PdmUiLineEditor::Keys::PLACEHOLDER_TEXT,
|
|
RiaDefines::key1VariableName() + "-" + RiaDefines::key2VariableName() );
|
|
m_nameTemplateString.uiCapability()->setAttribute( caf::PdmUiLineEditor::Keys::NOTIFY_WHEN_TEXT_IS_EDITED, true );
|
|
|
|
CAF_PDM_InitFieldNoDefault( &m_groupingMode, "GroupingMode", "Grouping Mode" );
|
|
|
|
CAF_PDM_InitScriptableField( &m_includeInAutoReload, "IncludeInAutoReload", false, "Include in Automatic Case Reloads" );
|
|
caf::PdmUiNativeCheckBoxEditor::configureFieldForEditor( &m_includeInAutoReload );
|
|
|
|
CAF_PDM_InitScriptableFieldNoDefault( &m_nameAndItemCount, "NameCount", "Name" );
|
|
m_nameAndItemCount.registerGetMethod( this, &RimSummaryEnsemble::nameAndItemCount );
|
|
RiaFieldHandleTools::disableWriteAndSetFieldHidden( &m_nameAndItemCount );
|
|
|
|
CAF_PDM_InitScriptableField( &m_isEnsemble, "IsEnsemble", false, "Is Ensemble" );
|
|
m_isEnsemble.uiCapability()->setUiHidden( true );
|
|
|
|
CAF_PDM_InitScriptableField( &m_ensembleId, "Id", -1, "Ensemble ID" );
|
|
m_ensembleId.registerKeywordAlias( "EnsembleId" );
|
|
m_ensembleId.uiCapability()->setUiReadOnly( true );
|
|
m_ensembleId.capability<caf::PdmAbstractFieldScriptingCapability>()->setIOWriteable( false );
|
|
|
|
CAF_PDM_InitFieldNoDefault( &m_dataVectorFolders, "DataVectorFolders", "Data Folders" );
|
|
m_dataVectorFolders = new RimSummaryAddressCollection();
|
|
m_dataVectorFolders.uiCapability()->setUiHidden( true );
|
|
m_dataVectorFolders->uiCapability()->setUiTreeHidden( true );
|
|
m_dataVectorFolders.xmlCapability()->disableIO();
|
|
|
|
CAF_PDM_InitFieldNoDefault( &m_ensembleParameters, "EnsembleParameters", "Ensemble Parameters" );
|
|
m_ensembleParameters = new RimSummaryEnsembleParameterCollection();
|
|
m_ensembleParameters.uiCapability()->setUiHidden( true );
|
|
m_ensembleParameters->uiCapability()->setUiTreeHidden( true );
|
|
m_ensembleParameters.xmlCapability()->disableIO();
|
|
|
|
CAF_PDM_InitFieldNoDefault( &m_ensembleDescription, "Description", "Description" );
|
|
m_ensembleDescription.registerGetMethod( this, &RimSummaryEnsemble::ensembleDescription );
|
|
m_ensembleDescription.uiCapability()->setUiLabelPosition( caf::PdmUiItemInfo::LabelPosition::TOP );
|
|
m_ensembleDescription.uiCapability()->setUiEditorTypeName( caf::PdmUiTextEditor::uiEditorTypeName() );
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
RimSummaryEnsemble::~RimSummaryEnsemble()
|
|
{
|
|
m_cases.deleteChildren();
|
|
|
|
// The cases are gone at this point, so the referring curve sets are cleared and their plots redrawn without data.
|
|
clearReferringCurveSets();
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RimSummaryEnsemble::removeCase( RimSummaryCase* summaryCase, bool notifyChange )
|
|
{
|
|
size_t caseCountBeforeRemove = m_cases.size();
|
|
|
|
m_cases.removeChild( summaryCase );
|
|
|
|
m_cachedSortedEnsembleParameters.clear();
|
|
m_analyzer.reset();
|
|
|
|
caseRemoved.send( summaryCase );
|
|
|
|
if ( notifyChange )
|
|
{
|
|
updateReferringCurveSetsZoomAll();
|
|
}
|
|
|
|
if ( m_isEnsemble && m_cases.size() != caseCountBeforeRemove )
|
|
{
|
|
if ( dynamic_cast<RimDeltaSummaryCase*>( summaryCase ) == nullptr ) calculateEnsembleParametersIntersectionHash();
|
|
}
|
|
|
|
clearChildNodes();
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RimSummaryEnsemble::addCase( RimSummaryCase* summaryCase, bool notifyChange )
|
|
{
|
|
addCaseWithoutDependencyUpdate( summaryCase );
|
|
|
|
// Update derived ensemble cases (if any)
|
|
std::vector<RimDeltaSummaryEnsemble*> referringObjects = objectsWithReferringPtrFieldsOfType<RimDeltaSummaryEnsemble>();
|
|
for ( auto derivedEnsemble : referringObjects )
|
|
{
|
|
if ( !derivedEnsemble ) continue;
|
|
|
|
derivedEnsemble->createDerivedEnsembleCases();
|
|
if ( notifyChange ) derivedEnsemble->updateReferringCurveSetsZoomAll();
|
|
}
|
|
|
|
if ( m_isEnsemble )
|
|
{
|
|
validateEnsembleCases( { summaryCase } );
|
|
calculateEnsembleParametersIntersectionHash();
|
|
}
|
|
|
|
if ( notifyChange ) updateReferringCurveSetsZoomAll();
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RimSummaryEnsemble::addCaseWithoutDependencyUpdate( RimSummaryCase* summaryCase )
|
|
{
|
|
summaryCase->nameChanged.connect( this, &RimSummaryEnsemble::onCaseNameChanged );
|
|
|
|
// Show realization data source for the first case. If we create for all, the performance will be bad
|
|
summaryCase->setShowTreeNodes( m_cases.empty() );
|
|
|
|
m_cases.push_back( summaryCase );
|
|
m_cachedSortedEnsembleParameters.clear();
|
|
m_analyzer.reset();
|
|
|
|
clearChildNodes();
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
std::vector<RimSummaryCase*> RimSummaryEnsemble::allSummaryCases() const
|
|
{
|
|
return m_cases.childrenByType();
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
RimSummaryCase* RimSummaryEnsemble::firstSummaryCase() const
|
|
{
|
|
if ( !m_cases.empty() ) return m_cases[0];
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RimSummaryEnsemble::replaceCases( const std::vector<RimSummaryCase*>& summaryCases, bool notifyChange )
|
|
{
|
|
m_cases.deleteChildren();
|
|
|
|
if ( summaryCases.empty() ) return;
|
|
|
|
auto lastCase = summaryCases.back();
|
|
|
|
for ( auto summaryCase : summaryCases )
|
|
{
|
|
if ( summaryCase == lastCase ) continue;
|
|
|
|
// Do what is required to add the case, avoid updates until all cases are added
|
|
summaryCase->nameChanged.connect( this, &RimSummaryEnsemble::onCaseNameChanged );
|
|
if ( m_cases.empty() )
|
|
{
|
|
summaryCase->setShowTreeNodes( true );
|
|
}
|
|
m_cases.push_back( summaryCase );
|
|
}
|
|
|
|
if ( lastCase )
|
|
{
|
|
// Add the last case, and update connected plots
|
|
addCase( lastCase, notifyChange );
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RimSummaryEnsemble::reloadCases()
|
|
{
|
|
for ( auto summaryCase : allSummaryCases() )
|
|
{
|
|
RiaSummaryTools::reloadSummaryCaseAndUpdateConnectedPlots( summaryCase );
|
|
|
|
RiaLogging::info( std::format( "Reloaded data for {}", summaryCase->summaryHeaderFilename() ) );
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RimSummaryEnsemble::setNameTemplate( const QString& name )
|
|
{
|
|
m_nameTemplateString = name;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RimSummaryEnsemble::setAutoName( bool autoName )
|
|
{
|
|
m_autoName = autoName;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
QString RimSummaryEnsemble::name() const
|
|
{
|
|
return m_name;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RimSummaryEnsemble::updateName( const std::set<QString>& existingEnsembleNames )
|
|
{
|
|
const auto [key1, key2] = nameKeys();
|
|
|
|
QString templateText;
|
|
if ( m_autoName )
|
|
{
|
|
templateText = nameTemplateText();
|
|
}
|
|
else
|
|
{
|
|
templateText = m_nameTemplateString();
|
|
}
|
|
|
|
std::map<QString, QString> keyValues = {
|
|
{ RiaDefines::key1VariableName(), QString::fromStdString( key1 ) },
|
|
{ RiaDefines::key2VariableName(), QString::fromStdString( key2 ) },
|
|
};
|
|
|
|
auto candidateName = RiaTextStringTools::replaceTemplateTextWithValues( templateText, keyValues );
|
|
|
|
if ( m_autoName )
|
|
{
|
|
candidateName = candidateName.trimmed();
|
|
|
|
// When using auto name, remove leading and trailing commas that may occur if key1 or key2 is empty
|
|
if ( candidateName.startsWith( "," ) )
|
|
{
|
|
candidateName = candidateName.mid( 1 );
|
|
}
|
|
if ( candidateName.endsWith( "," ) )
|
|
{
|
|
candidateName = candidateName.left( candidateName.length() - 1 );
|
|
}
|
|
|
|
// Avoid identical ensemble names by appending a number
|
|
if ( existingEnsembleNames.contains( candidateName ) )
|
|
{
|
|
int counter = 1;
|
|
QString uniqueName;
|
|
do
|
|
{
|
|
uniqueName = QString( "%1 (subset-%2)" ).arg( candidateName ).arg( counter++ );
|
|
} while ( existingEnsembleNames.contains( uniqueName ) );
|
|
|
|
candidateName = uniqueName;
|
|
}
|
|
}
|
|
|
|
if ( m_name == candidateName ) return;
|
|
|
|
m_name = candidateName;
|
|
caseNameChanged.send();
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RimSummaryEnsemble::setUsePathKey1( bool useKey1 )
|
|
{
|
|
m_useKey1 = useKey1;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RimSummaryEnsemble::setUsePathKey2( bool useKey2 )
|
|
{
|
|
m_useKey2 = useKey2;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RimSummaryEnsemble::setGroupingMode( RiaDefines::EnsembleGroupingMode groupingMode )
|
|
{
|
|
m_groupingMode = groupingMode;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
RiaDefines::EnsembleGroupingMode RimSummaryEnsemble::groupingMode() const
|
|
{
|
|
return m_groupingMode();
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
bool RimSummaryEnsemble::isEnsemble() const
|
|
{
|
|
return m_isEnsemble();
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RimSummaryEnsemble::setAsEnsemble( bool isEnsemble )
|
|
{
|
|
if ( isEnsemble != m_isEnsemble )
|
|
{
|
|
m_isEnsemble = isEnsemble;
|
|
updateIcon();
|
|
|
|
if ( m_isEnsemble && dynamic_cast<RimDeltaSummaryEnsemble*>( this ) == nullptr )
|
|
{
|
|
validateEnsembleCases( allSummaryCases() );
|
|
calculateEnsembleParametersIntersectionHash();
|
|
}
|
|
|
|
buildMetaData();
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
std::set<RifEclipseSummaryAddress> RimSummaryEnsemble::ensembleSummaryAddresses() const
|
|
{
|
|
// For performance reasons, we only return the addresses from the case with the most keywords.
|
|
// Calling buildMetaData() on all realizations is expensive
|
|
if ( auto caseWithMostKeywords = RimSummaryEnsembleTools::caseWithMostDataObjects( m_cases.childrenByType() ) )
|
|
{
|
|
if ( auto reader = caseWithMostKeywords->summaryReader() )
|
|
{
|
|
// If the reader has no addresses, we need to build the metadata to populate them
|
|
// This is typically the case for newly created cases or cases that have not been read yet
|
|
reader->createAddressesIfRequired();
|
|
|
|
return reader->allResultAddresses();
|
|
}
|
|
}
|
|
|
|
return {};
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
std::set<time_t> RimSummaryEnsemble::ensembleTimeSteps() const
|
|
{
|
|
std::set<time_t> allTimeSteps;
|
|
size_t maxAddrCount = 0;
|
|
int maxAddrIndex = -1;
|
|
|
|
for ( int i = 0; i < (int)m_cases.size(); i++ )
|
|
{
|
|
RimSummaryCase* currCase = m_cases[i];
|
|
if ( !currCase ) continue;
|
|
|
|
RifSummaryReaderInterface* reader = currCase->summaryReader();
|
|
if ( !reader ) continue;
|
|
|
|
size_t addrCount = reader->allResultAddresses().size();
|
|
if ( addrCount > maxAddrCount )
|
|
{
|
|
maxAddrCount = addrCount;
|
|
maxAddrIndex = (int)i;
|
|
}
|
|
}
|
|
|
|
if ( maxAddrIndex >= 0 && m_cases[maxAddrIndex]->summaryReader() )
|
|
{
|
|
RifSummaryReaderInterface* reader = m_cases[maxAddrIndex]->summaryReader();
|
|
|
|
const std::set<RifEclipseSummaryAddress>& addrs = reader->allResultAddresses();
|
|
for ( RifEclipseSummaryAddress addr : addrs )
|
|
{
|
|
std::vector<time_t> timeSteps = reader->timeSteps( addr );
|
|
if ( !timeSteps.empty() )
|
|
{
|
|
allTimeSteps.insert( timeSteps.begin(), timeSteps.end() );
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return allTimeSteps;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
std::vector<RigEnsembleParameter> RimSummaryEnsemble::variationSortedEnsembleParameters( bool excludeNoVariation ) const
|
|
{
|
|
if ( m_cachedSortedEnsembleParameters.empty() )
|
|
{
|
|
m_cachedSortedEnsembleParameters = RimSummaryEnsembleTools::createVariationSortedEnsembleParameters( allSummaryCases() );
|
|
}
|
|
|
|
if ( !excludeNoVariation )
|
|
{
|
|
return m_cachedSortedEnsembleParameters;
|
|
}
|
|
|
|
const double epsilon = 1e-9;
|
|
std::vector<RigEnsembleParameter> parametersWithVariation;
|
|
for ( const auto& p : m_cachedSortedEnsembleParameters )
|
|
{
|
|
if ( std::abs( p.normalizedStdDeviation() ) > epsilon )
|
|
{
|
|
parametersWithVariation.push_back( p );
|
|
}
|
|
}
|
|
return parametersWithVariation;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
std::vector<std::pair<RigEnsembleParameter, double>>
|
|
RimSummaryEnsemble::correlationSortedEnsembleParameters( const RifEclipseSummaryAddress& address ) const
|
|
{
|
|
auto parameters = parameterCorrelationsAllTimeSteps( address );
|
|
std::sort( parameters.begin(),
|
|
parameters.end(),
|
|
[]( const std::pair<RigEnsembleParameter, double>& lhs, const std::pair<RigEnsembleParameter, double>& rhs )
|
|
{ return std::abs( lhs.second ) > std::abs( rhs.second ); } );
|
|
return parameters;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
std::vector<std::pair<RigEnsembleParameter, double>>
|
|
RimSummaryEnsemble::correlationSortedEnsembleParameters( const RifEclipseSummaryAddress& address, time_t selectedTimeStep ) const
|
|
{
|
|
auto parameters = parameterCorrelations( address, selectedTimeStep );
|
|
std::sort( parameters.begin(),
|
|
parameters.end(),
|
|
[]( const std::pair<RigEnsembleParameter, double>& lhs, const std::pair<RigEnsembleParameter, double>& rhs )
|
|
{ return std::abs( lhs.second ) > std::abs( rhs.second ); } );
|
|
return parameters;
|
|
}
|
|
|
|
time_t timeDiff( time_t lhs, time_t rhs )
|
|
{
|
|
if ( lhs >= rhs )
|
|
{
|
|
return lhs - rhs;
|
|
}
|
|
return rhs - lhs;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
std::vector<std::pair<RigEnsembleParameter, double>>
|
|
RimSummaryEnsemble::parameterCorrelations( const RifEclipseSummaryAddress& address,
|
|
time_t timeStep,
|
|
const std::vector<QString>& selectedParameters,
|
|
const std::set<RimSummaryCase*>& selectedCases ) const
|
|
{
|
|
auto parameters = variationSortedEnsembleParameters( true );
|
|
|
|
if ( !selectedParameters.empty() )
|
|
{
|
|
parameters.erase( std::remove_if( parameters.begin(),
|
|
parameters.end(),
|
|
[&selectedParameters]( const RigEnsembleParameter& parameter )
|
|
{
|
|
return std::find( selectedParameters.begin(), selectedParameters.end(), parameter.name ) ==
|
|
selectedParameters.end();
|
|
} ),
|
|
parameters.end() );
|
|
}
|
|
|
|
std::vector<double> caseValuesAtTimestep;
|
|
std::map<RigEnsembleParameter, std::vector<double>> parameterValues;
|
|
|
|
for ( size_t caseIdx = 0u; caseIdx < m_cases.size(); ++caseIdx )
|
|
{
|
|
RimSummaryCase* summaryCase = m_cases[caseIdx];
|
|
RifSummaryReaderInterface* reader = summaryCase->summaryReader();
|
|
if ( !reader ) continue;
|
|
|
|
if ( !selectedCases.empty() && selectedCases.count( summaryCase ) == 0 ) continue;
|
|
|
|
if ( !summaryCase->caseRealizationParameters() ) continue;
|
|
|
|
double closestValue = std::numeric_limits<double>::infinity();
|
|
time_t closestTimeStep = 0;
|
|
auto [isOk, values] = reader->values( address );
|
|
if ( isOk )
|
|
{
|
|
const std::vector<time_t>& timeSteps = reader->timeSteps( address );
|
|
for ( size_t i = 0; i < timeSteps.size(); ++i )
|
|
{
|
|
if ( timeDiff( timeSteps[i], timeStep ) < timeDiff( timeStep, closestTimeStep ) )
|
|
{
|
|
closestValue = values[i];
|
|
closestTimeStep = timeSteps[i];
|
|
}
|
|
}
|
|
}
|
|
if ( std::isfinite( closestValue ) )
|
|
{
|
|
caseValuesAtTimestep.push_back( closestValue );
|
|
|
|
for ( auto parameter : parameters )
|
|
{
|
|
if ( caseIdx >= parameter.values.size() ) continue;
|
|
|
|
if ( parameter.isNumeric() && parameter.isValid() )
|
|
{
|
|
double paramValue = parameter.values[caseIdx].toDouble();
|
|
parameterValues[parameter].push_back( paramValue );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
std::vector<std::pair<RigEnsembleParameter, double>> correlationResults;
|
|
for ( auto parameterValuesPair : parameterValues )
|
|
{
|
|
double correlation = 0.0;
|
|
double pearson = RigStatisticsTools::pearsonCorrelation( parameterValuesPair.second, caseValuesAtTimestep );
|
|
if ( std::isfinite( pearson ) ) correlation = pearson;
|
|
correlationResults.push_back( std::make_pair( parameterValuesPair.first, correlation ) );
|
|
}
|
|
return correlationResults;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
/// Returns a vector of the parameters and the average absolute values of correlations per time step
|
|
//--------------------------------------------------------------------------------------------------
|
|
std::vector<std::pair<RigEnsembleParameter, double>>
|
|
RimSummaryEnsemble::parameterCorrelationsAllTimeSteps( const RifEclipseSummaryAddress& address,
|
|
const std::vector<QString>& selectedParameters ) const
|
|
{
|
|
const size_t maxTimeStepCount = 10;
|
|
std::set<time_t> timeSteps = ensembleTimeSteps();
|
|
if ( timeSteps.empty() ) return {};
|
|
|
|
std::vector<time_t> timeStepsVector( timeSteps.begin(), timeSteps.end() );
|
|
size_t stride = std::max( (size_t)1, timeStepsVector.size() / maxTimeStepCount );
|
|
|
|
std::vector<std::vector<std::pair<RigEnsembleParameter, double>>> correlationsForChosenTimeSteps;
|
|
|
|
for ( size_t i = stride; i < timeStepsVector.size(); i += stride )
|
|
{
|
|
std::vector<std::pair<RigEnsembleParameter, double>> correlationsForTimeStep =
|
|
parameterCorrelations( address, timeStepsVector[i], selectedParameters );
|
|
correlationsForChosenTimeSteps.push_back( correlationsForTimeStep );
|
|
}
|
|
|
|
for ( size_t i = 1; i < correlationsForChosenTimeSteps.size(); ++i )
|
|
{
|
|
for ( size_t j = 0; j < correlationsForChosenTimeSteps[0].size(); ++j )
|
|
{
|
|
correlationsForChosenTimeSteps[0][j].second += correlationsForChosenTimeSteps[i][j].second;
|
|
}
|
|
}
|
|
for ( size_t j = 0; j < correlationsForChosenTimeSteps[0].size(); ++j )
|
|
{
|
|
correlationsForChosenTimeSteps[0][j].second /= correlationsForChosenTimeSteps.size();
|
|
}
|
|
|
|
return correlationsForChosenTimeSteps[0];
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
RigEnsembleParameter RimSummaryEnsemble::ensembleParameter( const QString& paramName ) const
|
|
{
|
|
if ( !isEnsemble() || paramName.isEmpty() ) return {};
|
|
|
|
for ( const RigEnsembleParameter& ensParam : variationSortedEnsembleParameters() )
|
|
{
|
|
if ( ensParam.name == paramName ) return ensParam;
|
|
}
|
|
|
|
return {};
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RimSummaryEnsemble::calculateEnsembleParametersIntersectionHash()
|
|
{
|
|
RimSummaryEnsembleTools::calculateEnsembleParametersIntersectionHash( allSummaryCases() );
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RimSummaryEnsemble::loadDataAndUpdate()
|
|
{
|
|
onLoadDataAndUpdate();
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
bool RimSummaryEnsemble::validateEnsembleCases( const std::vector<RimSummaryCase*> cases )
|
|
{
|
|
// Validate ensemble parameters
|
|
QString errors;
|
|
std::hash<std::string> paramsHasher;
|
|
size_t paramsHash = 0;
|
|
RimSummaryCase* parameterBaseCase = nullptr;
|
|
|
|
for ( RimSummaryCase* rimCase : cases )
|
|
{
|
|
if ( rimCase->caseRealizationParameters() == nullptr || rimCase->caseRealizationParameters()->parameters().empty() )
|
|
{
|
|
errors.append(
|
|
QString( "The case %1 has no ensemble parameters\n" ).arg( QFileInfo( rimCase->summaryHeaderFilename() ).fileName() ) );
|
|
}
|
|
else
|
|
{
|
|
QString paramNames;
|
|
for ( std::pair<QString, RigCaseRealizationParameters::Value> paramPair : rimCase->caseRealizationParameters()->parameters() )
|
|
{
|
|
paramNames.append( paramPair.first );
|
|
}
|
|
|
|
size_t currHash = paramsHasher( paramNames.toStdString() );
|
|
if ( paramsHash == 0 )
|
|
{
|
|
paramsHash = currHash;
|
|
parameterBaseCase = rimCase;
|
|
}
|
|
else if ( paramsHash != currHash )
|
|
{
|
|
errors.append( QString( "The parameters in case %1 is not matching base parameters in %2\n" )
|
|
.arg( QFileInfo( rimCase->summaryHeaderFilename() ).fileName() )
|
|
.arg( QFileInfo( parameterBaseCase->summaryHeaderFilename() ).fileName() ) );
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( !errors.isEmpty() )
|
|
{
|
|
const int maxNumberOfCharactersToDisplaye = 1000;
|
|
QString textToDisplay = errors.left( maxNumberOfCharactersToDisplaye );
|
|
|
|
RiuMessageDialog::showError( nullptr, "", textToDisplay );
|
|
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
/// Sorting operator for sets and maps. Sorts by name.
|
|
//--------------------------------------------------------------------------------------------------
|
|
bool RimSummaryEnsemble::operator<( const RimSummaryEnsemble& rhs ) const
|
|
{
|
|
return name() < rhs.name();
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
RiaDefines::EclipseUnitSystem RimSummaryEnsemble::unitSystem() const
|
|
{
|
|
// The first realization can be missing data, use the first realization with a known unit system
|
|
for ( auto* summaryCase : m_cases.childrenByType() )
|
|
{
|
|
auto unitSystem = summaryCase->unitsSystem();
|
|
if ( unitSystem != RiaDefines::EclipseUnitSystem::UNITS_UNKNOWN ) return unitSystem;
|
|
}
|
|
|
|
return RiaDefines::EclipseUnitSystem::UNITS_UNKNOWN;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
caf::PdmFieldHandle* RimSummaryEnsemble::userDescriptionField()
|
|
{
|
|
return &m_name;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RimSummaryEnsemble::onLoadDataAndUpdate()
|
|
{
|
|
if ( m_isEnsemble )
|
|
{
|
|
calculateEnsembleParametersIntersectionHash();
|
|
clearChildNodes();
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RimSummaryEnsemble::updateReferringCurveSets( bool doZoomAll )
|
|
{
|
|
// Update curve set referring to this group
|
|
std::vector<caf::PdmObject*> referringObjects = objectsWithReferringPtrFieldsOfType<PdmObject>();
|
|
|
|
for ( auto object : referringObjects )
|
|
{
|
|
RimEnsembleCurveSet* curveSet = dynamic_cast<RimEnsembleCurveSet*>( object );
|
|
|
|
bool updateParentPlot = true;
|
|
if ( curveSet )
|
|
{
|
|
curveSet->loadDataAndUpdate( updateParentPlot );
|
|
|
|
if ( doZoomAll )
|
|
{
|
|
if ( auto parentPlot = curveSet->firstAncestorOrThisOfType<RimSummaryPlot>() )
|
|
{
|
|
// If the ensemble name has changed, make sure the name in the project tree is updated
|
|
parentPlot->updateConnectedEditors();
|
|
parentPlot->zoomAll();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RimSummaryEnsemble::updateReferringCurveSets()
|
|
{
|
|
updateReferringCurveSets( false );
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
/// Clear the curves of the curve sets referring to this ensemble, and redraw the affected plots.
|
|
///
|
|
/// Used when the summary cases have been deleted. Reloading the curve data has nothing to read at that point, so this
|
|
/// deletes the curves directly instead of going through loadDataAndUpdate(). Each plot is updated once, even when it
|
|
/// holds several curve sets referring to this ensemble.
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RimSummaryEnsemble::clearReferringCurveSets()
|
|
{
|
|
std::set<RimSummaryPlot*> plotsToUpdate;
|
|
|
|
for ( auto curveSet : objectsWithReferringPtrFieldsOfType<RimEnsembleCurveSet>() )
|
|
{
|
|
if ( !curveSet ) continue;
|
|
|
|
curveSet->deleteEnsembleCurves();
|
|
curveSet->deleteStatisticsCurves();
|
|
curveSet->filterChanged.send();
|
|
|
|
if ( auto parentPlot = curveSet->firstAncestorOrThisOfType<RimSummaryPlot>() )
|
|
{
|
|
plotsToUpdate.insert( parentPlot );
|
|
}
|
|
}
|
|
|
|
for ( auto plot : plotsToUpdate )
|
|
{
|
|
plot->updateAll();
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RimSummaryEnsemble::updateReferringCurveSetsZoomAll()
|
|
{
|
|
updateReferringCurveSets( true );
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
RiaSummaryAddressAnalyzer* RimSummaryEnsemble::addressAnalyzer()
|
|
{
|
|
if ( !m_analyzer )
|
|
{
|
|
m_analyzer = std::make_unique<RiaSummaryAddressAnalyzer>();
|
|
|
|
m_analyzer->appendAddresses( ensembleSummaryAddresses() );
|
|
}
|
|
|
|
return m_analyzer.get();
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RimSummaryEnsemble::computeMinMax( const RifEclipseSummaryAddress& address )
|
|
{
|
|
if ( m_minMaxValues.count( address ) > 0 ) return;
|
|
|
|
double minimumValue( std::numeric_limits<double>::infinity() );
|
|
double maximumValue( -std::numeric_limits<double>::infinity() );
|
|
|
|
for ( const auto& s : m_cases() )
|
|
{
|
|
if ( auto reader = s->summaryReader() )
|
|
{
|
|
auto [isOk, values] = reader->values( address );
|
|
if ( values.empty() ) continue;
|
|
|
|
const auto [min, max] = std::minmax_element( values.begin(), values.end() );
|
|
|
|
minimumValue = std::min( *min, minimumValue );
|
|
maximumValue = std::max( *max, maximumValue );
|
|
}
|
|
}
|
|
|
|
if ( minimumValue != std::numeric_limits<double>::infinity() && maximumValue != -std::numeric_limits<double>::infinity() )
|
|
{
|
|
setMinMax( address, minimumValue, maximumValue );
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RimSummaryEnsemble::setMinMax( const RifEclipseSummaryAddress& address, double min, double max )
|
|
{
|
|
m_minMaxValues[address] = std::pair( min, max );
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
std::pair<double, double> RimSummaryEnsemble::minMax( const RifEclipseSummaryAddress& address )
|
|
{
|
|
computeMinMax( address );
|
|
|
|
return m_minMaxValues[address];
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RimSummaryEnsemble::cleanupBeforeDelete()
|
|
{
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
QString RimSummaryEnsemble::nameAndItemCount() const
|
|
{
|
|
size_t itemCount = m_cases.size();
|
|
const size_t itemCountThreshold = 20;
|
|
if ( itemCount > itemCountThreshold )
|
|
{
|
|
return QString( "%1 (%2)" ).arg( m_name() ).arg( itemCount );
|
|
}
|
|
|
|
return m_name();
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RimSummaryEnsemble::updateIcon()
|
|
{
|
|
if ( m_isEnsemble )
|
|
setUiIconFromResourceString( ":/SummaryEnsemble.svg" );
|
|
else
|
|
setUiIconFromResourceString( ":/SummaryGroup16x16.png" );
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RimSummaryEnsemble::initAfterRead()
|
|
{
|
|
if ( m_ensembleId() == -1 )
|
|
{
|
|
RimProject* project = RimProject::current();
|
|
project->assignIdToEnsemble( this );
|
|
}
|
|
|
|
updateIcon();
|
|
|
|
for ( const auto& summaryCase : m_cases )
|
|
{
|
|
summaryCase->nameChanged.connect( this, &RimSummaryEnsemble::onCaseNameChanged );
|
|
}
|
|
|
|
if ( RimProject::current()->isProjectFileVersionEqualOrOlderThan( "2022.06.2" ) ) m_autoName = false;
|
|
|
|
if ( RimProject::current()->isProjectFileVersionEqualOrOlderThan( "2024.12.2" ) )
|
|
{
|
|
m_nameTemplateString = m_name;
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RimSummaryEnsemble::fieldChangedByUi( const caf::PdmFieldHandle* changedField, const QVariant& oldValue, const QVariant& newValue )
|
|
{
|
|
if ( changedField == &m_isEnsemble )
|
|
{
|
|
updateIcon();
|
|
}
|
|
if ( changedField == &m_autoName || changedField == &m_nameTemplateString )
|
|
{
|
|
RiaSummaryTools::updateSummaryEnsembleNames();
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RimSummaryEnsemble::onCaseNameChanged( const SignalEmitter* emitter )
|
|
{
|
|
caseNameChanged.send();
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RimSummaryEnsemble::appendMenuItems( caf::CmdFeatureMenuBuilder& menuBuilder ) const
|
|
{
|
|
menuBuilder.subMenuStart( "Import" );
|
|
menuBuilder << "RicImportSummaryCaseFeature";
|
|
menuBuilder << "RicImportSummaryCasesFeature";
|
|
menuBuilder << "RicImportSummaryGroupFeature";
|
|
menuBuilder << "RicImportEnsembleFeature";
|
|
menuBuilder.subMenuEnd();
|
|
menuBuilder << "RicNewSummaryMultiPlotFeature";
|
|
menuBuilder << "RicNewDerivedEnsembleFeature";
|
|
menuBuilder << "RicOpenSummaryPlotEditorFeature";
|
|
menuBuilder << "RicAppendSummaryCurvesForSummaryCasesFeature";
|
|
menuBuilder << "RicAppendSummaryPlotsForSummaryCasesFeature";
|
|
menuBuilder.addSeparator();
|
|
menuBuilder << "RicAppendSummaryCurvesForSummaryCasesFeature";
|
|
menuBuilder << "RicAppendSummaryPlotsForSummaryCasesFeature";
|
|
menuBuilder << "RicCreateMultiPlotFromSelectionFeature";
|
|
menuBuilder << "RicCreatePlotFromTemplateByShortcutFeature";
|
|
menuBuilder.addSeparator();
|
|
menuBuilder << "RicExportEnsembleParametersFeature";
|
|
menuBuilder.addSeparator();
|
|
menuBuilder << "RicReloadSummaryCaseFeature";
|
|
menuBuilder << "RicReplaceSummaryEnsembleFeature";
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
bool RimSummaryEnsemble::isAutoNameChecked() const
|
|
{
|
|
return m_autoName();
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RimSummaryEnsemble::defineUiOrdering( QString uiConfigName, caf::PdmUiOrdering& uiOrdering )
|
|
{
|
|
uiOrdering.add( &m_autoName );
|
|
|
|
if ( !m_autoName() )
|
|
{
|
|
uiOrdering.add( &m_nameTemplateString );
|
|
}
|
|
|
|
uiOrdering.add( &m_name );
|
|
m_name.uiCapability()->setUiReadOnly( true );
|
|
|
|
if ( m_isEnsemble() ) uiOrdering.add( &m_ensembleId );
|
|
|
|
uiOrdering.add( &m_ensembleDescription );
|
|
|
|
uiOrdering.add( &m_includeInAutoReload );
|
|
|
|
uiOrdering.skipRemainingFields( true );
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RimSummaryEnsemble::defineUiTreeOrdering( caf::PdmUiTreeOrdering& uiTreeOrdering, QString uiConfigName /*= ""*/ )
|
|
{
|
|
if ( m_isEnsemble() )
|
|
{
|
|
buildChildNodes();
|
|
|
|
m_dataVectorFolders->updateUiTreeOrdering( uiTreeOrdering );
|
|
|
|
if ( !m_cases.empty() )
|
|
{
|
|
auto subnode = uiTreeOrdering.add( "Realizations", ":/Folder.png" );
|
|
for ( auto& smcase : m_cases )
|
|
{
|
|
subnode->add( smcase );
|
|
}
|
|
}
|
|
|
|
m_ensembleParameters->updateUiTreeOrdering( uiTreeOrdering );
|
|
|
|
uiTreeOrdering.skipRemainingChildren( true );
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
std::vector<caf::PdmUiTreeViewItemTag> RimSummaryEnsemble::treeItemTags()
|
|
{
|
|
std::vector<caf::PdmUiTreeViewItemTag> tags;
|
|
|
|
if ( m_includeInAutoReload() )
|
|
{
|
|
auto tag = caf::PdmUiTreeViewItemTag::create();
|
|
tag.icon = caf::IconProvider( ":/TimedRefresh.png" );
|
|
tags.push_back( tag );
|
|
}
|
|
|
|
return tags;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
QString RimSummaryEnsemble::ensembleDescription() const
|
|
{
|
|
QString txt;
|
|
|
|
if ( !m_cases.empty() )
|
|
{
|
|
txt += QString( "First summary file of %1 files :\n " ).arg( m_cases.size() );
|
|
auto firstCase = m_cases[0]->summaryHeaderFilename();
|
|
txt += firstCase;
|
|
txt += "\n";
|
|
}
|
|
|
|
const auto [key1, key2] = nameKeys();
|
|
txt += "\nDetected variables that can be used when defining the text in the 'Name Template' field:\n";
|
|
txt += QString( " %1: %2\n" ).arg( RiaDefines::key1VariableName() ).arg( QString::fromStdString( key1 ) );
|
|
txt += QString( " %1: %2\n" ).arg( RiaDefines::key2VariableName() ).arg( QString::fromStdString( key2 ) );
|
|
|
|
txt += "\n";
|
|
if ( m_groupingMode() == RiaDefines::EnsembleGroupingMode::EVEREST_FOLDER_STRUCTURE )
|
|
{
|
|
txt += "The ensemble is grouped by the file folder structure.\n";
|
|
}
|
|
else if ( m_groupingMode() == RiaDefines::EnsembleGroupingMode::FMU_FOLDER_STRUCTURE )
|
|
{
|
|
txt += "The ensemble is grouped by the FMU file folder structure.\n";
|
|
}
|
|
else if ( m_groupingMode() == RiaDefines::EnsembleGroupingMode::RESINSIGHT_OPMFLOW_STRUCTURE )
|
|
{
|
|
txt += "The ensemble is grouped by the ResInsight Opm Flow file folder structure.\n";
|
|
}
|
|
|
|
return txt;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
std::pair<std::string, std::string> RimSummaryEnsemble::nameKeys() const
|
|
{
|
|
std::string key1 = "Undefined KEY1";
|
|
std::string key2 = "Undefined KEY2";
|
|
|
|
if ( m_groupingMode() == RiaDefines::EnsembleGroupingMode::FMU_FOLDER_STRUCTURE )
|
|
{
|
|
if ( !m_cases.empty() )
|
|
{
|
|
auto fileNames = RiaEnsembleNameTools::groupFilePathsFmu( { m_cases[0]->summaryHeaderFilename().toStdString() } );
|
|
if ( !fileNames.empty() )
|
|
{
|
|
key1 = fileNames.begin()->first.first;
|
|
key2 = fileNames.begin()->first.second;
|
|
}
|
|
}
|
|
}
|
|
else if ( m_groupingMode() == RiaDefines::EnsembleGroupingMode::EVEREST_FOLDER_STRUCTURE )
|
|
{
|
|
if ( m_cases.size() > 1 )
|
|
{
|
|
auto name1 = RiaFilePathTools::toInternalSeparator( m_cases[0]->summaryHeaderFilename() ).toStdString();
|
|
auto name2 = RiaFilePathTools::toInternalSeparator( m_cases[1]->summaryHeaderFilename() ).toStdString();
|
|
|
|
auto parts1 = RiaStdStringTools::splitString( name1, '/' );
|
|
auto parts2 = RiaStdStringTools::splitString( name2, '/' );
|
|
|
|
size_t commonParts = 0;
|
|
for ( size_t i = 0; i < std::min( parts1.size(), parts2.size() ); i++ )
|
|
{
|
|
if ( parts1[i] == parts2[i] )
|
|
{
|
|
commonParts++;
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( commonParts == 1 )
|
|
{
|
|
key2 = parts1[commonParts - 1];
|
|
}
|
|
else if ( commonParts > 1 )
|
|
{
|
|
key1 = parts1[commonParts - 2];
|
|
key2 = parts1[commonParts - 1];
|
|
}
|
|
}
|
|
}
|
|
else if ( m_groupingMode() == RiaDefines::EnsembleGroupingMode::RESINSIGHT_OPMFLOW_STRUCTURE )
|
|
{
|
|
if ( !m_cases.empty() )
|
|
{
|
|
auto fileNames = RiaEnsembleNameTools::groupFilePathsOpm( { m_cases[0]->summaryHeaderFilename().toStdString() } );
|
|
if ( !fileNames.empty() )
|
|
{
|
|
key1 = fileNames.begin()->first.first;
|
|
key2 = fileNames.begin()->first.second;
|
|
}
|
|
}
|
|
}
|
|
|
|
return { key1, key2 };
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
QString RimSummaryEnsemble::nameTemplateText() const
|
|
{
|
|
QString text;
|
|
if ( m_useKey1() ) text += RiaDefines::key1VariableName();
|
|
if ( m_useKey2() )
|
|
{
|
|
if ( !text.isEmpty() ) text += ", ";
|
|
text += RiaDefines::key2VariableName();
|
|
}
|
|
|
|
return text;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RimSummaryEnsemble::setEnsembleId( int ensembleId )
|
|
{
|
|
m_ensembleId = ensembleId;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
int RimSummaryEnsemble::ensembleId() const
|
|
{
|
|
return m_ensembleId();
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
bool RimSummaryEnsemble::hasEnsembleParameters() const
|
|
{
|
|
for ( RimSummaryCase* rimCase : allSummaryCases() )
|
|
{
|
|
if ( rimCase->caseRealizationParameters() != nullptr )
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RimSummaryEnsemble::buildChildNodes()
|
|
{
|
|
if ( m_dataVectorFolders->isEmpty() )
|
|
{
|
|
m_dataVectorFolders->updateFolderStructure( ensembleSummaryAddresses(), -1, m_ensembleId );
|
|
}
|
|
if ( m_ensembleParameters->isEmpty() )
|
|
{
|
|
m_ensembleParameters->setEnsembleId( ensembleId() );
|
|
for ( auto& ensPar : variationSortedEnsembleParameters() )
|
|
{
|
|
m_ensembleParameters->addParameter( ensPar.name, false /*check duplicate*/ );
|
|
}
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RimSummaryEnsemble::buildMetaData()
|
|
{
|
|
clearChildNodes();
|
|
buildChildNodes();
|
|
updateConnectedEditors();
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RimSummaryEnsemble::onSummaryVectorsChanged()
|
|
{
|
|
m_analyzer.reset();
|
|
buildMetaData();
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RimSummaryEnsemble::onCalculationUpdated()
|
|
{
|
|
if ( auto caseWithMostKeywords = RimSummaryEnsembleTools::caseWithMostDataObjects( m_cases.childrenByType() ) )
|
|
{
|
|
if ( auto reader = caseWithMostKeywords->summaryReader() )
|
|
{
|
|
// https://github.com/OPM/ResInsight/issues/13101
|
|
// Call createAndSetAddresses to ensure that addresses are recreated based on an update to the calculation objects.
|
|
|
|
reader->createAndSetAddresses();
|
|
}
|
|
}
|
|
|
|
// The addresses of the other cases in the ensemble are not created up front for performance reasons. If addresses have been created,
|
|
// the calculated addresses must be refreshed to reflect the current set of calculation objects.
|
|
// https://github.com/OPM/ResInsight/issues/14559
|
|
for ( auto summaryCase : allSummaryCases() )
|
|
{
|
|
if ( auto reader = summaryCase->summaryReader() )
|
|
{
|
|
reader->refreshCalculatedAddresses();
|
|
}
|
|
}
|
|
|
|
m_dataVectorFolders->deleteCalculatedAddresses();
|
|
m_dataVectorFolders->updateFolderStructure( ensembleSummaryAddresses(), -1, m_ensembleId );
|
|
|
|
m_analyzer.reset();
|
|
|
|
updateConnectedEditors();
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RimSummaryEnsemble::clearChildNodes()
|
|
{
|
|
m_dataVectorFolders->deleteChildren();
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
bool RimSummaryEnsemble::includeInAutoReload() const
|
|
{
|
|
return m_includeInAutoReload();
|
|
}
|