///////////////////////////////////////////////////////////////////////////////// // // 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 // for more details. // ///////////////////////////////////////////////////////////////////////////////// #include "RimFaultReactivationDataAccessorStressGeoMech.h" #include "RiaEclipseUnitTools.h" #include "RiaLogging.h" #include "RigFaultReactivationModel.h" #include "RigFemAddressDefines.h" #include "RigFemPartCollection.h" #include "RigFemPartResultsCollection.h" #include "RigFemResultAddress.h" #include "RigFemScalarResultFrames.h" #include "RigGeoMechCaseData.h" #include "RigGeoMechWellLogExtractor.h" #include "RigGriddedPart3d.h" #include "RigResultAccessorFactory.h" #include "RigWellPath.h" #include "RimFaultReactivationDataAccessorStress.h" #include "RimFaultReactivationDataAccessorWellLogExtraction.h" #include "RimFaultReactivationEnums.h" #include "RimGeoMechCase.h" #include "RimWellIADataAccess.h" #include "cvfVector3.h" #include #include //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- RimFaultReactivationDataAccessorStressGeoMech::RimFaultReactivationDataAccessorStressGeoMech( RimGeoMechCase* geoMechCase, RimFaultReactivation::Property property, double gradient, double seabedDepth ) : RimFaultReactivationDataAccessorStress( property, gradient, seabedDepth ) , m_geoMechCase( geoMechCase ) { m_geoMechCaseData = geoMechCase->geoMechData(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- RimFaultReactivationDataAccessorStressGeoMech::~RimFaultReactivationDataAccessorStressGeoMech() { } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimFaultReactivationDataAccessorStressGeoMech::updateResultAccessor() { const int partIndex = 0; auto loadFrameLambda = [&]( auto femParts, RigFemResultAddress addr, int timeStepIndex ) -> RigFemScalarResultFrames* { auto result = femParts->findOrLoadScalarResult( partIndex, addr ); int frameIndex = result->frameCount( timeStepIndex ) - 1; if ( result->frameData( timeStepIndex, frameIndex ).empty() ) { return nullptr; } return result; }; auto femParts = m_geoMechCaseData->femPartResults(); m_femPart = femParts->parts()->part( partIndex ); int timeStepIndex = 0; m_s33Frames = loadFrameLambda( femParts, getResultAddress( "ST", "S33" ), timeStepIndex ); m_s11Frames = loadFrameLambda( femParts, getResultAddress( "ST", "S11" ), timeStepIndex ); m_s22Frames = loadFrameLambda( femParts, getResultAddress( "ST", "S22" ), timeStepIndex ); m_porBarFrames = loadFrameLambda( femParts, RigFemAddressDefines::nodalPorBarAddress(), timeStepIndex ); auto [faultTopPosition, faultBottomPosition] = m_model->faultTopBottom(); auto faultNormal = m_model->modelNormal() ^ cvf::Vec3d::Z_AXIS; faultNormal.normalize(); double distanceFromFault = 1.0; auto [topDepth, bottomDepth] = m_model->depthTopBottom(); for ( auto gridPart : m_model->allGridParts() ) { double sign = m_model->normalPointsAt() == gridPart ? -1.0 : 1.0; std::vector wellPoints = RimFaultReactivationDataAccessorWellLogExtraction::generateWellPoints( faultTopPosition, faultBottomPosition, m_seabedDepth, bottomDepth, sign * faultNormal * distanceFromFault ); cvf::ref wellPath = new RigWellPath( wellPoints, RimFaultReactivationDataAccessorWellLogExtraction::generateMds( wellPoints ) ); m_wellPaths[gridPart] = wellPath; std::string errorName = "fault reactivation data access"; cvf::ref extractor = new RigGeoMechWellLogExtractor( m_geoMechCaseData, partIndex, wellPath.p(), errorName ); m_extractors[gridPart] = extractor; } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- RigFemResultAddress RimFaultReactivationDataAccessorStressGeoMech::getResultAddress( const std::string& fieldName, const std::string& componentName ) { return RigFemResultAddress( RIG_ELEMENT_NODAL, fieldName, componentName ); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- bool RimFaultReactivationDataAccessorStressGeoMech::isDataAvailable() const { return m_s11Frames && m_s22Frames && m_s33Frames && m_porBarFrames && m_femPart; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- double RimFaultReactivationDataAccessorStressGeoMech::extractStressValue( StressType stressType, const cvf::Vec3d& position, RimFaultReactivation::GridPart gridPart ) const { RimWellIADataAccess iaDataAccess( m_geoMechCase ); int timeStepIndex = 0; RigFemScalarResultFrames* frames = dataFrames( stressType ); int frameIndex = frames->frameCount( timeStepIndex ) - 1; const std::vector& s11Data = frames->frameData( timeStepIndex, frameIndex ); return interpolatedResultValue( iaDataAccess, m_femPart, position, s11Data ); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- RigFemScalarResultFrames* RimFaultReactivationDataAccessorStressGeoMech::dataFrames( StressType stressType ) const { if ( stressType == StressType::S11 ) return m_s11Frames; else if ( stressType == StressType::S22 ) return m_s22Frames; else { CAF_ASSERT( stressType == StressType::S33 ); return m_s33Frames; } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- std::pair RimFaultReactivationDataAccessorStressGeoMech::calculatePorBar( const cvf::Vec3d& position, RimFaultReactivation::ElementSets elementSet, double gradient, RimFaultReactivation::GridPart gridPart ) const { int timeStepIndex = 0; int frameIndex = m_s33Frames->frameCount( timeStepIndex ) - 1; return calculatePorBar( position, elementSet, m_gradient, gridPart, timeStepIndex, frameIndex ); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- bool RimFaultReactivationDataAccessorStressGeoMech::isPositionValid( const cvf::Vec3d& position, const cvf::Vec3d& topPosition, const cvf::Vec3d& bottomPosition, RimFaultReactivation::GridPart gridPart ) const { RimWellIADataAccess iaDataAccess( m_geoMechCase ); int centerElementIdx = iaDataAccess.elementIndex( position ); int bottomElementIdx = iaDataAccess.elementIndex( bottomPosition ); int topElementIdx = iaDataAccess.elementIndex( topPosition ); return ( centerElementIdx != -1 && topElementIdx != -1 && bottomElementIdx != -1 ); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- double RimFaultReactivationDataAccessorStressGeoMech::interpolatedResultValue( RimWellIADataAccess& iaDataAccess, const RigFemPart* femPart, const cvf::Vec3d& position, const std::vector& scalarResults ) const { return iaDataAccess.interpolatedResultValue( femPart, scalarResults, RIG_ELEMENT_NODAL, position ); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- std::pair RimFaultReactivationDataAccessorStressGeoMech::calculatePorBar( const cvf::Vec3d& position, RimFaultReactivation::ElementSets elementSet, double gradient, RimFaultReactivation::GridPart gridPart, int timeStepIndex, int frameIndex ) const { CAF_ASSERT( m_extractors.find( gridPart ) != m_extractors.end() ); auto extractor = m_extractors.find( gridPart )->second; if ( !extractor->valid() ) { RiaLogging::error( "Invalid extractor when extracting PorBar" ); return { std::numeric_limits::infinity(), cvf::Vec3d::UNDEFINED }; } RigFemResultAddress resAddr = RigFemAddressDefines::nodalPorBarAddress(); std::vector values; extractor->curveData( resAddr, timeStepIndex, frameIndex, &values ); auto [value, extractionPos] = RimFaultReactivationDataAccessorWellLogExtraction::calculatePorBar( *m_model, gridPart, extractor->intersections(), values, position, elementSet, gradient ); if ( extractionPos.isUndefined() ) { // If extraction position is not defined the position is not close to the border between the two parts. // This means it should be safe to use POR-BAR from the model. const std::vector& frameData = m_porBarFrames->frameData( timeStepIndex, frameIndex ); // Use data from geo mech grid if defined (only position is reservoir). RimWellIADataAccess iaDataAccess( m_geoMechCase ); double gridValue = iaDataAccess.interpolatedResultValue( m_femPart, frameData, RIG_NODAL, position ); if ( !std::isinf( gridValue ) && !std::isnan( gridValue ) ) { return { gridValue, position }; } // Use calculated value when POR-BAR is inf (outside of reservoir). return { value, position }; } else { return { value, extractionPos }; } }