Fault Reactivation updates (#10665)

* Support getting pore pressure for all nodes
* Misc fixes and updates in fault reactivation UI
* Disable fault reactivation result unless you load an odb from a valid working folder
This commit is contained in:
jonjenssen 2023-09-27 15:15:17 +02:00 committed by GitHub
parent be0d83e41e
commit 0ccddcb836
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 357 additions and 10 deletions

View File

@ -62,9 +62,11 @@ void RicRunFaultReactModelingFeature::onActionTriggered( bool isChecked )
runProgress.setProgressDescription( "Writing input files." );
if ( model->fault() == nullptr )
auto [modelOk, errorMsg] = model->validateBeforeRun();
if ( !modelOk )
{
QMessageBox::critical( nullptr, frmTitle, "A fault has not selected. Please check your model settings." );
QMessageBox::critical( nullptr, frmTitle, QString::fromStdString( errorMsg ) );
return;
}

View File

@ -4,6 +4,7 @@ set(SOURCE_GROUP_HEADER_FILES
${CMAKE_CURRENT_LIST_DIR}/RimFaultReactivationModel.h
${CMAKE_CURRENT_LIST_DIR}/RimFaultReactivationModelCollection.h
${CMAKE_CURRENT_LIST_DIR}/RimFaultReactivationTools.h
${CMAKE_CURRENT_LIST_DIR}/RimFaultReactivationDataAccess.h
)
set(SOURCE_GROUP_SOURCE_FILES
@ -12,6 +13,7 @@ set(SOURCE_GROUP_SOURCE_FILES
${CMAKE_CURRENT_LIST_DIR}/RimFaultReactivationModel.cpp
${CMAKE_CURRENT_LIST_DIR}/RimFaultReactivationModelCollection.cpp
${CMAKE_CURRENT_LIST_DIR}/RimFaultReactivationTools.cpp
${CMAKE_CURRENT_LIST_DIR}/RimFaultReactivationDataAccess.cpp
)
list(APPEND CODE_HEADER_FILES ${SOURCE_GROUP_HEADER_FILES})

View File

@ -0,0 +1,118 @@
/////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2023 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 "RimFaultReactivationDataAccess.h"
#include "RiaPorosityModel.h"
#include "RigCaseCellResultsData.h"
#include "RigEclipseCaseData.h"
#include "RigEclipseResultAddress.h"
#include "RigFault.h"
#include "RigMainGrid.h"
#include "RigResultAccessorFactory.h"
#include "RimEclipseCase.h"
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RimFaultReactivationDataAccess::RimFaultReactivationDataAccess( RimEclipseCase* thecase, size_t timeStepIndex )
: m_case( thecase )
, m_caseData( nullptr )
, m_mainGrid( nullptr )
, m_timeStepIndex( timeStepIndex )
{
if ( m_case )
{
m_caseData = m_case->eclipseCaseData();
m_mainGrid = m_case->mainGrid();
}
if ( m_caseData )
{
RigEclipseResultAddress resVarAddress( RiaDefines::ResultCatType::DYNAMIC_NATIVE, "PRESSURE" );
m_case->results( RiaDefines::PorosityModelType::MATRIX_MODEL )->ensureKnownResultLoaded( resVarAddress );
m_resultAccessor = RigResultAccessorFactory::createFromResultAddress( m_caseData,
0,
RiaDefines::PorosityModelType::MATRIX_MODEL,
timeStepIndex,
resVarAddress );
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RimFaultReactivationDataAccess::~RimFaultReactivationDataAccess()
{
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimFaultReactivationDataAccess::useCellIndexAdjustment( std::map<size_t, size_t> adjustments )
{
m_cellIndexAdjustment = adjustments;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
double RimFaultReactivationDataAccess::porePressureAtPosition( const cvf::Vec3d& position, double defaultPorePressureGradient )
{
size_t cellIdx = cvf::UNDEFINED_SIZE_T;
if ( ( m_mainGrid != nullptr ) && m_resultAccessor.notNull() )
{
cellIdx = m_mainGrid->findReservoirCellIndexFromPoint( position );
// adjust cell index to be on correct side of fault
if ( auto search = m_cellIndexAdjustment.find( cellIdx ); search != m_cellIndexAdjustment.end() )
{
cellIdx = search->second;
}
if ( ( cellIdx != cvf::UNDEFINED_SIZE_T ) )
{
double value = m_resultAccessor->cellScalar( cellIdx );
if ( !std::isinf( value ) )
{
return 100000.0 * m_resultAccessor->cellScalar( cellIdx ); // return in pascal, not bar
}
}
}
return calculatePorePressure( std::abs( position.z() ), defaultPorePressureGradient );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
double RimFaultReactivationDataAccess::calculatePorePressure( double depth, double gradient )
{
return gradient * 9.81 * depth * 1000;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
size_t RimFaultReactivationDataAccess::timeStepIndex() const
{
return m_timeStepIndex;
}

View File

@ -0,0 +1,57 @@
/////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2021 - 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.
//
/////////////////////////////////////////////////////////////////////////////////
#pragma once
#include "cvfObject.h"
#include "cvfVector3.h"
#include <map>
class RimEclipseCase;
class RigEclipseCaseData;
class RigResultAccessor;
class RigMainGrid;
//==================================================================================================
///
///
//==================================================================================================
class RimFaultReactivationDataAccess
{
public:
RimFaultReactivationDataAccess( RimEclipseCase* thecase, size_t timeStepIndex );
~RimFaultReactivationDataAccess();
void useCellIndexAdjustment( std::map<size_t, size_t> adjustments );
double porePressureAtPosition( const cvf::Vec3d& position, double defaultPorePressureGradient );
size_t timeStepIndex() const;
protected:
double calculatePorePressure( double depth, double gradient );
private:
RimEclipseCase* m_case;
RigEclipseCaseData* m_caseData;
const RigMainGrid* m_mainGrid;
size_t m_timeStepIndex;
cvf::ref<RigResultAccessor> m_resultAccessor;
std::map<size_t, size_t> m_cellIndexAdjustment;
};

View File

@ -41,6 +41,7 @@
#include "RimEclipseView.h"
#include "RimFaultInView.h"
#include "RimFaultInViewCollection.h"
#include "RimFaultReactivationDataAccess.h"
#include "RimFaultReactivationTools.h"
#include "RimPolylineTarget.h"
#include "RimTimeStepFilter.h"
@ -163,6 +164,29 @@ caf::PdmFieldHandle* RimFaultReactivationModel::userDescriptionField()
return &m_userDescription;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::pair<bool, std::string> RimFaultReactivationModel::validateBeforeRun() const
{
if ( fault() == nullptr )
{
return std::make_pair( false, "A fault has not been selected. Please check your model settings." );
}
if ( selectedTimeSteps().size() < 2 )
{
return std::make_pair( false, "You need at least 2 selected timesteps. Please check your model settings." );
}
if ( selectedTimeSteps()[0] != m_availableTimeSteps[0] )
{
return std::make_pair( false, "The first available timestep must always be selected. Please check your model settings." );
}
return std::make_pair( true, "" );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
@ -527,7 +551,13 @@ void RimFaultReactivationModel::updateTimeSteps()
//--------------------------------------------------------------------------------------------------
std::vector<QDateTime> RimFaultReactivationModel::selectedTimeSteps() const
{
return m_selectedTimeSteps();
std::vector<QDateTime> dates;
for ( auto d : m_selectedTimeSteps() )
dates.push_back( d );
// selected dates might come in the order they were selected, sort them
std::sort( dates.begin(), dates.end() );
return dates;
}
//--------------------------------------------------------------------------------------------------
@ -625,8 +655,29 @@ bool RimFaultReactivationModel::exportModelSettings()
//--------------------------------------------------------------------------------------------------
bool RimFaultReactivationModel::extractAndExportModelData()
{
exportModelSettings();
model()->clearModelData();
if ( !exportModelSettings() ) return false;
auto eCase = eclipseCase();
// get the selected time step indexes
std::vector<size_t> selectedTimeStepIndexes;
for ( auto& timeStep : selectedTimeSteps() )
{
auto idx = std::find( m_availableTimeSteps.begin(), m_availableTimeSteps.end(), timeStep );
if ( idx == m_availableTimeSteps.end() ) return false;
selectedTimeStepIndexes.push_back( idx - m_availableTimeSteps.begin() );
}
// extract data for each timestep
size_t outputTimeStepIndex = 0;
for ( auto timeStepIdx : selectedTimeStepIndexes )
{
RimFaultReactivationDataAccess dataAccess( eCase, timeStepIdx );
model()->extractModelData( &dataAccess, outputTimeStepIndex++ );
}
// TODO - get values from eclipse and geomech models here
return true;
}

View File

@ -67,6 +67,8 @@ public:
QString userDescription();
void setUserDescription( QString description );
std::pair<bool, std::string> validateBeforeRun() const;
void setFault( RimFaultInView* fault );
RimFaultInView* fault() const;

View File

@ -101,6 +101,14 @@ RimGeoMechFaultReactivationResult::~RimGeoMechFaultReactivationResult()
{
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
bool RimGeoMechFaultReactivationResult::isValid() const
{
return m_bHaveValidData;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
@ -109,7 +117,9 @@ void RimGeoMechFaultReactivationResult::onLoadDataAndUpdate()
auto geomCase = geoMechCase();
if ( geomCase == nullptr ) return;
auto filename = geomCase->gridFileName();
auto filename = geomCase->gridFileName();
if ( !filename.toLower().endsWith( ".odb" ) ) return;
QFileInfo fi( filename );
auto folder = fi.path();
auto basename = fi.baseName();

View File

@ -47,6 +47,8 @@ public:
void onLoadDataAndUpdate();
bool isValid() const;
private:
QList<caf::PdmOptionItemInfo> calculateValueOptions( const caf::PdmFieldHandle* fieldNeedingOptions ) override;
void defineUiOrdering( QString uiConfigName, caf::PdmUiOrdering& uiOrdering ) override;

View File

@ -201,7 +201,13 @@ void RimGeoMechView::onLoadDataAndUpdate()
if ( m_partsCollection ) m_partsCollection->syncWithCase( m_geomechCase );
if ( m_faultReactivationResult ) m_faultReactivationResult->onLoadDataAndUpdate();
if ( m_faultReactivationResult )
{
if ( m_geomechCase->gridFileName().toLower().endsWith( ".odb" ) )
{
m_faultReactivationResult->onLoadDataAndUpdate();
}
}
scheduleCreateDisplayModelAndRedraw();
@ -1025,7 +1031,11 @@ void RimGeoMechView::defineUiTreeOrdering( caf::PdmUiTreeOrdering& uiTreeOrderin
uiTreeOrdering.add( m_tensorResults() );
uiTreeOrdering.add( m_cellFilterCollection() );
uiTreeOrdering.add( m_propertyFilterCollection() );
uiTreeOrdering.add( m_faultReactivationResult() );
if ( ( m_faultReactivationResult() != nullptr ) && ( m_faultReactivationResult->isValid() ) )
{
uiTreeOrdering.add( m_faultReactivationResult() );
}
addRequiredUiTreeObjects( uiTreeOrdering );

View File

@ -22,6 +22,8 @@
#include "RigFemPartCollection.h"
#include "RigFemPartResultsCollection.h"
#include "RigGeoMechCaseData.h"
#include "RigHexIntersectionTools.h"
#include "RimGeoMechCase.h"
#include "../cafHexInterpolator/cafHexInterpolator.h" // Use relative path, as this is a header only file not part of a library
@ -67,11 +69,21 @@ int RimWellIADataAccess::elementIndex( cvf::Vec3d position )
cvf::BoundingBox bb;
bb.add( position );
auto part = m_caseData->femParts()->part( 0 );
std::vector<size_t> closeElements;
m_caseData->femParts()->part( 0 )->findIntersectingElementIndices( bb, &closeElements );
part->findIntersectingElementIndices( bb, &closeElements );
if ( closeElements.empty() ) return -1;
return (int)closeElements[0];
for ( auto elmIdx : closeElements )
{
std::array<cvf::Vec3d, 8> coordinates;
if ( !part->fillElementCoordinates( elmIdx, coordinates ) ) continue;
if ( RigHexIntersectionTools::isPointInCell( position, coordinates.data() ) ) return (int)elmIdx;
}
return -1;
}
//--------------------------------------------------------------------------------------------------

View File

@ -21,6 +21,8 @@
#include "RigGriddedPart3d.h"
#include "RigPolyLinesData.h"
#include "RimFaultReactivationDataAccess.h"
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
@ -57,6 +59,8 @@ RigFaultReactivationModel::RigFaultReactivationModel()
for ( auto part : allGridParts() )
{
m_3dparts[part] = std::make_shared<RigGriddedPart3d>( part == GridPart::PART2 );
m_cellIndexAdjustmentMap[part] = {};
}
}
@ -101,6 +105,17 @@ void RigFaultReactivationModel::reset()
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RigFaultReactivationModel::clearModelData()
{
for ( auto part : allGridParts() )
{
m_3dparts[part]->clearModelData();
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
@ -328,3 +343,15 @@ std::shared_ptr<RigGriddedPart3d> RigFaultReactivationModel::grid( GridPart part
{
return m_3dparts.at( part );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RigFaultReactivationModel::extractModelData( RimFaultReactivationDataAccess* dataAccess, size_t outputTimeStep )
{
for ( auto part : allGridParts() )
{
dataAccess->useCellIndexAdjustment( m_cellIndexAdjustmentMap[part] );
m_3dparts[part]->extractModelData( dataAccess, outputTimeStep );
}
}

View File

@ -30,6 +30,7 @@
#include <vector>
class RigGriddedPart3d;
class RimFaultReactivationDataAccess;
class RigFRModelPart
{
@ -94,6 +95,9 @@ public:
std::shared_ptr<RigGriddedPart3d> grid( GridPart part ) const;
void clearModelData();
void extractModelData( RimFaultReactivationDataAccess* dataAccess, size_t outputTimeStep );
protected:
void generateGrids( cvf::Vec3dArray points );
@ -122,4 +126,6 @@ private:
bool m_isValid;
std::map<GridPart, std::shared_ptr<RigGriddedPart3d>> m_3dparts;
std::map<GridPart, std::map<size_t, size_t>> m_cellIndexAdjustmentMap;
};

View File

@ -18,6 +18,8 @@
#include "RigGriddedPart3d.h"
#include "RimFaultReactivationDataAccess.h"
#include "cvfTextureImage.h"
//--------------------------------------------------------------------------------------------------
@ -46,6 +48,16 @@ void RigGriddedPart3d::reset()
m_nodes.clear();
m_elementIndices.clear();
m_meshLines.clear();
clearModelData();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RigGriddedPart3d::clearModelData()
{
m_nodePorePressure.clear();
}
//--------------------------------------------------------------------------------------------------
@ -344,3 +356,29 @@ const std::map<RigGriddedPart3d::Boundary, std::vector<unsigned int>>& RigGridde
{
return m_boundaryNodes;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
const std::vector<double>& RigGriddedPart3d::nodePorePressure( size_t outputTimeStep ) const
{
if ( outputTimeStep >= m_nodePorePressure.size() ) return m_emptyData;
return m_nodePorePressure[outputTimeStep];
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RigGriddedPart3d::extractModelData( RimFaultReactivationDataAccess* dataAccess, size_t outputTimeStep )
{
if ( m_nodePorePressure.size() <= outputTimeStep )
{
m_nodePorePressure.resize( outputTimeStep + 1 );
}
for ( auto& node : m_nodes )
{
double pressure = dataAccess->porePressureAtPosition( node, 1.0 );
m_nodePorePressure[outputTimeStep].push_back( pressure );
}
}

View File

@ -24,6 +24,8 @@
#include <map>
#include <vector>
class RimFaultReactivationDataAccess;
//==================================================================================================
///
///
@ -51,6 +53,7 @@ public:
~RigGriddedPart3d() override;
void reset();
void clearModelData();
void generateGeometry( std::vector<cvf::Vec3d> inputPoints,
int nHorzCells,
@ -59,6 +62,8 @@ public:
int nVertCellsUpper,
double thickness );
void extractModelData( RimFaultReactivationDataAccess* dataAccess, size_t outputTimeStep );
const std::vector<cvf::Vec3d>& nodes() const;
const std::vector<std::vector<unsigned int>>& elementIndices() const;
const std::map<BorderSurface, std::vector<unsigned int>>& borderSurfaceElements() const;
@ -67,6 +72,8 @@ public:
const std::map<Boundary, std::vector<unsigned int>>& boundaryElements() const;
const std::map<Boundary, std::vector<unsigned int>>& boundaryNodes() const;
const std::vector<double>& nodePorePressure( size_t outputTimeStep ) const;
protected:
cvf::Vec3d stepVector( cvf::Vec3d start, cvf::Vec3d stop, int nSteps );
void generateMeshlines( std::vector<cvf::Vec3d> cornerPoints, int numHorzCells, int numVertCells );
@ -80,4 +87,7 @@ private:
std::vector<std::vector<cvf::Vec3d>> m_meshLines;
std::map<Boundary, std::vector<unsigned int>> m_boundaryElements;
std::map<Boundary, std::vector<unsigned int>> m_boundaryNodes;
std::vector<std::vector<double>> m_nodePorePressure;
const std::vector<double> m_emptyData;
};