Well target updates (#14619)

Support well target mapping calculations for ensembles.
This commit is contained in:
JJ
2026-08-28 13:36:02 +02:00
committed by GitHub
parent 04be720491
commit 75ebd7f20b
19 changed files with 248 additions and 81 deletions
@@ -64,6 +64,7 @@
#include "RimCellFilterCollection.h"
#include "RimCommandRouter.h"
#include "RimCompletionTemplateCollection.h"
#include "RimEclipseCase.h"
#include "RimEclipseCaseCollection.h"
#include "RimEclipseCaseEnsemble.h"
#include "RimEclipseView.h"
@@ -115,6 +116,7 @@
#include "RimWellPath.h"
#include "RimWellPathCollection.h"
#include "RimWellPathFracture.h"
#include "RimWellTargetMapping.h"
#include "VerticalFlowPerformance/RimVfpDataCollection.h"
#include "VerticalFlowPerformance/RimVfpPlotCollection.h"
@@ -865,6 +867,15 @@ bool RiaApplication::loadProject( const QString& projectFileName, ProjectLoadAct
{
view->loadDataAndUpdate();
}
for ( auto wellTargetMapping : gridEnsemble->wellTargetMappings() )
{
if ( !wellTargetMapping->ensembleStatisticsCase() ) continue;
for ( auto view : wellTargetMapping->ensembleStatisticsCase()->views() )
{
view->loadDataAndUpdate();
}
}
}
}
@@ -51,7 +51,16 @@ CAF_CMD_SOURCE_INIT( RicCloseCaseFeature, "RicCloseCaseFeature" );
//--------------------------------------------------------------------------------------------------
bool RicCloseCaseFeature::isCommandEnabled() const
{
return !selectedCases().empty();
if ( !selectedCases().empty() )
{
for ( auto ecase : selectedCases() )
{
if ( !ecase->isDeletable() ) return false;
}
return true;
}
return false;
}
//--------------------------------------------------------------------------------------------------
@@ -39,16 +39,11 @@ bool RicNewWellTargetMappingFeature::isCommandEnabled() const
{
if ( auto gridEnsembles = caf::selectedObjectsByTypeStrict<RimReservoirGridEnsemble*>(); !gridEnsembles.empty() )
{
// Computation of well target mappings for individual grids is implemented in RimWellTargetMapping::generateEnsembleStatistics().
//
// If there is a future need to support well target mappings for ensembles with individual grids, the implementation in
// RimWellTargetMapping::generateEnsembleStatistics() will need to be updated, and this check can be removed.
//
// The main performance reason is the memory consumption of loading all grids in the ensemble into memory at the same time, which is
// currently needed in order to compute the well target mapping for ensembles with individual grids. For ensembles with shared grid,
// only the shared grid needs to be loaded, which is much more memory efficient.
return false;
return true;
}
if ( auto cases = caf::selectedObjectsByType<RimEclipseCase*>(); !cases.empty() )
{
return ( cases.front()->ensemble() == nullptr );
}
return true;
}
@@ -614,6 +614,11 @@ void RimStatisticsContourMap::onComputeStatisticsClicked()
void RimStatisticsContourMap::computeStatistics()
{
computeStatisticsForMaps( { this } );
if ( auto ensemble = firstAncestorOrThisOfType<RimReservoirGridEnsemble>() )
{
ensemble->reloadMetaDataIfNeeded();
}
}
//--------------------------------------------------------------------------------------------------
@@ -682,7 +687,7 @@ void RimStatisticsContourMap::computeStatisticsForMaps( const std::vector<RimSta
// The bounding box is empty unless the primary case is open with active cell info
cvf::BoundingBox gridBoundingBox = primaryCase->activeCellsBoundingBox();
gridBoundingBox.expandPercent( map->m_boundingBoxExpPercent() );
gridBoundingBox.expandPercent( map->m_boundingBoxExpPercent(), map->m_boundingBoxExpPercent() );
double sampleSpacing = 1.0;
if ( auto mainGrid = primaryCase->mainGrid() ) sampleSpacing = map->sampleSpacingFactor() * mainGrid->characteristicIJCellSize();
@@ -722,12 +727,7 @@ void RimStatisticsContourMap::computeStatisticsForMaps( const std::vector<RimSta
RiaLogging::info( std::format( "Computing statistics for {} ensemble contour map(s)", contexts.size() ) );
// All maps belong to the same ensemble, so the realization cases are shared
auto cases = contexts.front().map->ensembleCases();
auto casesInViews = contexts.front().map->ensembleCasesInViews();
std::set<RimEclipseCase*> primaryCases;
for ( const auto& ctx : contexts )
primaryCases.insert( ctx.map->eclipseCase() );
auto cases = contexts.front().map->ensembleCases();
const size_t nCases = cases.size();
caf::ProgressInfo progInfo( nCases, QString( "Reading Eclipse Ensemble" ) );
@@ -739,6 +739,8 @@ void RimStatisticsContourMap::computeStatisticsForMaps( const std::vector<RimSta
{
auto task = progInfo.task( QString( "Processing Case %1 of %2" ).arg( i++ ).arg( nCases ) );
bool closeCase = !eCase->isReservoirCaseOpen();
RifReaderSettings oldSettings = eCase->readerSettings();
eCase->setReaderSettings( readerSettings );
@@ -796,7 +798,7 @@ void RimStatisticsContourMap::computeStatisticsForMaps( const std::vector<RimSta
// Release the grid data for cases that were opened only to compute statistics. A case is kept open if it has
// its own views, if it is the primary case of one of the contour maps, or if it is displayed in one of the
// ensemble views.
if ( eCase->views().empty() && !primaryCases.contains( eCase ) && !casesInViews.contains( eCase ) )
if ( closeCase )
{
eCase->closeReservoirCase();
}
@@ -981,6 +983,12 @@ void RimStatisticsContourMap::ensureResultsComputed()
}
computeStatisticsForMaps( maps );
// contour map calculations on shared grids clears the ensemble meta data, reload it
if ( auto ensemble = firstAncestorOrThisOfType<RimReservoirGridEnsemble>() )
{
ensemble->reloadMetaDataIfNeeded();
}
}
//--------------------------------------------------------------------------------------------------
@@ -1119,6 +1119,15 @@ bool RimEclipseCase::ensureReservoirCaseIsOpen()
return openReservoirCase();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
bool RimEclipseCase::isReservoirCaseOpen() const
{
// for most grid case types, this is a sufficient test
return eclipseCaseData() != nullptr;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
@@ -1468,3 +1477,11 @@ RimFaultDistanceCollection* RimEclipseCase::faultDistanceCollection() const
{
return m_dataAnalyticsCollection ? m_dataAnalyticsCollection->faultDistanceCollection() : nullptr;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RimReservoirGridEnsemble* RimEclipseCase::ensemble() const
{
return firstAncestorOrThisOfType<RimReservoirGridEnsemble>();
}
@@ -60,6 +60,7 @@ class RimReservoirCellResultsStorage;
class RimEclipseResultAddressCollection;
class RimEclipseViewCollection;
class RimResultNameAlias;
class RimReservoirGridEnsemble;
namespace caf
{
@@ -87,6 +88,7 @@ public:
bool ensureReservoirCaseIsOpen();
bool openReservoirCase();
virtual bool isReservoirCaseOpen() const;
virtual void closeReservoirCase();
virtual bool openEclipseGridFile() = 0;
virtual void reloadEclipseGridFile();
@@ -155,6 +157,8 @@ public:
void updateFormationNamesData() override;
RimReservoirGridEnsemble* ensemble() const;
protected:
void initAfterRead() override;
void fieldChangedByUi( const caf::PdmFieldHandle* changedField, const QVariant& oldValue, const QVariant& newValue ) override;
@@ -415,6 +415,14 @@ void RimEclipseResultCase::closeReservoirCase()
m_activeCellInfoIsReadFromFile = false;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
bool RimEclipseResultCase::isReservoirCaseOpen() const
{
return RimEclipseCase::isReservoirCaseOpen() && m_gridAndWellDataIsReadFromFile && m_activeCellInfoIsReadFromFile;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
@@ -58,6 +58,8 @@ public:
bool hasSourSimFile();
bool openEclipseGridFile() override;
bool isReservoirCaseOpen() const override;
void closeReservoirCase() override;
bool importGridAndResultMetaData( bool showTimeStepFilter );
@@ -141,6 +141,8 @@ RimEclipseStatisticsCase::RimEclipseStatisticsCase()
m_activeFormationNames.uiCapability()->setUiHidden( true );
m_displayNameOption = RimCaseDisplayNameTools::DisplayName::CUSTOM;
setDeletable( true );
}
//--------------------------------------------------------------------------------------------------
@@ -405,10 +405,9 @@ void RimGridView::onCreatePartCollectionFromSelection( cvf::Collection<cvf::Part
{
RiuEclipseSelectionItem* eclipseSelItem = static_cast<RiuEclipseSelectionItem*>( items[i] );
if ( eclipseSelItem && eclipseSelItem->m_view == this && eclipseSelItem->m_resultDefinition->eclipseCase() )
if ( eclipseSelItem && eclipseSelItem->m_view == this && eclipseSelItem->m_resultDefinition->eclipseCase() &&
eclipseSelItem->m_resultDefinition->eclipseCase()->eclipseCaseData() )
{
CAF_ASSERT( eclipseSelItem->m_resultDefinition->eclipseCase()->eclipseCaseData() );
RivSingleCellPartGenerator partGen( eclipseSelItem->m_resultDefinition->eclipseCase()->eclipseCaseData(),
eclipseSelItem->m_gridIndex,
eclipseSelItem->m_gridLocalCellIndex,
@@ -108,6 +108,9 @@ RimReservoirGridEnsemble::RimReservoirGridEnsemble()
m_statisticsCaseCollection->uiCapability()->setUiName( "Derived Statistics" );
m_statisticsCaseCollection->uiCapability()->setUiIconFromResourceString( ":/Histograms16x16.png" );
CAF_PDM_InitFieldNoDefault( &m_ensembleCase, "EnsembleCase", "Ensemble Grid" );
m_ensembleCase = nullptr;
CAF_PDM_InitFieldNoDefault( &m_viewCollection, "ViewCollection", "Views" );
m_viewCollection = new RimEclipseViewCollection;
m_viewCollection->setEclipseCaseProvider( [this]() { return this->cases(); } );
@@ -563,6 +566,7 @@ std::set<RimEclipseCase*> RimReservoirGridEnsemble::casesInViews() const
void RimReservoirGridEnsemble::addWellTargetMapping( RimWellTargetMapping* wellTargetMapping )
{
m_wellTargetMappings.push_back( wellTargetMapping );
wellTargetMapping->setName( QString( "Well Target Mapping #%1" ).arg( m_wellTargetMappings.size() ) );
wellTargetMapping->updateResultDefinition();
}
@@ -927,6 +931,17 @@ void RimReservoirGridEnsemble::loadGridDataFromFiles()
updateConnectedEditors();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimReservoirGridEnsemble::reloadMetaDataIfNeeded()
{
if ( gridMode() == GridModeType::INDIVIDUAL_GRIDS ) return;
m_mainGrid = nullptr;
loadGridDataFromFiles();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
@@ -84,6 +84,7 @@ public:
// Deferred loading control
void loadGridDataFromFiles();
bool isGridDataLoaded() const;
void reloadMetaDataIfNeeded();
// Helper methods
bool hasSharedGrid() const;
@@ -157,6 +158,7 @@ private:
// Cases
caf::PdmChildField<RimCaseCollection*> m_caseCollection;
caf::PdmChildField<RimCaseCollection*> m_statisticsCaseCollection;
caf::PdmChildField<RimEclipseCase*> m_ensembleCase;
// Grid mode
caf::PdmField<bool> m_autoDetectGridType;
@@ -41,12 +41,14 @@
#include "RimEclipsePropertyFilterCollection.h"
#include "RimEclipseResultDefinition.h"
#include "RimEclipseView.h"
#include "RimEclipseViewCollection.h"
#include "RimRegularGridCase.h"
#include "RimReservoirGridEnsemble.h"
#include "RimTools.h"
#include "cafPdmUiDoubleSliderEditor.h"
#include "cafPdmUiSliderTools.h"
#include "cafPdmUiTreeOrdering.h"
#include <cmath>
#include <limits>
@@ -93,6 +95,8 @@ RimWellTargetMapping::RimWellTargetMapping()
{
CAF_PDM_InitObject( "Well Target Mapping", ":/WellTargets.png" );
setName( "Well Target Mapping" );
CAF_PDM_InitField( &m_timeStep, "TimeStep", 0, "Time Step" );
CAF_PDM_InitFieldNoDefault( &m_volumeType, "VolumeType", "Volume" );
@@ -141,6 +145,21 @@ RimWellTargetMapping::RimWellTargetMapping()
CAF_PDM_InitField( &m_cellCountJ, "CellCountJ", 100, "Cell Count J" );
CAF_PDM_InitField( &m_cellCountK, "CellCountK", 10, "Cell Count K" );
CAF_PDM_InitField( &m_expandBoundingBoxXYPercent,
"ExpandBoundingBoxXYPercent",
5.0,
"Expand Bounding Box XY [%]",
"",
"How much to increase the bounding box of the first case to cover for any grid size differences across the "
"ensemble." );
CAF_PDM_InitField( &m_expandBoundingBoxZPercent,
"ExpandBoundingBoxZPercent",
10.0,
"Expand Bounding Box Z [%]",
"",
"How much to increase the bounding box of the first case to cover for any grid size differences across the "
"ensemble." );
CAF_PDM_InitFieldNoDefault( &m_filterView, "FilterView", "Filter By View" );
CAF_PDM_InitFieldNoDefault( &m_ensembleStatisticsCase, "EnsembleStatisticsCase", "Ensemble Statistics Case" );
@@ -180,7 +199,10 @@ RimWellTargetMapping::~RimWellTargetMapping()
//--------------------------------------------------------------------------------------------------
void RimWellTargetMapping::fieldChangedByUi( const caf::PdmFieldHandle* changedField, const QVariant& oldValue, const QVariant& newValue )
{
updateAllBoundaries();
if ( changedField != nameField() )
{
updateAllBoundaries();
}
}
//--------------------------------------------------------------------------------------------------
@@ -384,15 +406,16 @@ void RimWellTargetMapping::generateCandidates( RimEclipseCase* eclipseCase, bool
//--------------------------------------------------------------------------------------------------
void RimWellTargetMapping::generateEnsembleStatistics()
{
// See RicNewWellTargetMappingFeature::isCommandEnabled() for comment on future support for ensembles with varying geometry.
auto ensemble = firstAncestorOrThisOfType<RimEclipseCaseEnsemble>();
auto ensemble = firstAncestorOrThisOfType<RimReservoirGridEnsemble>();
if ( !ensemble ) return;
const cvf::Vec3st& resultGridCellCount = getResultGridCellCount();
RigWellTargetMapping::ClusteringLimits limits = getClusteringLimits();
RigFloodingSettings floodingSettings( m_oilFloodingType(), m_userDefinedFloodingOil(), m_gasFloodingType(), m_userDefinedFloodingGas() );
double expandXY = ensemble->hasSharedGrid() ? 0.0 : m_expandBoundingBoxXYPercent();
double expandZ = ensemble->hasSharedGrid() ? 0.0 : m_expandBoundingBoxZPercent();
RimRegularGridCase* regularGridCase = RigWellTargetMapping::generateEnsembleCandidates( ensemble->cases(),
m_timeStep(),
resultGridCellCount,
@@ -400,9 +423,17 @@ void RimWellTargetMapping::generateEnsembleStatistics()
m_volumesType(),
m_volumeResultType(),
floodingSettings,
limits );
limits,
expandXY,
expandZ );
regularGridCase->setCustomCaseName( "Ensemble Grid" );
regularGridCase->setDeletable( false );
if ( m_ensembleStatisticsCase() != nullptr )
{
delete m_ensembleStatisticsCase();
}
m_ensembleStatisticsCase = regularGridCase;
@@ -411,14 +442,17 @@ void RimWellTargetMapping::generateEnsembleStatistics()
eclipseView->cellResult()->setResultType( RiaDefines::ResultCatType::GENERATED );
eclipseView->cellResult()->setResultVariable( "TOTAL_PORV_SOIL_P10" );
eclipseView->loadDataAndUpdate();
updateConnectedEditors();
m_ensembleStatisticsCase->updateConnectedEditors();
ensemble->reloadMetaDataIfNeeded();
if ( RiaGuiApplication::isRunning() || RiuMainWindow::instance() )
{
RiuMainWindow::instance()->selectAsCurrentItem( eclipseView->cellResult() );
}
eclipseView->loadDataAndUpdate();
m_ensembleStatisticsCase->updateConnectedEditors();
}
//--------------------------------------------------------------------------------------------------
@@ -426,6 +460,9 @@ void RimWellTargetMapping::generateEnsembleStatistics()
//--------------------------------------------------------------------------------------------------
void RimWellTargetMapping::defineUiOrdering( QString uiConfigName, caf::PdmUiOrdering& uiOrdering )
{
uiOrdering.add( nameField() );
uiOrdering.addNewButton( "Compute", [this]() { onGenerateButtonClicked(); } );
caf::PdmUiGroup* resultGroup = uiOrdering.addNewGroup( "Result" );
resultGroup->add( &m_timeStep );
resultGroup->add( &m_volumeType );
@@ -458,8 +495,9 @@ void RimWellTargetMapping::defineUiOrdering( QString uiConfigName, caf::PdmUiOrd
resultGroup->add( &m_volumesType );
auto hasEnsembleParent = firstAncestorOrThisOfType<RimEclipseCaseEnsemble>() != nullptr ||
firstAncestorOrThisOfType<RimReservoirGridEnsemble>() != nullptr;
auto gridEnsemble = firstAncestorOrThisOfType<RimReservoirGridEnsemble>();
auto hasEnsembleParent = firstAncestorOrThisOfType<RimEclipseCaseEnsemble>() != nullptr || gridEnsemble != nullptr;
if ( !hasEnsembleParent ) uiOrdering.add( &m_filterView );
caf::PdmUiGroup* minimumCellValuesGroup = uiOrdering.addNewGroup( "Minimum Cell Values" );
@@ -476,14 +514,18 @@ void RimWellTargetMapping::defineUiOrdering( QString uiConfigName, caf::PdmUiOrd
ensembleGridGroup->add( &m_cellCountI );
ensembleGridGroup->add( &m_cellCountJ );
ensembleGridGroup->add( &m_cellCountK );
if ( gridEnsemble && !gridEnsemble->hasSharedGrid() )
{
ensembleGridGroup->add( &m_expandBoundingBoxXYPercent );
ensembleGridGroup->add( &m_expandBoundingBoxZPercent );
}
}
caf::PdmUiGroup* advancedGroup = uiOrdering.addNewGroup( "Advanced" );
advancedGroup->add( &m_maxNumTargets );
advancedGroup->setCollapsedByDefault();
uiOrdering.addNewButton( "Generate", [this]() { onGenerateButtonClicked(); } );
uiOrdering.skipRemainingFields();
if ( m_minimumPressure == cvf::UNDEFINED_DOUBLE || m_maximumPressure == cvf::UNDEFINED_DOUBLE )
@@ -492,6 +534,19 @@ void RimWellTargetMapping::defineUiOrdering( QString uiConfigName, caf::PdmUiOrd
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimWellTargetMapping::defineUiTreeOrdering( caf::PdmUiTreeOrdering& uiTreeOrdering, QString uiConfigName )
{
if ( ensembleStatisticsCase() )
{
uiTreeOrdering.add( &m_ensembleStatisticsCase );
}
uiTreeOrdering.skipRemainingChildren();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
@@ -563,12 +618,15 @@ RimEclipseCase* RimWellTargetMapping::firstCase() const
//--------------------------------------------------------------------------------------------------
void RimWellTargetMapping::initAfterRead()
{
if ( RimEclipseCase* eclipseCase = firstCase() )
if ( firstAncestorOrThisOfType<RimReservoirGridEnsemble>() )
{
return; // no need to to do anything for ensembles, as the data have been cached
}
else if ( RimEclipseCase* eclipseCase = firstCase() )
{
// Automatically generate results on project load
m_resultDefinition->setEclipseCase( eclipseCase );
// Automatically generate results on project load
// Consider to also do this for ensemble cases, but this will be more expensive
bool setTimeStepInView = false;
generateCandidates( eclipseCase, setTimeStepInView );
}
@@ -641,20 +699,10 @@ void RimWellTargetMapping::resetMinimumCellValuesToDefault()
//--------------------------------------------------------------------------------------------------
void RimWellTargetMapping::onGenerateButtonClicked()
{
auto hasEclipseCaseEnsembleParent = firstAncestorOrThisOfType<RimEclipseCaseEnsemble>() != nullptr;
auto hasGridEnsembleParent = firstAncestorOrThisOfType<RimReservoirGridEnsemble>() != nullptr;
if ( hasEclipseCaseEnsembleParent )
if ( firstAncestorOrThisOfType<RimReservoirGridEnsemble>() != nullptr )
{
generateEnsembleStatistics();
}
else if ( hasGridEnsembleParent )
{
// For RimReservoirGridEnsemble implement two variants, shared grid and individual grids. Individual grids variant will require more
// work, and is not currently prioritized, so for now just show a warning if user tries to generate candidates for a
// RimReservoirGridEnsemble
//
// See RicNewWellTargetMappingFeature::isCommandEnabled() for comment on future support for ensembles with varying geometry.
}
else if ( auto eclipseCase = firstCase() )
{
generateCandidates( eclipseCase );
@@ -18,6 +18,8 @@
#pragma once
#include "RimNamedObject.h"
#include "cafAppEnum.h"
#include "cafPdmField.h"
#include "cafPdmObject.h"
@@ -34,7 +36,7 @@ class RimEclipseView;
///
///
//==================================================================================================
class RimWellTargetMapping : public caf::PdmObject
class RimWellTargetMapping : public RimNamedObject
{
CAF_PDM_HEADER_INIT;
@@ -53,9 +55,11 @@ public:
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;
void defineUiOrdering( QString uiConfigName, caf::PdmUiOrdering& uiOrdering ) override;
void defineUiTreeOrdering( caf::PdmUiTreeOrdering& uiTreeOrdering, QString uiConfigName ) override;
void initAfterRead() override;
QList<caf::PdmOptionItemInfo> calculateValueOptions( const caf::PdmFieldHandle* fieldNeedingOptions ) override;
void defineUiOrdering( QString uiConfigName, caf::PdmUiOrdering& uiOrdering ) override;
void initAfterRead() override;
private:
void generateCandidates( RimEclipseCase* eclipseCase, bool setTimeStepInView = true );
@@ -90,6 +94,9 @@ private:
caf::PdmField<double> m_permeability;
caf::PdmField<double> m_transmissibility;
caf::PdmField<double> m_expandBoundingBoxXYPercent;
caf::PdmField<double> m_expandBoundingBoxZPercent;
caf::PdmField<int> m_maxIterations;
caf::PdmField<int> m_maxNumTargets;
@@ -24,6 +24,7 @@
#include "RiaEclipseUnitTools.h"
#include "RiaLogging.h"
#include "RiaPorosityModel.h"
#include "RiaPreferencesGrid.h"
#include "RiaQStringFormatter.h"
#include "RiaResultNames.h"
#include "RiaWeightedMeanCalculator.h"
@@ -364,27 +365,32 @@ RimRegularGridCase* RigWellTargetMapping::generateEnsembleCandidates( const std:
VolumesType volumesType,
VolumeResultType volumeResultType,
const RigFloodingSettings& floodingSettings,
const ClusteringLimits& limits )
const ClusteringLimits& limits,
double expandBoundingBoxXYPercent,
double expandBoundingBoxZPercent )
{
RiaLogging::debug( "Generating ensemble statistics" );
caf::ProgressInfo progInfo( cases.size() * 2, "Generating ensemble statistics" );
for ( auto eclipseCase : cases )
{
auto task = progInfo.task( "Generating realization statistics.", 1 );
generateCandidates( eclipseCase, timeStepIdx, volumeType, volumesType, volumeResultType, floodingSettings, limits, false );
}
caf::ProgressInfo progInfo( cases.size(), "Generating ensemble statistics" );
cvf::BoundingBox boundingBox;
for ( auto eclipseCase : cases )
{
cvf::BoundingBox bb =
RigWellTargetMappingTools::computeBoundingBoxForResult( *eclipseCase, RigWellTargetMapping::wellTargetResultName(), timeStepIdx );
boundingBox.add( bb );
if ( eclipseCase->eclipseCaseData() != nullptr )
{
boundingBox.add( eclipseCase->activeCellsBoundingBox() );
break;
}
}
if ( !boundingBox.isValid() )
{
RiaLogging::error( "Failed to compute valid bounding box for ensemble statistics. At least one view must be opened" );
return nullptr;
}
boundingBox.expandPercent( expandBoundingBoxXYPercent, expandBoundingBoxZPercent );
RiaLogging::debug( QString( "Clusters bounding box min: [%1 %2 %3]" )
.arg( boundingBox.min().x() )
.arg( boundingBox.min().y() )
@@ -414,13 +420,44 @@ RimRegularGridCase* RigWellTargetMapping::generateEnsembleCandidates( const std:
resultNamesAndSamples["TOTAL_SFIPOIL"] = {};
resultNamesAndSamples["TOTAL_SFIPGAS"] = {};
auto readerSettings = RiaPreferencesGrid::gridOnlyReaderSettings();
readerSettings.onlyLoadActiveCells = true;
auto oldReaderType = RiaPreferencesGrid::current()->gridModelReaderOverride();
RiaPreferencesGrid::current()->setGridModelReaderOverride( RiaDefines::GridModelReader::OPM_COMMON );
for ( auto eclipseCase : cases )
{
auto task = progInfo.task( "Accumulating results.", 1 );
auto task = progInfo.task( "Generating realization statistics.", 1 );
RigWellTargetMappingTools::accumulateResultsForSingleCase( *eclipseCase, *targetCase, resultNamesAndSamples, occurrence, timeStepIdx );
bool closeGrid = ( !eclipseCase->isReservoirCaseOpen() );
if ( closeGrid )
{
RiaLogging::debug( QString( "Opening grid for case: %1" ).arg( eclipseCase->gridFileName() ).toStdString() );
}
auto oldReaderSettings = eclipseCase->readerSettings();
if ( closeGrid )
{
eclipseCase->setReaderSettings( readerSettings );
}
if ( eclipseCase->ensureReservoirCaseIsOpen() )
{
generateCandidates( eclipseCase, timeStepIdx, volumeType, volumesType, volumeResultType, floodingSettings, limits, false );
RigWellTargetMappingTools::accumulateResultsForSingleCase( *eclipseCase, *targetCase, resultNamesAndSamples, occurrence, timeStepIdx );
}
if ( closeGrid )
{
eclipseCase->closeReservoirCase();
eclipseCase->setReaderSettings( oldReaderSettings );
RiaLogging::debug( QString( "Closing grid for case: %1" ).arg( eclipseCase->gridFileName() ).toStdString() );
}
}
RiaPreferencesGrid::current()->setGridModelReaderOverride( oldReaderType );
auto createFractionVector = []( const std::vector<int>& occurrence, int maxRealizationCount ) -> std::vector<double>
{
std::vector<double> fractions( occurrence.size() );
@@ -95,7 +95,9 @@ public:
VolumesType volumesType,
VolumeResultType volumeResultType,
const RigFloodingSettings& floodingSettings,
const ClusteringLimits& limits );
const ClusteringLimits& limits,
double expandBoundingBoxXYPercent,
double expandBoundingBoxZPercent );
static QString wellTargetResultName();
};
@@ -474,6 +474,7 @@ std::vector<ReservoirCellIndex> RigWellTargetMappingTools::findCandidates( RimEc
double transmissibility = data.transmissibilityNNC->at( nncResultIdx );
ActiveCellIndex otherResultIndex = resultsData->activeCellInfo()->cellResultIndex( otherCellIdx );
if ( otherResultIndex.value() == cvf::UNDEFINED_SIZE_T ) continue;
double permeability = data.permeabilityX[otherResultIndex.value()];
+11 -12
View File
@@ -41,7 +41,8 @@
#include <limits>
namespace cvf {
namespace cvf
{
@@ -68,8 +69,7 @@ namespace cvf {
//--------------------------------------------------------------------------------------------------
BoundingBox::BoundingBox(const Vec3d& min, const Vec3d& max)
: m_min(min), m_max(max)
{
}
{}
//--------------------------------------------------------------------------------------------------
@@ -77,8 +77,7 @@ namespace cvf {
//--------------------------------------------------------------------------------------------------
BoundingBox::BoundingBox(const Vec3f& min, const Vec3f& max)
: m_min(min), m_max(max)
{
}
{}
//--------------------------------------------------------------------------------------------------
@@ -87,8 +86,7 @@ namespace cvf {
BoundingBox::BoundingBox(const BoundingBox& other)
: m_min(other.m_min),
m_max(other.m_max)
{
}
{}
//--------------------------------------------------------------------------------------------------
@@ -359,14 +357,15 @@ namespace cvf {
///
/// If a bounding box is expanded by 10%, the bounding box's size will increase by 5% in each direction
//--------------------------------------------------------------------------------------------------
void BoundingBox::expandPercent(double percent)
void BoundingBox::expandPercent(double percentXY, double percentZ)
{
const auto ext = extent();
const double factor = percent / 100.0;
const double factorXY = percentXY / 100.0;
const double factorZ = percentZ / 100.0;
const double xHalf = (ext.x() / 2) * factor;
const double yHalf = (ext.y() / 2) * factor;
const double zHalf = (ext.z() / 2) * factor;
const double xHalf = (ext.x() / 2) * factorXY;
const double yHalf = (ext.y() / 2) * factorXY;
const double zHalf = (ext.z() / 2) * factorZ;
m_min.x() -= xHalf;
m_min.y() -= yHalf;
+3 -2
View File
@@ -41,7 +41,8 @@
#include "cvfMatrix4.h"
#include "cvfString.h"
namespace cvf {
namespace cvf
{
//==================================================================================================
@@ -83,7 +84,7 @@ namespace cvf {
void cornerVertices(Vec3d corners[8]) const;
void expand(double amount);
void expandPercent(double percent);
void expandPercent(double percentXY, double percentZ);
void transform(const Mat4d& matrix);
const BoundingBox getTransformed(const Mat4d& matrix) const;