From b24481017e15cd0eb9dace856168d82153123f26 Mon Sep 17 00:00:00 2001 From: Kristian Bendiksen Date: Mon, 16 Dec 2024 12:44:29 +0100 Subject: [PATCH 1/2] Grid calculator: add support for grid ensembles. --- .../ProjectDataModel/RimGridCalculation.cpp | 46 ++++++++++++++++--- .../ProjectDataModel/RimGridCalculation.h | 3 ++ 2 files changed, 43 insertions(+), 6 deletions(-) diff --git a/ApplicationLibCode/ProjectDataModel/RimGridCalculation.cpp b/ApplicationLibCode/ProjectDataModel/RimGridCalculation.cpp index 6211ad6b9f..ea91d24e0c 100644 --- a/ApplicationLibCode/ProjectDataModel/RimGridCalculation.cpp +++ b/ApplicationLibCode/ProjectDataModel/RimGridCalculation.cpp @@ -37,6 +37,7 @@ #include "RimCaseCollection.h" #include "RimEclipseCase.h" #include "RimEclipseCaseCollection.h" +#include "RimEclipseCaseEnsemble.h" #include "RimEclipseCaseTools.h" #include "RimEclipseCellColors.h" #include "RimEclipseResultAddress.h" @@ -77,6 +78,7 @@ void caf::AppEnum::setUp() { addItem( RimGridCalculation::AdditionalCasesType::NONE, "NONE", "None" ); addItem( RimGridCalculation::AdditionalCasesType::GRID_CASE_GROUP, "GRID_CASE_GROUP", "Case Group" ); + addItem( RimGridCalculation::AdditionalCasesType::ENSEMBLE, "ENSEMBLE", "Ensemble" ); addItem( RimGridCalculation::AdditionalCasesType::ALL_CASES, "NONE", "All Cases" ); setDefault( RimGridCalculation::AdditionalCasesType::NONE ); } @@ -98,6 +100,7 @@ RimGridCalculation::RimGridCalculation() CAF_PDM_InitFieldNoDefault( &m_additionalCasesType, "AdditionalCasesType", "Apply To Additional Cases" ); CAF_PDM_InitFieldNoDefault( &m_additionalCaseGroup, "AdditionalCaseGroup", "Case Group" ); + CAF_PDM_InitFieldNoDefault( &m_additionalEnsemble, "AdditionalEnsemble", "Ensemble" ); CAF_PDM_InitFieldNoDefault( &m_nonVisibleResultAddress, "NonVisibleResultAddress", "" ); m_nonVisibleResultAddress = new RimEclipseResultAddress; @@ -175,7 +178,8 @@ bool RimGridCalculation::calculate() { // Equal grid size is required if there is more than one grid case in the expression. If a cell filter view is active, the visibility is // based on one view and reused for all other grid models, and requires equal grid size. - bool checkIfGridSizeIsEqual = !allSourceCasesAreEqualToDestinationCase() || m_cellFilterView != nullptr; + bool checkIfGridSizeIsEqual = ( !allSourceCasesAreEqualToDestinationCase() || m_cellFilterView != nullptr ) && + m_additionalCasesType != AdditionalCasesType::ENSEMBLE; for ( auto calculationCase : outputEclipseCases() ) { @@ -183,9 +187,16 @@ bool RimGridCalculation::calculate() if ( !calculationCase->eclipseCaseData() ) { - QString msg = QString( "No data available for case %1, aborting calculation" ).arg( calculationCase->caseUserDescription() ); - RiaLogging::errorInMessageBox( nullptr, "Grid Property Calculator", msg ); - return false; + if ( m_additionalCasesType == AdditionalCasesType::ENSEMBLE ) + { + calculationCase->ensureReservoirCaseIsOpen(); + } + else + { + QString msg = QString( "No data available for case %1, aborting calculation" ).arg( calculationCase->caseUserDescription() ); + RiaLogging::errorInMessageBox( nullptr, "Grid Property Calculator", msg ); + return false; + } } if ( !calculationCase->eclipseCaseData()->activeCellInfo( RiaDefines::PorosityModelType::MATRIX_MODEL ) ) @@ -275,6 +286,11 @@ std::vector RimGridCalculation::outputEclipseCases() const return m_additionalCaseGroup()->caseCollection()->reservoirs.childrenByType(); } + if ( m_additionalCasesType() == RimGridCalculation::AdditionalCasesType::ENSEMBLE ) + { + if ( m_additionalEnsemble() ) return m_additionalEnsemble()->cases(); + } + return { m_destinationCase }; } @@ -333,6 +349,9 @@ void RimGridCalculation::defineUiOrdering( QString uiConfigName, caf::PdmUiOrder uiOrdering.add( &m_additionalCaseGroup ); m_additionalCaseGroup.uiCapability()->setUiHidden( m_additionalCasesType() != RimGridCalculation::AdditionalCasesType::GRID_CASE_GROUP ); + uiOrdering.add( &m_additionalEnsemble ); + m_additionalEnsemble.uiCapability()->setUiHidden( m_additionalCasesType() != RimGridCalculation::AdditionalCasesType::ENSEMBLE ); + caf::PdmUiGroup* filterGroup = uiOrdering.addNewGroup( "Cell Filter" ); filterGroup->setCollapsedByDefault(); filterGroup->add( &m_cellFilterView ); @@ -442,6 +461,20 @@ QList RimGridCalculation::calculateValueOptions( const c } } } + else if ( &m_additionalEnsemble == fieldNeedingOptions ) + { + options.push_back( caf::PdmOptionItemInfo( "None", nullptr ) ); + + RimProject* proj = RimProject::current(); + if ( proj->activeOilField() && proj->activeOilField()->analysisModels() ) + { + auto analysisModels = proj->activeOilField()->analysisModels(); + for ( RimEclipseCaseEnsemble* e : analysisModels->caseEnsembles() ) + { + options.push_back( caf::PdmOptionItemInfo( e->name(), e, false, e->uiIconProvider() ) ); + } + } + } return options; } @@ -903,8 +936,9 @@ bool RimGridCalculation::calculateForCases( const std::vector& RimGridCalculationVariable* v = dynamic_cast( m_variables[i] ); CAF_ASSERT( v != nullptr ); - bool useDataFromSourceCase = ( v->eclipseCase() == m_destinationCase ); - auto sourceCase = useDataFromSourceCase ? calculationCase : v->eclipseCase(); + bool useDataFromSourceCase = ( v->eclipseCase() == m_destinationCase ) || + m_additionalCasesType == AdditionalCasesType::ENSEMBLE; + auto sourceCase = useDataFromSourceCase ? calculationCase : v->eclipseCase(); auto dataForVariable = getActiveCellValuesForVariable( v, tsId, porosityModel, sourceCase, calculationCase ); if ( dataForVariable.empty() ) diff --git a/ApplicationLibCode/ProjectDataModel/RimGridCalculation.h b/ApplicationLibCode/ProjectDataModel/RimGridCalculation.h index a4454f5a5b..eb50781b28 100644 --- a/ApplicationLibCode/ProjectDataModel/RimGridCalculation.h +++ b/ApplicationLibCode/ProjectDataModel/RimGridCalculation.h @@ -19,6 +19,7 @@ #pragma once #include "RiaPorosityModel.h" +#include "RimEclipseCaseEnsemble.h" #include "RimGridCalculationVariable.h" #include "RimUserDefinedCalculation.h" @@ -56,6 +57,7 @@ class RimGridCalculation : public RimUserDefinedCalculation { NONE, GRID_CASE_GROUP, + ENSEMBLE, ALL_CASES }; @@ -146,6 +148,7 @@ class RimGridCalculation : public RimUserDefinedCalculation caf::PdmField> m_additionalCasesType; caf::PdmPtrField m_additionalCaseGroup; + caf::PdmPtrField m_additionalEnsemble; caf::PdmField> m_selectedTimeSteps; From 0e1c62afca0055633f61612dad6bde5d9c521207 Mon Sep 17 00:00:00 2001 From: Kristian Bendiksen Date: Mon, 16 Dec 2024 15:36:53 +0100 Subject: [PATCH 2/2] Well Target Candidates: misc improvements. Improvements: * Well Target Candidates: Add filtering by binary result * Well Target Candidates: make result grid cell count configurable. * Well Target Candidates: Improve ui ordering. * Well Target Candidates: compute probability, and simplify vector names. * Optimization: Avoid expensive unintended duplication of data --- .../RimEclipseCaseEnsemble.cpp | 1 + .../ProjectDataModel/RimRegularGridCase.cpp | 22 +++++- .../ProjectDataModel/RimRegularGridCase.h | 6 ++ .../RimStatisticsContourMap.cpp | 1 + .../RimWellTargetCandidatesGenerator.cpp | 73 ++++++++++++++++++- .../RimWellTargetCandidatesGenerator.h | 22 ++++-- .../Well/RigWellTargetCandidatesGenerator.cpp | 66 +++++++++++++---- .../Well/RigWellTargetCandidatesGenerator.h | 19 +++-- 8 files changed, 179 insertions(+), 31 deletions(-) diff --git a/ApplicationLibCode/ProjectDataModel/RimEclipseCaseEnsemble.cpp b/ApplicationLibCode/ProjectDataModel/RimEclipseCaseEnsemble.cpp index ea9499f7e7..bf9f87f908 100644 --- a/ApplicationLibCode/ProjectDataModel/RimEclipseCaseEnsemble.cpp +++ b/ApplicationLibCode/ProjectDataModel/RimEclipseCaseEnsemble.cpp @@ -193,6 +193,7 @@ RimEclipseViewCollection* RimEclipseCaseEnsemble::viewCollection() const void RimEclipseCaseEnsemble::addWellTargetsGenerator( RimWellTargetCandidatesGenerator* generator ) { m_wellTargetGenerators.push_back( generator ); + generator->updateResultDefinition(); } //-------------------------------------------------------------------------------------------------- diff --git a/ApplicationLibCode/ProjectDataModel/RimRegularGridCase.cpp b/ApplicationLibCode/ProjectDataModel/RimRegularGridCase.cpp index 9804784b4e..18508dbba8 100644 --- a/ApplicationLibCode/ProjectDataModel/RimRegularGridCase.cpp +++ b/ApplicationLibCode/ProjectDataModel/RimRegularGridCase.cpp @@ -36,18 +36,36 @@ RimRegularGridCase::RimRegularGridCase() CAF_PDM_InitFieldNoDefault( &m_maximum, "Maximum", "Maximum" ); m_maximum.uiCapability()->setUiReadOnly( true ); + + CAF_PDM_InitField( &m_cellCountI, "CellCountI", 100, "Cell Count I" ); + m_cellCountI.uiCapability()->setUiReadOnly( true ); + + CAF_PDM_InitField( &m_cellCountJ, "CellCountJ", 100, "Cell Count J" ); + m_cellCountJ.uiCapability()->setUiReadOnly( true ); + + CAF_PDM_InitField( &m_cellCountK, "CellCountK", 10, "Cell Count K" ); + m_cellCountK.uiCapability()->setUiReadOnly( true ); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- - void RimRegularGridCase::setBoundingBox( const cvf::BoundingBox& boundingBox ) { m_minimum = boundingBox.min(); m_maximum = boundingBox.max(); } +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RimRegularGridCase::setCellCount( const cvf::Vec3st& cellCount ) +{ + m_cellCountI = static_cast( cellCount.x() ); + m_cellCountJ = static_cast( cellCount.y() ); + m_cellCountK = static_cast( cellCount.z() ); +} + //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- @@ -58,7 +76,7 @@ cvf::ref RimRegularGridCase::createModel( QString modelName reader->setWorldCoordinates( m_minimum, m_maximum ); - cvf::Vec3st gridPointDimensions( 50, 50, 10 ); + cvf::Vec3st gridPointDimensions( m_cellCountI, m_cellCountJ, m_cellCountK ); reader->setGridPointDimensions( gridPointDimensions ); reader->open( "", reservoir.p() ); diff --git a/ApplicationLibCode/ProjectDataModel/RimRegularGridCase.h b/ApplicationLibCode/ProjectDataModel/RimRegularGridCase.h index 898c388bf0..136491e4a6 100644 --- a/ApplicationLibCode/ProjectDataModel/RimRegularGridCase.h +++ b/ApplicationLibCode/ProjectDataModel/RimRegularGridCase.h @@ -39,6 +39,8 @@ class RimRegularGridCase : public RimEclipseResultCase void setBoundingBox( const cvf::BoundingBox& boundingBox ); + void setCellCount( const cvf::Vec3st& cellCount ); + bool openEclipseGridFile() override; cvf::ref createModel( QString modelName ); @@ -46,4 +48,8 @@ class RimRegularGridCase : public RimEclipseResultCase private: caf::PdmField m_minimum; caf::PdmField m_maximum; + + caf::PdmField m_cellCountI; + caf::PdmField m_cellCountJ; + caf::PdmField m_cellCountK; }; diff --git a/ApplicationLibCode/ProjectDataModel/RimStatisticsContourMap.cpp b/ApplicationLibCode/ProjectDataModel/RimStatisticsContourMap.cpp index 493bc83629..c0ad3026fa 100644 --- a/ApplicationLibCode/ProjectDataModel/RimStatisticsContourMap.cpp +++ b/ApplicationLibCode/ProjectDataModel/RimStatisticsContourMap.cpp @@ -188,6 +188,7 @@ void RimStatisticsContourMap::initAfterRead() RimEclipseCase* eclipseCase = ensemble->cases().front(); setEclipseCase( eclipseCase ); } + //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- diff --git a/ApplicationLibCode/ProjectDataModel/RimWellTargetCandidatesGenerator.cpp b/ApplicationLibCode/ProjectDataModel/RimWellTargetCandidatesGenerator.cpp index 651cdd51ae..8c59280f27 100644 --- a/ApplicationLibCode/ProjectDataModel/RimWellTargetCandidatesGenerator.cpp +++ b/ApplicationLibCode/ProjectDataModel/RimWellTargetCandidatesGenerator.cpp @@ -33,6 +33,7 @@ #include "RimEclipseCaseCollection.h" #include "RimEclipseCaseEnsemble.h" #include "RimEclipseCellColors.h" +#include "RimEclipseResultDefinition.h" #include "RimEclipseView.h" #include "RimOilField.h" #include "RimProject.h" @@ -108,6 +109,17 @@ RimWellTargetCandidatesGenerator::RimWellTargetCandidatesGenerator() CAF_PDM_InitField( &m_maxIterations, "Iterations", 10000, "Max Iterations" ); CAF_PDM_InitField( &m_maxClusters, "MaxClusters", 5, "Max Clusters" ); + CAF_PDM_InitFieldNoDefault( &m_resultDefinition, "ResultDefinition", "" ); + m_resultDefinition.uiCapability()->setUiTreeChildrenHidden( true ); + m_resultDefinition = new RimEclipseResultDefinition; + m_resultDefinition->findField( "MResultType" )->uiCapability()->setUiName( "Result" ); + m_resultDefinition->setResultType( RiaDefines::ResultCatType::DYNAMIC_NATIVE ); + m_resultDefinition->setResultVariable( "SOIL" ); + + CAF_PDM_InitField( &m_cellCountI, "CellCountI", 100, "Cell Count I" ); + CAF_PDM_InitField( &m_cellCountJ, "CellCountJ", 100, "Cell Count J" ); + CAF_PDM_InitField( &m_cellCountK, "CellCountK", 10, "Cell Count K" ); + CAF_PDM_InitField( &m_generateEnsembleStatistics, "GenerateEnsembleStatistics", true, "Generate Ensemble Statistics" ); caf::PdmUiPushButtonEditor::configureEditorLabelHidden( &m_generateEnsembleStatistics ); @@ -279,6 +291,14 @@ void RimWellTargetCandidatesGenerator::defineEditorAttribute( const caf::PdmFiel } } +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +cvf::Vec3st RimWellTargetCandidatesGenerator::getResultGridCellCount() const +{ + return cvf::Vec3st( m_cellCountI, m_cellCountJ, m_cellCountK ); +} + //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- @@ -296,9 +316,11 @@ void RimWellTargetCandidatesGenerator::generateEnsembleStatistics() auto ensemble = firstAncestorOrThisOfType(); if ( !ensemble ) return; - RigWellTargetCandidatesGenerator::ClusteringLimits limits = getClusteringLimits(); - RimRegularGridCase* regularGridCase = RigWellTargetCandidatesGenerator::generateEnsembleCandidates( *ensemble, + const cvf::Vec3st& resultGridCellCount = getResultGridCellCount(); + RigWellTargetCandidatesGenerator::ClusteringLimits limits = getClusteringLimits(); + RimRegularGridCase* regularGridCase = RigWellTargetCandidatesGenerator::generateEnsembleCandidates( *ensemble, m_timeStep(), + resultGridCellCount, m_volumeType(), m_volumesType(), m_volumeResultType(), @@ -331,7 +353,31 @@ void RimWellTargetCandidatesGenerator::generateEnsembleStatistics() //-------------------------------------------------------------------------------------------------- void RimWellTargetCandidatesGenerator::defineUiOrdering( QString uiConfigName, caf::PdmUiOrdering& uiOrdering ) { - PdmObject::defineUiOrdering( uiConfigName, uiOrdering ); + caf::PdmUiGroup* resultGroup = uiOrdering.addNewGroup( "Result" ); + resultGroup->add( &m_timeStep ); + resultGroup->add( &m_volumeType ); + resultGroup->add( &m_volumeResultType ); + resultGroup->add( &m_volumesType ); + + caf::PdmUiGroup* clusterLimitsGroup = uiOrdering.addNewGroup( "Cluster Growth Limits" ); + clusterLimitsGroup->add( &m_volume ); + clusterLimitsGroup->add( &m_pressure ); + clusterLimitsGroup->add( &m_permeability ); + clusterLimitsGroup->add( &m_transmissibility ); + + caf::PdmUiGroup* resultDefinitionGroup = uiOrdering.addNewGroup( "Cluster Filter" ); + m_resultDefinition->uiOrdering( uiConfigName, *resultDefinitionGroup ); + + caf::PdmUiGroup* ensembleGridGroup = uiOrdering.addNewGroup( "Ensemble Statistics Grid" ); + ensembleGridGroup->add( &m_cellCountI ); + ensembleGridGroup->add( &m_cellCountJ ); + ensembleGridGroup->add( &m_cellCountK ); + + caf::PdmUiGroup* advancedGroup = uiOrdering.addNewGroup( "Advanced" ); + advancedGroup->add( &m_maxIterations ); + advancedGroup->add( &m_maxClusters ); + + uiOrdering.add( &m_generateEnsembleStatistics ); if ( m_minimumVolume == cvf::UNDEFINED_DOUBLE || m_maximumVolume == cvf::UNDEFINED_DOUBLE ) { @@ -349,7 +395,8 @@ RigWellTargetCandidatesGenerator::ClusteringLimits RimWellTargetCandidatesGenera .pressure = m_pressure, .transmissibility = m_transmissibility, .maxClusters = m_maxClusters, - .maxIterations = m_maxIterations }; + .maxIterations = m_maxIterations, + .filterAddress = m_resultDefinition->eclipseResultAddress() }; } //-------------------------------------------------------------------------------------------------- @@ -361,3 +408,21 @@ RimEclipseCase* RimWellTargetCandidatesGenerator::firstCase() const if ( !ensemble || ensemble->cases().empty() ) return nullptr; return ensemble->cases()[0]; } + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RimWellTargetCandidatesGenerator::initAfterRead() +{ + RimEclipseCase* eclipseCase = firstCase(); + if ( eclipseCase ) m_resultDefinition->setEclipseCase( eclipseCase ); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RimWellTargetCandidatesGenerator::updateResultDefinition() +{ + RimEclipseCase* eclipseCase = firstCase(); + if ( eclipseCase ) m_resultDefinition->setEclipseCase( eclipseCase ); +} diff --git a/ApplicationLibCode/ProjectDataModel/RimWellTargetCandidatesGenerator.h b/ApplicationLibCode/ProjectDataModel/RimWellTargetCandidatesGenerator.h index 8ecd8d3ba9..1d012956da 100644 --- a/ApplicationLibCode/ProjectDataModel/RimWellTargetCandidatesGenerator.h +++ b/ApplicationLibCode/ProjectDataModel/RimWellTargetCandidatesGenerator.h @@ -25,6 +25,7 @@ #include "Well/RigWellTargetCandidatesGenerator.h" +class RimEclipseResultDefinition; class RimEclipseCase; //================================================================================================== @@ -39,16 +40,20 @@ class RimWellTargetCandidatesGenerator : public caf::PdmObject RimWellTargetCandidatesGenerator(); ~RimWellTargetCandidatesGenerator() override; + void updateResultDefinition(); + protected: void fieldChangedByUi( const caf::PdmFieldHandle* changedField, const QVariant& oldValue, const QVariant& newValue ) override; void defineEditorAttribute( const caf::PdmFieldHandle* field, QString uiConfigName, caf::PdmUiEditorAttribute* attribute ) override; QList calculateValueOptions( const caf::PdmFieldHandle* fieldNeedingOptions ) override; void defineUiOrdering( QString uiConfigName, caf::PdmUiOrdering& uiOrdering ) override; + void initAfterRead() override; private: - void generateCandidates( RimEclipseCase* eclipseCase ); - void updateAllBoundaries(); - void generateEnsembleStatistics(); + void generateCandidates( RimEclipseCase* eclipseCase ); + void updateAllBoundaries(); + void generateEnsembleStatistics(); + cvf::Vec3st getResultGridCellCount() const; RimEclipseCase* firstCase() const; @@ -65,8 +70,15 @@ class RimWellTargetCandidatesGenerator : public caf::PdmObject caf::PdmField m_permeability; caf::PdmField m_transmissibility; - caf::PdmField m_maxIterations; - caf::PdmField m_maxClusters; + caf::PdmField m_maxIterations; + caf::PdmField m_maxClusters; + + caf::PdmChildField m_resultDefinition; + + caf::PdmField m_cellCountI; + caf::PdmField m_cellCountJ; + caf::PdmField m_cellCountK; + caf::PdmField m_generateEnsembleStatistics; double m_minimumVolume; diff --git a/ApplicationLibCode/ReservoirDataModel/Well/RigWellTargetCandidatesGenerator.cpp b/ApplicationLibCode/ReservoirDataModel/Well/RigWellTargetCandidatesGenerator.cpp index 28fab47f5d..23a822d413 100644 --- a/ApplicationLibCode/ReservoirDataModel/Well/RigWellTargetCandidatesGenerator.cpp +++ b/ApplicationLibCode/ReservoirDataModel/Well/RigWellTargetCandidatesGenerator.cpp @@ -104,6 +104,17 @@ void RigWellTargetCandidatesGenerator::generateCandidates( RimEclipseCase* resultsData->ensureKnownResultLoaded( transmissibilityZAddress ); const std::vector& transmissibilityZ = resultsData->cellScalarResults( transmissibilityZAddress, 0 ); + std::vector filterVector; + if ( limits.filterAddress.isValid() ) + { + resultsData->ensureKnownResultLoaded( limits.filterAddress ); + filterVector = resultsData->cellScalarResults( limits.filterAddress, timeStepIdx ); + } + else + { + filterVector.resize( pressure.size(), std::numeric_limits::infinity() ); + } + std::vector clusters( activeCellCount.value(), 0 ); auto start = std::chrono::high_resolution_clock::now(); int numClusters = limits.maxClusters; @@ -122,6 +133,7 @@ void RigWellTargetCandidatesGenerator::generateCandidates( RimEclipseCase* transmissibilityX, transmissibilityY, transmissibilityZ, + filterVector, clusters ); if ( startCell.has_value() ) @@ -143,6 +155,7 @@ void RigWellTargetCandidatesGenerator::generateCandidates( RimEclipseCase* transmissibilityX, transmissibilityY, transmissibilityZ, + filterVector, clusters, clusterId, timeStepIdx, @@ -247,6 +260,7 @@ std::optional RigWellTargetCandidatesGenerator::findStartCell( RimE const std::vector& transmissibilityX, const std::vector& transmissibilityY, const std::vector& transmissibilityZ, + const std::vector& filterVector, const std::vector& clusters ) { auto resultsData = eclipseCase->results( RiaDefines::PorosityModelType::MATRIX_MODEL ); @@ -273,7 +287,10 @@ std::optional RigWellTargetCandidatesGenerator::findStartCell( RimE const bool permeabilityValidInAnyDirection = ( cellPermeabiltyX >= limits.permeability || cellPermeabiltyY >= limits.permeability || cellPermeabiltyZ >= limits.permeability ); - if ( cellVolume > maxVolume && cellVolume >= limits.volume && cellPressure >= limits.pressure && permeabilityValidInAnyDirection ) + const bool filterValue = !std::isinf( filterVector[resultIndex] ) && filterVector[resultIndex] > 0.0; + + if ( cellVolume > maxVolume && cellVolume >= limits.volume && cellPressure >= limits.pressure && + permeabilityValidInAnyDirection && filterValue ) { maxVolume = cellVolume; startCell = reservoirCellIdx; @@ -300,6 +317,7 @@ void RigWellTargetCandidatesGenerator::growCluster( RimEclipseCase* e const std::vector& transmissibilityX, const std::vector& transmissibilityY, const std::vector& transmissibilityZ, + const std::vector& filterVector, std::vector& clusters, int clusterId, size_t timeStepIdx, @@ -325,6 +343,7 @@ void RigWellTargetCandidatesGenerator::growCluster( RimEclipseCase* e transmissibilityX, transmissibilityY, transmissibilityZ, + filterVector, clusters ); if ( foundCells.empty() ) break; assignClusterIdToCells( *resultsData->activeCellInfo(), foundCells, clusters, clusterId ); @@ -345,6 +364,7 @@ std::vector RigWellTargetCandidatesGenerator::findCandidates( const RimE const std::vector& transmissibilityX, const std::vector& transmissibilityY, const std::vector& transmissibilityZ, + const std::vector& filterVector, std::vector& clusters ) { std::vector candidates; @@ -388,8 +408,10 @@ std::vector RigWellTargetCandidatesGenerator::findCandidates( const RimE face, resultIndex, neighborResultIndex ); + bool filterValue = !std::isinf( filterVector[neighborResultIndex] ) && filterVector[neighborResultIndex] > 0.0; + if ( volume[neighborResultIndex] > limits.volume && pressure[neighborResultIndex] > limits.pressure && - permeability > limits.permeability && transmissibility > limits.transmissibility ) + permeability > limits.permeability && transmissibility > limits.transmissibility && filterValue ) { candidates.push_back( neighborResvCellIdx ); clusters[neighborResultIndex] = -1; @@ -677,8 +699,9 @@ std::vector statistics[i].totalPorvSoil += porvSoil[idx]; statistics[i].totalPorvSgas += porvSgas[idx]; statistics[i].totalPorvSoilAndSgas += porvSoilAndSgas[idx]; - statistics[i].totalFipOil += fipOil[idx]; - statistics[i].totalFipGas += fipGas[idx]; + + if ( idx < fipOil.size() ) statistics[i].totalFipOil += fipOil[idx]; + if ( idx < fipGas.size() ) statistics[i].totalFipGas += fipGas[idx]; double meanPermeability = ( permeabilityX[idx] + permeabilityY[idx] + permeabilityZ[idx] ) / 3.0; permeabilityCalculators[i].addValueAndWeight( meanPermeability, porv[idx] ); @@ -702,6 +725,7 @@ std::vector //-------------------------------------------------------------------------------------------------- RimRegularGridCase* RigWellTargetCandidatesGenerator::generateEnsembleCandidates( RimEclipseCaseEnsemble& ensemble, size_t timeStepIdx, + const cvf::Vec3st& resultGridCellCount, VolumeType volumeType, VolumesType volumesType, VolumeResultType volumeResultType, @@ -732,9 +756,10 @@ RimRegularGridCase* RigWellTargetCandidatesGenerator::generateEnsembleCandidates RimRegularGridCase* targetCase = new RimRegularGridCase; targetCase->setBoundingBox( boundingBox ); + targetCase->setCellCount( resultGridCellCount ); targetCase->createModel( "" ); - std::vector occupancy; + std::vector occurrence; std::map>> resultNamesAndSamples; resultNamesAndSamples["TOTAL_PORV_SOIL"] = {}; @@ -747,10 +772,23 @@ RimRegularGridCase* RigWellTargetCandidatesGenerator::generateEnsembleCandidates { auto task = progInfo.task( "Accumulating results.", 1 ); - accumulateResultsForSingleCase( *eclipseCase, *targetCase, resultNamesAndSamples, occupancy ); + accumulateResultsForSingleCase( *eclipseCase, *targetCase, resultNamesAndSamples, occurrence ); } - createResultVector( *targetCase, "OCCUPANCY", occupancy ); + auto createFractionVector = []( const std::vector& occurrence, int maxRealizationCount ) -> std::vector + { + std::vector fractions( occurrence.size() ); + std::transform( occurrence.begin(), + occurrence.end(), + fractions.begin(), + [maxRealizationCount]( int value ) { return static_cast( value ) / maxRealizationCount; } ); + + return fractions; + }; + + createResultVector( *targetCase, "OCCURRENCE", occurrence ); + std::vector probability = createFractionVector( occurrence, static_cast( ensemble.cases().size() ) ); + createResultVector( *targetCase, "PROBABILITY", probability ); for ( auto [resultName, vec] : resultNamesAndSamples ) { @@ -806,12 +844,12 @@ void RigWellTargetCandidatesGenerator::computeStatisticsAndCreateVectors( RimEcl if ( RiaStatisticsTools::isValidNumber( maxValue ) && maxValue > -std::numeric_limits::max() ) maxResults[i] = maxValue; } - createResultVector( targetCase, "ENSEMBLE_" + resultName + "_P10", p10Results ); - createResultVector( targetCase, "ENSEMBLE_" + resultName + "_P50", p50Results ); - createResultVector( targetCase, "ENSEMBLE_" + resultName + "_P90", p90Results ); - createResultVector( targetCase, "ENSEMBLE_" + resultName + "_MEAN", meanResults ); - createResultVector( targetCase, "ENSEMBLE_" + resultName + "_MIN", minResults ); - createResultVector( targetCase, "ENSEMBLE_" + resultName + "_MAX", maxResults ); + createResultVector( targetCase, resultName + "_P10", p10Results ); + createResultVector( targetCase, resultName + "_P50", p50Results ); + createResultVector( targetCase, resultName + "_P90", p90Results ); + createResultVector( targetCase, resultName + "_MEAN", meanResults ); + createResultVector( targetCase, resultName + "_MIN", minResults ); + createResultVector( targetCase, resultName + "_MAX", maxResults ); } //-------------------------------------------------------------------------------------------------- @@ -872,7 +910,7 @@ void RigWellTargetCandidatesGenerator::accumulateResultsForSingleCase( RimEclips if ( !std::isinf( clusterNum[resultIndex] ) && clusterNum[resultIndex] > 0 ) { occupancy[targetResultIndex]++; - for ( auto [resultName, vec] : resultNamesAndSamples ) + for ( const auto& [resultName, vec] : resultNamesAndSamples ) { namedOutputVector[resultName][targetResultIndex] = namedInputVector[resultName]->at( resultIndex ); } diff --git a/ApplicationLibCode/ReservoirDataModel/Well/RigWellTargetCandidatesGenerator.h b/ApplicationLibCode/ReservoirDataModel/Well/RigWellTargetCandidatesGenerator.h index 09be5daa1c..a5a6834d4b 100644 --- a/ApplicationLibCode/ReservoirDataModel/Well/RigWellTargetCandidatesGenerator.h +++ b/ApplicationLibCode/ReservoirDataModel/Well/RigWellTargetCandidatesGenerator.h @@ -23,6 +23,8 @@ #include "cvfBoundingBox.h" #include "cvfStructGrid.h" +#include "RigEclipseResultAddress.h" + #include #include @@ -61,12 +63,13 @@ class RigWellTargetCandidatesGenerator struct ClusteringLimits { - double volume; - double permeability; - double pressure; - double transmissibility; - int maxClusters; - int maxIterations; + double volume; + double permeability; + double pressure; + double transmissibility; + int maxClusters; + int maxIterations; + RigEclipseResultAddress filterAddress; }; static void generateCandidates( RimEclipseCase* eclipseCase, @@ -84,6 +87,7 @@ class RigWellTargetCandidatesGenerator static RimRegularGridCase* generateEnsembleCandidates( RimEclipseCaseEnsemble& ensemble, size_t timeStepIdx, + const cvf::Vec3st& resultGridCellCount, VolumeType volumeType, VolumesType volumesType, VolumeResultType volumeResultType, @@ -128,6 +132,7 @@ class RigWellTargetCandidatesGenerator const std::vector& transmissibilityX, const std::vector& transmissibilityY, const std::vector& transmissibilityZ, + const std::vector& filterVector, const std::vector& clusters ); static void growCluster( RimEclipseCase* eclipseCase, @@ -141,6 +146,7 @@ class RigWellTargetCandidatesGenerator const std::vector& transmissibilityX, const std::vector& transmissibilityY, const std::vector& transmissibilityZ, + const std::vector& filterVector, std::vector& clusters, int clusterId, size_t timeStepIdx, @@ -157,6 +163,7 @@ class RigWellTargetCandidatesGenerator const std::vector& transmissibilityX, const std::vector& transmissibilityY, const std::vector& transmissibilityZ, + const std::vector& filterVector, std::vector& clusters ); static void assignClusterIdToCells( const RigActiveCellInfo& activeCellInfo,