Files
ResInsight/ApplicationLibCode/ProjectDataModel/RimReservoirGridEnsemble.cpp
T
Magne Sjaastad bf5d6e7465 * #14319 Compute all pending ensemble contour maps in one sweep over realizations
* #14319 Compute all pending ensemble contour maps in one sweep over realizations

* #14319 Initialize result definition and open primary case before building contour map grid

* Contour Map: Compute histogram from map projection values to show correct sample count in overlay info

* #14319 Add comments describing the realization sweep and grid caching

Document why the realization loop is the outer loop, when a realization case is
closed again after processing, and that the contour map grid doubles as the
"statistics computed" flag in ensureResultsComputed().
2026-08-05 14:34:38 +02:00

1006 lines
37 KiB
C++

/////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2026- Equinor 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 "RimReservoirGridEnsemble.h"
#include "RiaLogging.h"
#include "RiaQStringFormatter.h"
#include "RiaResultNames.h"
#include "RifReaderOpmCommon.h"
#include "RigActiveCellInfo.h"
#include "RigEclipseCaseData.h"
#include "RigGridBase.h"
#include "RigGridManager.h"
#include "RigMainGrid.h"
#include "ContourMap/RimStatisticsContourMap.h"
#include "ContourMap/RimStatisticsContourMapView.h"
#include "EnsembleFileSet/RimEnsembleFileSet.h"
#include "EnsembleFileSet/RimEnsembleFileSetTools.h"
#include "Formations/RimFormationNames.h"
#include "Formations/RimFormationNamesCollection.h"
#include "RimCaseCollection.h"
#include "RimEclipseCase.h"
#include "RimEclipseCellColors.h"
#include "RimEclipseResultCase.h"
#include "RimEclipseStatisticsCase.h"
#include "RimEclipseView.h"
#include "RimEclipseViewCollection.h"
#include "RimOilField.h"
#include "RimProject.h"
#include "RimWellTargetMapping.h"
#include "cafCmdFeatureMenuBuilder.h"
#include "cafIconProvider.h"
#include "cafPdmFieldScriptingCapability.h"
#include "cafPdmObjectScriptingCapability.h"
#include "cafPdmUiTreeAttributes.h"
#include "cafProgressInfo.h"
#include "RigCaseCellResultsData.h"
CAF_PDM_SOURCE_INIT( RimReservoirGridEnsemble, "RimReservoirGridEnsemble" );
namespace caf
{
template <>
void caf::AppEnum<RimReservoirGridEnsembleBase::GridModeType>::setUp()
{
addItem( RimReservoirGridEnsembleBase::GridModeType::SHARED_GRID, "SharedGrid", "Shared Grid" );
addItem( RimReservoirGridEnsembleBase::GridModeType::INDIVIDUAL_GRIDS, "IndividualGrids", "Individual Grids" );
setDefault( RimReservoirGridEnsembleBase::GridModeType::SHARED_GRID );
}
} // namespace caf
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RimReservoirGridEnsemble::RimReservoirGridEnsemble()
: m_mainGrid( nullptr )
{
CAF_PDM_InitScriptableObjectWithNameAndComment( "Reservoir Grid Ensemble",
":/GridCaseGroup16x16.png",
"",
"",
"ReservoirGridEnsemble",
"Grid Ensemble from File Set" );
CAF_PDM_InitScriptableField( &m_groupId, "GroupId", -1, "Ensemble ID" );
m_groupId.uiCapability()->setUiReadOnly( true );
m_groupId.capability<caf::PdmAbstractFieldScriptingCapability>()->setIOWriteable( false );
CAF_PDM_InitFieldNoDefault( &m_ensembleFileSet, "EnsembleFileSet", "Ensemble File Set" );
m_ensembleFileSet.uiCapability()->setUiReadOnly( true );
CAF_PDM_InitField( &m_autoDetectGridType, "AutoDetectGridType", true, "Auto Detect Grid Type" );
CAF_PDM_InitField( &m_gridMode, "GridMode", GridModeType::SHARED_GRID, "Grid Mode" );
CAF_PDM_InitField( &m_detectedGridMode, "DetectedGridMode", GridModeType::SHARED_GRID, "Detected Grid Mode" );
m_detectedGridMode.uiCapability()->setUiReadOnly( true );
CAF_PDM_InitFieldNoDefault( &m_activeFormationNames, "DefaultFormationNames", "Formation Names" );
CAF_PDM_InitFieldNoDefault( &m_caseCollection, "CaseCollection", "Source Cases" );
m_caseCollection = new RimCaseCollection;
m_caseCollection->uiCapability()->setUiName( "Realizations" );
m_caseCollection->uiCapability()->setUiIconFromResourceString( ":/Cases16x16.png" );
m_caseCollection.xmlCapability()->disableIO();
CAF_PDM_InitFieldNoDefault( &m_statisticsCaseCollection, "StatisticsCaseCollection", "Statistics Cases" );
m_statisticsCaseCollection = new RimCaseCollection;
m_statisticsCaseCollection->uiCapability()->setUiName( "Derived Statistics" );
m_statisticsCaseCollection->uiCapability()->setUiIconFromResourceString( ":/Histograms16x16.png" );
CAF_PDM_InitFieldNoDefault( &m_viewCollection, "ViewCollection", "Views" );
m_viewCollection = new RimEclipseViewCollection;
m_viewCollection->setEclipseCaseProvider( [this]() { return this->cases(); } );
CAF_PDM_InitFieldNoDefault( &m_wellTargetMappings, "WellTargetMappings", "Well Target Mappings" );
CAF_PDM_InitFieldNoDefault( &m_statisticsContourMaps, "StatisticsContourMaps", "Statistics Contour Maps" );
m_unionOfMatrixActiveCells = new RigActiveCellInfo;
m_unionOfFractureActiveCells = new RigActiveCellInfo;
setDeletable( true );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
int RimReservoirGridEnsemble::groupId() const
{
return m_groupId;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimReservoirGridEnsemble::setGroupId( int id )
{
m_groupId = id;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimReservoirGridEnsemble::setEnsembleFileSet( RimEnsembleFileSet* ensembleFileSet )
{
if ( m_ensembleFileSet )
{
m_ensembleFileSet->fileSetChanged.disconnect( this );
m_ensembleFileSet->nameChanged.disconnect( this );
}
m_ensembleFileSet = ensembleFileSet;
if ( m_ensembleFileSet )
{
m_ensembleFileSet->fileSetChanged.connect( this, &RimReservoirGridEnsemble::onFileSetChanged );
m_ensembleFileSet->nameChanged.connect( this,
[this]( const caf::SignalEmitter* )
{
if ( m_ensembleFileSet ) setName( m_ensembleFileSet->name() );
} );
setName( m_ensembleFileSet->name() );
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RimEnsembleFileSet* RimReservoirGridEnsemble::ensembleFileSet() const
{
return m_ensembleFileSet;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimReservoirGridEnsemble::addCase( RimEclipseCase* reservoir )
{
CVF_ASSERT( reservoir );
if ( !m_mainGrid && reservoir->eclipseCaseData() )
{
m_mainGrid = reservoir->eclipseCaseData()->mainGrid();
}
else if ( hasSharedGrid() && reservoir->eclipseCaseData() )
{
// Share the main grid for identical grids
reservoir->eclipseCaseData()->setMainGrid( m_mainGrid );
}
m_caseCollection()->reservoirs().push_back( reservoir );
clearActiveCellUnions();
clearStatisticsResults();
updateMainGridAndActiveCellsForStatisticsCases();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimReservoirGridEnsemble::removeCase( RimEclipseCase* reservoir )
{
if ( m_caseCollection()->reservoirs().count( reservoir ) == 0 ) return;
m_caseCollection()->reservoirs().removeChild( reservoir );
if ( m_caseCollection()->reservoirs().empty() )
{
m_mainGrid = nullptr;
}
clearActiveCellUnions();
clearStatisticsResults();
updateMainGridAndActiveCellsForStatisticsCases();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
bool RimReservoirGridEnsemble::contains( RimEclipseCase* reservoir ) const
{
CVF_ASSERT( reservoir );
for ( RimEclipseCase* rimReservoir : cases() )
{
if ( reservoir->gridFileName() == rimReservoir->gridFileName() ) return true;
}
return false;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::vector<RimEclipseCase*> RimReservoirGridEnsemble::cases() const
{
if ( !m_caseCollection ) return {};
return m_caseCollection->reservoirs.childrenByType();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RimEclipseCase* RimReservoirGridEnsemble::mainCase()
{
if ( !m_caseCollection()->reservoirs().empty() )
{
return m_caseCollection()->reservoirs()[0];
}
return nullptr;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::vector<RimEclipseCase*> RimReservoirGridEnsemble::sourceCases() const
{
return cases();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RimEclipseCase* RimReservoirGridEnsemble::findByFileName( const QString& gridFileName ) const
{
for ( RimEclipseCase* rimReservoir : cases() )
{
if ( gridFileName == rimReservoir->gridFileName() ) return rimReservoir;
}
return nullptr;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
bool RimReservoirGridEnsemble::hasSharedGrid() const
{
return gridMode() == GridModeType::SHARED_GRID;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
bool RimReservoirGridEnsemble::isGridDataLoaded() const
{
return m_mainGrid != nullptr;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RimReservoirGridEnsembleBase::GridModeType RimReservoirGridEnsemble::gridMode() const
{
if ( m_autoDetectGridType ) return m_detectedGridMode.v();
return m_gridMode.v();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
QString RimReservoirGridEnsemble::ensembleName() const
{
return RimNamedObject::name();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RigMainGrid* RimReservoirGridEnsemble::mainGrid()
{
// Trigger deferred loading if needed
if ( !m_mainGrid && !cases().empty() )
{
const_cast<RimReservoirGridEnsemble*>( this )->loadGridDataFromFiles();
}
return m_mainGrid;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimReservoirGridEnsemble::setupSharedGrid()
{
if ( !hasSharedGrid() ) return;
auto allCases = cases();
if ( allCases.empty() ) return;
// Use the first case's grid as the shared grid
RimEclipseCase* firstCase = allCases[0];
if ( !firstCase || !firstCase->eclipseCaseData() ) return;
m_mainGrid = firstCase->eclipseCaseData()->mainGrid();
// Set the shared grid on all other cases
for ( size_t i = 1; i < allCases.size(); i++ )
{
if ( allCases[i] && allCases[i]->eclipseCaseData() )
{
allCases[i]->eclipseCaseData()->setMainGrid( m_mainGrid );
}
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RigActiveCellInfo* RimReservoirGridEnsemble::unionOfActiveCells( RiaDefines::PorosityModelType porosityType )
{
if ( porosityType == RiaDefines::PorosityModelType::MATRIX_MODEL )
{
return m_unionOfMatrixActiveCells.p();
}
else
{
return m_unionOfFractureActiveCells.p();
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimReservoirGridEnsemble::computeUnionOfActiveCells()
{
if ( !hasSharedGrid() ) return;
if ( m_unionOfMatrixActiveCells->reservoirActiveCellCount() > 0 )
{
return;
}
if ( m_caseCollection->reservoirs.empty() || !m_mainGrid )
{
clearActiveCellUnions();
return;
}
m_unionOfMatrixActiveCells->setReservoirCellCount( m_mainGrid->totalCellCount() );
m_unionOfFractureActiveCells->setReservoirCellCount( m_mainGrid->totalCellCount() );
m_unionOfMatrixActiveCells->setGridCount( m_mainGrid->gridCount() );
m_unionOfFractureActiveCells->setGridCount( m_mainGrid->gridCount() );
size_t globalActiveMatrixIndex = 0;
size_t globalActiveFractureIndex = 0;
for ( size_t gridIdx = 0; gridIdx < m_mainGrid->gridCount(); gridIdx++ )
{
RigGridBase* grid = m_mainGrid->gridByIndex( gridIdx );
std::vector<char> activeM( grid->cellCount(), 0 );
std::vector<char> activeF( grid->cellCount(), 0 );
for ( size_t gridLocalCellIndex = 0; gridLocalCellIndex < grid->cellCount(); gridLocalCellIndex++ )
{
for ( size_t caseIdx = 0; caseIdx < m_caseCollection->reservoirs.size(); caseIdx++ )
{
size_t reservoirCellIndex = grid->reservoirCellIndex( gridLocalCellIndex );
if ( activeM[gridLocalCellIndex] == 0 )
{
if ( m_caseCollection->reservoirs[caseIdx]
->eclipseCaseData()
->activeCellInfo( RiaDefines::PorosityModelType::MATRIX_MODEL )
->isActive( ReservoirCellIndex( reservoirCellIndex ) ) )
{
activeM[gridLocalCellIndex] = 1;
}
}
if ( activeF[gridLocalCellIndex] == 0 )
{
if ( m_caseCollection->reservoirs[caseIdx]
->eclipseCaseData()
->activeCellInfo( RiaDefines::PorosityModelType::FRACTURE_MODEL )
->isActive( ReservoirCellIndex( reservoirCellIndex ) ) )
{
activeF[gridLocalCellIndex] = 1;
}
}
}
}
size_t activeMatrixIndex = 0;
size_t activeFractureIndex = 0;
for ( size_t gridLocalCellIndex = 0; gridLocalCellIndex < grid->cellCount(); gridLocalCellIndex++ )
{
size_t reservoirCellIndex = grid->reservoirCellIndex( gridLocalCellIndex );
if ( activeM[gridLocalCellIndex] != 0 )
{
m_unionOfMatrixActiveCells->setCellResultIndex( ReservoirCellIndex( reservoirCellIndex ),
ActiveCellIndex( globalActiveMatrixIndex++ ) );
activeMatrixIndex++;
}
if ( activeF[gridLocalCellIndex] != 0 )
{
m_unionOfFractureActiveCells->setCellResultIndex( ReservoirCellIndex( reservoirCellIndex ),
ActiveCellIndex( globalActiveFractureIndex++ ) );
activeFractureIndex++;
}
}
m_unionOfMatrixActiveCells->setGridActiveCellCounts( gridIdx, activeMatrixIndex );
m_unionOfFractureActiveCells->setGridActiveCellCounts( gridIdx, activeFractureIndex );
}
m_unionOfMatrixActiveCells->computeDerivedData();
m_unionOfFractureActiveCells->computeDerivedData();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RimCaseCollection* RimReservoirGridEnsemble::statisticsCaseCollection() const
{
return m_statisticsCaseCollection;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RimEclipseStatisticsCase* RimReservoirGridEnsemble::createAndAppendStatisticsCase()
{
if ( !hasSharedGrid() )
{
RiaLogging::warning( std::format( "Cannot create statistics case for ensemble '{}': grids are not identical.", name() ) );
return nullptr;
}
return RimReservoirGridEnsembleBase::createAndAppendStatisticsCase();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimReservoirGridEnsemble::addView( RimEclipseView* view )
{
m_viewCollection->addView( view );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RimEclipseView* RimReservoirGridEnsemble::addViewForCase( RimEclipseCase* eclipseCase )
{
return m_viewCollection->addView( eclipseCase );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RimEclipseViewCollection* RimReservoirGridEnsemble::viewCollection() const
{
return m_viewCollection;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::vector<RimEclipseView*> RimReservoirGridEnsemble::allViews() const
{
std::vector<RimEclipseView*> views;
// Use childrenByType() and null checks here, as this function can be called while child objects
// are being deleted during project close. PdmPointer entries in the child array fields are
// nulled one by one as the objects are destroyed.
if ( m_viewCollection )
{
for ( auto view : m_viewCollection->views() )
{
views.push_back( view );
}
}
if ( m_statisticsCaseCollection )
{
for ( auto statCase : m_statisticsCaseCollection->reservoirs.childrenByType() )
{
for ( auto view : statCase->reservoirViews() )
{
views.push_back( view );
}
}
}
for ( auto cmap : m_statisticsContourMaps.childrenByType() )
{
for ( auto view : cmap->views() )
{
views.push_back( view );
}
}
return views;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::set<RimEclipseCase*> RimReservoirGridEnsemble::casesInViews() const
{
if ( !m_caseCollection ) return {};
if ( !m_viewCollection || m_viewCollection->isEmpty() ) return {};
std::set<RimEclipseCase*> retCases;
for ( auto view : m_viewCollection->views() )
{
if ( view->eclipseCase() != nullptr ) retCases.insert( view->eclipseCase() );
}
return retCases;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimReservoirGridEnsemble::addWellTargetMapping( RimWellTargetMapping* wellTargetMapping )
{
m_wellTargetMappings.push_back( wellTargetMapping );
wellTargetMapping->updateResultDefinition();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::vector<RimWellTargetMapping*> RimReservoirGridEnsemble::wellTargetMappings() const
{
return m_wellTargetMappings.childrenByType();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimReservoirGridEnsemble::addStatisticsContourMap( RimStatisticsContourMap* statisticsContourMap )
{
m_statisticsContourMaps.push_back( statisticsContourMap );
statisticsContourMap->setName( QString( "Ensemble Contour Map #%1" ).arg( m_statisticsContourMaps.size() ) );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::vector<RimStatisticsContourMap*> RimReservoirGridEnsemble::statisticsContourMaps() const
{
return m_statisticsContourMaps.childrenByType();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimReservoirGridEnsemble::loadDataAndUpdate()
{
if ( !isGridDataLoaded() )
{
loadGridDataFromFiles();
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimReservoirGridEnsemble::createGridCasesFromEnsembleFileSet()
{
if ( !m_ensembleFileSet ) return;
// Clear existing cases and statistics
m_caseCollection->reservoirs.deleteChildren();
m_statisticsCaseCollection->reservoirs.deleteChildren();
m_mainGrid = nullptr;
clearActiveCellUnions();
// Create case objects without loading grids
createCaseObjectsFromEnsembleFileSet();
updateConnectedEditors();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimReservoirGridEnsemble::appendMenuItems( caf::CmdFeatureMenuBuilder& menuBuilder ) const
{
menuBuilder << "RicNewViewForGridEnsembleFeature";
menuBuilder << "RicNewStatisticsContourMapFeature";
menuBuilder << "RicNewWellTargetMappingFeature";
if ( hasSharedGrid() )
{
menuBuilder << "RicNewStatisticsCaseFeature";
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RimFormationNames* RimReservoirGridEnsemble::activeFormationNames() const
{
if ( !hasSharedGrid() ) return nullptr;
return m_activeFormationNames();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
QList<caf::PdmOptionItemInfo> RimReservoirGridEnsemble::calculateValueOptions( const caf::PdmFieldHandle* fieldNeedingOptions )
{
QList<caf::PdmOptionItemInfo> options;
if ( fieldNeedingOptions == &m_ensembleFileSet )
{
return RimEnsembleFileSetTools::ensembleFileSetOptions();
}
if ( fieldNeedingOptions == &m_activeFormationNames )
{
RimProject* proj = RimProject::current();
if ( proj && proj->activeOilField() && proj->activeOilField()->formationNamesCollection() )
{
for ( RimFormationNames* fnames : proj->activeOilField()->formationNamesCollection()->formationNamesList() )
{
options.push_back( caf::PdmOptionItemInfo( fnames->shortName(), fnames, false, fnames->uiCapability()->uiIconProvider() ) );
}
}
options.push_front( caf::PdmOptionItemInfo( "None", nullptr ) );
}
return options;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimReservoirGridEnsemble::defineUiOrdering( QString uiConfigName, caf::PdmUiOrdering& uiOrdering )
{
uiOrdering.add( nameField() );
uiOrdering.add( &m_groupId );
uiOrdering.add( &m_ensembleFileSet );
uiOrdering.add( &m_autoDetectGridType );
if ( m_autoDetectGridType )
{
uiOrdering.add( &m_detectedGridMode );
}
else
{
uiOrdering.add( &m_gridMode );
}
if ( hasSharedGrid() )
{
uiOrdering.add( &m_activeFormationNames );
}
uiOrdering.skipRemainingFields();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimReservoirGridEnsemble::fieldChangedByUi( const caf::PdmFieldHandle* changedField, const QVariant& oldValue, const QVariant& newValue )
{
if ( changedField == &m_autoDetectGridType || changedField == &m_gridMode )
{
updateGridModeToolTip();
updateStatisticsVisibility();
updateConnectedEditors();
}
else if ( changedField == &m_activeFormationNames )
{
for ( auto rimCase : cases() )
{
rimCase->updateFormationNamesData();
}
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimReservoirGridEnsemble::createDerivedObjects()
{
createCaseObjectsFromEnsembleFileSet();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimReservoirGridEnsemble::initAfterRead()
{
if ( m_ensembleFileSet )
{
m_ensembleFileSet->fileSetChanged.connect( this, &RimReservoirGridEnsemble::onFileSetChanged );
m_ensembleFileSet->nameChanged.connect( this,
[this]( const caf::SignalEmitter* )
{
if ( m_ensembleFileSet ) setName( m_ensembleFileSet->name() );
} );
}
// Set the case provider for views in the view collection
if ( m_viewCollection )
{
m_viewCollection->setEclipseCaseProvider( [this]() { return this->cases(); } );
}
updateStatisticsVisibility();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimReservoirGridEnsemble::onFileSetChanged( const caf::SignalEmitter* emitter )
{
createGridCasesFromEnsembleFileSet();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimReservoirGridEnsemble::clearActiveCellUnions()
{
m_unionOfMatrixActiveCells->clear();
m_unionOfFractureActiveCells->clear();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimReservoirGridEnsemble::clearStatisticsResults()
{
for ( size_t i = 0; i < m_statisticsCaseCollection->reservoirs().size(); i++ )
{
RimEclipseCase* rimStaticsCase = m_statisticsCaseCollection->reservoirs[i];
if ( !rimStaticsCase ) continue;
if ( rimStaticsCase->results( RiaDefines::PorosityModelType::MATRIX_MODEL ) )
{
rimStaticsCase->results( RiaDefines::PorosityModelType::MATRIX_MODEL )->clearAllResults();
}
if ( rimStaticsCase->results( RiaDefines::PorosityModelType::FRACTURE_MODEL ) )
{
rimStaticsCase->results( RiaDefines::PorosityModelType::FRACTURE_MODEL )->clearAllResults();
}
auto views = rimStaticsCase->reservoirViews();
for ( size_t j = 0; j < views.size(); j++ )
{
RimEclipseView* rimReservoirView = views[j];
rimReservoirView->cellResult()->setResultVariable( RiaResultNames::undefinedResultName() );
rimReservoirView->loadDataAndUpdate();
}
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimReservoirGridEnsemble::updateStatisticsVisibility()
{
bool hidden = !hasSharedGrid();
m_statisticsCaseCollection->uiCapability()->setUiTreeHidden( hidden );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimReservoirGridEnsemble::updateMainGridAndActiveCellsForStatisticsCases()
{
for ( size_t i = 0; i < m_statisticsCaseCollection->reservoirs().size(); i++ )
{
RimEclipseCase* rimStaticsCase = m_statisticsCaseCollection->reservoirs[i];
if ( rimStaticsCase->eclipseCaseData() )
{
rimStaticsCase->eclipseCaseData()->setMainGrid( mainGrid() );
if ( i == 0 )
{
rimStaticsCase->computeActiveCellsBoundingBox();
}
}
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimReservoirGridEnsemble::createCaseObjectsFromEnsembleFileSet()
{
if ( !m_ensembleFileSet ) return;
// Get grid file paths from the file set
QStringList gridFiles = m_ensembleFileSet->createPaths( ".EGRID" );
if ( gridFiles.empty() )
{
// Try .GRID extension
gridFiles = m_ensembleFileSet->createPaths( ".GRID" );
}
if ( gridFiles.empty() )
{
RiaLogging::warning( std::format( "No grid files found for ensemble '{}'", name() ) );
return;
}
// Create case objects without loading grids
for ( const QString& gridFile : gridFiles )
{
RimEclipseResultCase* resultCase = new RimEclipseResultCase();
resultCase->setGridFileName( gridFile );
// DO NOT call openEclipseGridFile() here - deferred loading
RimProject::current()->assignCaseIdToCase( resultCase );
m_caseCollection->reservoirs().push_back( resultCase );
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimReservoirGridEnsemble::loadGridDataFromFiles()
{
// Guard: Only load once
if ( m_mainGrid != nullptr ) return;
auto allCases = cases();
if ( allCases.empty() ) return;
// Determine effective grid mode
if ( m_autoDetectGridType )
{
bool identical = detectGridDimensionEquality();
m_detectedGridMode = identical ? GridModeType::SHARED_GRID : GridModeType::INDIVIDUAL_GRIDS;
}
GridModeType effectiveMode = gridMode();
// Load grids based on effective mode
if ( effectiveMode == GridModeType::SHARED_GRID )
{
loadGridsInSharedMode();
}
else
{
// TODO: Probably not needed load grids here, they are loaded on demand in individual mode
// loadGridsInIndividualMode();
}
updateGridModeToolTip();
updateStatisticsVisibility();
updateConnectedEditors();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimReservoirGridEnsemble::updateGridModeToolTip()
{
const bool individualGrids = ( gridMode() == GridModeType::INDIVIDUAL_GRIDS );
uiCapability()->setUiToolTip( individualGrids ? QString( "This ensemble has grids with varying number of K layers." ) : QString() );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimReservoirGridEnsemble::defineObjectEditorAttribute( QString uiConfigName, caf::PdmUiEditorAttribute* attribute )
{
if ( auto* treeItemAttribute = dynamic_cast<caf::PdmUiTreeViewItemAttribute*>( attribute ) )
{
if ( gridMode() == GridModeType::INDIVIDUAL_GRIDS )
{
auto tag = caf::PdmUiTreeViewItemAttribute::createTag();
tag->icon = caf::IconProvider( ":/info16x16.png" );
treeItemAttribute->tags.push_back( std::move( tag ) );
}
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
bool RimReservoirGridEnsemble::detectGridDimensionEquality()
{
auto allCases = cases();
if ( allCases.size() < 2 ) return true;
RifReaderOpmCommon::GridDimensions firstDim;
for ( int i = 0; i < static_cast<int>( allCases.size() ); i++ )
{
auto dim = RifReaderOpmCommon::readGridDimensions( allCases[i]->gridFileName() );
if ( i == 0 )
{
firstDim = dim;
}
else if ( dim.i != firstDim.i || dim.j != firstDim.j || dim.k != firstDim.k )
{
return false; // Different dimensions
}
}
return true; // All dimensions identical
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimReservoirGridEnsemble::loadGridsInSharedMode()
{
auto allCases = cases();
RiaLogging::info( std::format( "Grid ensemble '{}': Loading grid in shared mode for {} cases.", name(), allCases.size() ) );
// Load first case fully
RimEclipseCase* firstCase = allCases[0];
if ( firstCase->openEclipseGridFile() )
{
m_mainGrid = firstCase->eclipseCaseData()->mainGrid();
// Load remaining cases and share grid
for ( size_t i = 1; i < allCases.size(); i++ )
{
RimEclipseCase* eclipseCase = allCases[i];
if ( auto resultCase = dynamic_cast<RimEclipseResultCase*>( eclipseCase ) )
{
resultCase->openAndReadActiveCellData( firstCase->eclipseCaseData() );
}
eclipseCase->eclipseCaseData()->setMainGrid( m_mainGrid );
}
RigCaseCellResultsData::copyResultsMetaDataFromMainCase( firstCase->eclipseCaseData(),
RiaDefines::PorosityModelType::MATRIX_MODEL,
allCases );
computeUnionOfActiveCells();
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimReservoirGridEnsemble::loadGridsInIndividualMode()
{
auto allCases = cases();
RiaLogging::info( std::format( "Grid ensemble '{}': Loading grids in individual mode for {} cases.", name(), allCases.size() ) );
for ( auto eclipseCase : allCases )
{
eclipseCase->openEclipseGridFile();
}
// Store first grid as reference
if ( !allCases.empty() && allCases[0]->eclipseCaseData() )
{
m_mainGrid = allCases[0]->eclipseCaseData()->mainGrid();
}
}