From 086de468264f6bdd1bfe4297fabdb2b948998085 Mon Sep 17 00:00:00 2001 From: Kristian Bendiksen Date: Tue, 15 Sep 2020 18:22:33 +0200 Subject: [PATCH] #6550 Fracture Model: find distance to barrier and dip automatically. --- .../Completions/RimFractureModel.cpp | 230 +++++++++++++++++- .../Completions/RimFractureModel.h | 41 +++- 2 files changed, 251 insertions(+), 20 deletions(-) diff --git a/ApplicationCode/ProjectDataModel/Completions/RimFractureModel.cpp b/ApplicationCode/ProjectDataModel/Completions/RimFractureModel.cpp index ae042c438b..8d0009aeca 100644 --- a/ApplicationCode/ProjectDataModel/Completions/RimFractureModel.cpp +++ b/ApplicationCode/ProjectDataModel/Completions/RimFractureModel.cpp @@ -18,36 +18,34 @@ #include "RimFractureModel.h" -#include "RiaColorTables.h" #include "RiaCompletionTypeCalculationScheduler.h" #include "RiaEclipseUnitTools.h" #include "RiaFractureDefines.h" #include "RiaFractureModelDefines.h" #include "RiaLogging.h" -#include "Riu3DMainWindowTools.h" - #include "RigEclipseCaseData.h" #include "RigMainGrid.h" +#include "RigSimulationWellCoordsAndMD.h" #include "RigWellPath.h" +#include "RigWellPathIntersectionTools.h" #include "Rim3dView.h" +#include "RimAnnotationCollection.h" +#include "RimAnnotationInViewCollection.h" #include "RimColorLegend.h" #include "RimColorLegendCollection.h" #include "RimColorLegendItem.h" #include "RimEclipseCase.h" -#include "RimEclipseCellColors.h" #include "RimEclipseView.h" #include "RimElasticProperties.h" -#include "RimEllipseFractureTemplate.h" #include "RimFractureModelPlot.h" #include "RimModeledWellPath.h" #include "RimOilField.h" +#include "RimPolylineTarget.h" #include "RimProject.h" -#include "RimReservoirCellResultsStorage.h" -#include "RimStimPlanColors.h" -#include "RimStimPlanFractureTemplate.h" #include "RimTools.h" +#include "RimUserDefinedPolylinesAnnotation.h" #include "RimWellPath.h" #include "RimWellPathCollection.h" #include "RimWellPathGeometryDef.h" @@ -235,15 +233,23 @@ RimFractureModel::RimFractureModel() m_formationDip.uiCapability()->setUiReadOnly( true ); m_formationDip.uiCapability()->setUiEditorTypeName( caf::PdmUiDoubleValueEditor::uiEditorTypeName() ); + CAF_PDM_InitScriptableField( &m_autoComputeBarrier, "AutoComputeBarrier", true, "Auto Compute Barrier", "", "", "" ); CAF_PDM_InitScriptableField( &m_hasBarrier, "Barrier", true, "Barrier", "", "", "" ); CAF_PDM_InitScriptableField( &m_distanceToBarrier, "DistanceToBarrier", 0.0, "Distance To Barrier [m]", "", "", "" ); + m_distanceToBarrier.uiCapability()->setUiEditorTypeName( caf::PdmUiDoubleValueEditor::uiEditorTypeName() ); + m_distanceToBarrier.uiCapability()->setUiReadOnly( true ); + CAF_PDM_InitScriptableField( &m_barrierDip, "BarrierDip", 0.0, "Barrier Dip", "", "", "" ); + m_barrierDip.uiCapability()->setUiEditorTypeName( caf::PdmUiDoubleValueEditor::uiEditorTypeName() ); + m_barrierDip.uiCapability()->setUiReadOnly( true ); CAF_PDM_InitScriptableField( &m_wellPenetrationLayer, "WellPenetrationLayer", 0, "Well Penetration Layer", "", "", "" ); CAF_PDM_InitScriptableFieldNoDefault( &m_elasticProperties, "ElasticProperties", "Elastic Properties", "", "", "" ); m_elasticProperties.uiCapability()->setUiHidden( true ); m_elasticProperties.uiCapability()->setUiTreeHidden( true ); + CAF_PDM_InitScriptableFieldNoDefault( &m_barrierAnnotation, "BarrierAnnotation", "Barrier Annotation", "", "", "" ); + setDeletable( true ); } @@ -252,6 +258,8 @@ RimFractureModel::RimFractureModel() //-------------------------------------------------------------------------------------------------- RimFractureModel::~RimFractureModel() { + clearBarrierAnnotation(); + RimWellPath* wellPath = m_thicknessDirectionWellPath.value(); RimWellPathCollection* wellPathCollection = RimTools::wellPathCollection(); @@ -294,9 +302,25 @@ void RimFractureModel::fieldChangedByUi( const caf::PdmFieldHandle* changedField } if ( changedField == &m_MD || changedField == &m_extractionType || changedField == &m_boundingBoxVertical || - changedField == &m_boundingBoxHorizontal ) + changedField == &m_boundingBoxHorizontal || changedField == &m_fractureOrientation || + changedField == &m_autoComputeBarrier ) { updateThicknessDirection(); + + if ( m_autoComputeBarrier ) + { + updateDistanceToBarrierAndDip(); + } + else + { + clearBarrierAnnotation(); + } + } + + if ( changedField == &m_autoComputeBarrier || changedField == &m_hasBarrier ) + { + m_barrierDip.uiCapability()->setUiReadOnly( m_autoComputeBarrier || !m_hasBarrier ); + m_distanceToBarrier.uiCapability()->setUiReadOnly( m_autoComputeBarrier || !m_hasBarrier ); } if ( changedField == &m_extractionType || changedField == &m_thicknessDirectionWellPath ) @@ -552,6 +576,189 @@ cvf::Vec3d RimFractureModel::calculateTSTDirection() const return ( direction / static_cast( numContributingCells ) ).getNormalized(); } +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RimFractureModel::updateDistanceToBarrierAndDip() +{ + caf::PdmObjectHandle* objHandle = dynamic_cast( this ); + if ( !objHandle ) return; + + RimWellPath* wellPath = nullptr; + objHandle->firstAncestorOrThisOfType( wellPath ); + if ( !wellPath ) return; + + RigEclipseCaseData* eclipseCaseData = getEclipseCaseData(); + if ( !eclipseCaseData ) return; + + const cvf::Vec3d& position = anchorPosition(); + + RiaLogging::info( "Computing distance to barrier." ); + RiaLogging::info( QString( "Anchor position: %1" ).arg( RimFractureModel::vecToString( position ) ) ); + + RigWellPath* wellPathGeometry = wellPath->wellPathGeometry(); + + // Find the well path points closest to the anchor position + cvf::Vec3d p1; + cvf::Vec3d p2; + wellPathGeometry->twoClosestPoints( position, &p1, &p2 ); + RiaLogging::info( QString( "Closest points on well path: %1 %2" ) + .arg( RimFractureModel::vecToString( p1 ) ) + .arg( RimFractureModel::vecToString( p2 ) ) ); + + // Create a well direction based on the two points + cvf::Vec3d wellDirection = ( p2 - p1 ).getNormalized(); + RiaLogging::info( QString( "Well direction: %1" ).arg( RimFractureModel::vecToString( wellDirection ) ) ); + + cvf::Vec3d fractureDirection = wellDirection; + if ( m_fractureOrientation == FractureOrientation::ALONG_WELL_PATH ) + { + cvf::Mat3d azimuthRotation = cvf::Mat3d::fromRotation( cvf::Vec3d::Z_AXIS, cvf::Math::toRadians( 90.0 ) ); + fractureDirection.transformVector( azimuthRotation ); + } + + // The direction to the barrier is normal to the TST + cvf::Vec3d directionToBarrier = ( thicknessDirection() ^ fractureDirection ).getNormalized(); + RiaLogging::info( QString( "Direction to barrier: %1" ).arg( RimFractureModel::vecToString( directionToBarrier ) ) ); + + std::vector intersections = + generateBarrierIntersections( eclipseCaseData, position, directionToBarrier ); + + RiaLogging::info( QString( "Intersections: %1" ).arg( intersections.size() ) ); + + double shortestDistance = std::numeric_limits::max(); + + RigMainGrid* mainGrid = eclipseCaseData->mainGrid(); + bool foundFault = false; + cvf::Vec3d barrierPosition; + double barrierDip = 0.0; + for ( const WellPathCellIntersectionInfo& intersection : intersections ) + { + // Find the closest cell face which is a fault + double distance = position.pointDistance( intersection.startPoint ); + const RigFault* fault = mainGrid->findFaultFromCellIndexAndCellFace( intersection.globCellIndex, + intersection.intersectedCellFaceIn ); + if ( fault && distance < shortestDistance ) + { + foundFault = true; + shortestDistance = distance; + barrierPosition = intersection.startPoint; + + const RigCell& cell = mainGrid->globalCellArray()[intersection.globCellIndex]; + cvf::Vec3d faceNormal = cell.faceNormalWithAreaLength( intersection.intersectedCellFaceIn ); + barrierDip = calculateFormationDip( faceNormal ); + } + } + + if ( foundFault ) + { + RiaLogging::info( QString( "Found barrier distance: %1 Dip: %2" ).arg( shortestDistance ).arg( barrierDip ) ); + clearBarrierAnnotation(); + addBarrierAnnotation( position, barrierPosition ); + + m_hasBarrier = true; + m_barrierDip = barrierDip; + m_distanceToBarrier = shortestDistance; + } + else + { + RiaLogging::info( "No barrier found." ); + clearBarrierAnnotation(); + m_hasBarrier = false; + m_barrierDip = 0.0; + m_distanceToBarrier = 0.0; + } +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +std::vector + RimFractureModel::generateBarrierIntersections( RigEclipseCaseData* eclipseCaseData, + const cvf::Vec3d& position, + const cvf::Vec3d& directionToBarrier ) +{ + double randoDistance = 10000.0; + cvf::Vec3d forwardPosition = position + ( directionToBarrier * randoDistance ); + cvf::Vec3d backwardPosition = position + ( directionToBarrier * -randoDistance ); + std::vector intersections = + generateBarrierIntersectionsBetweenPoints( eclipseCaseData, position, forwardPosition ); + std::vector backwardIntersections = + generateBarrierIntersectionsBetweenPoints( eclipseCaseData, position, backwardPosition ); + + // Merge the intersections for the search for closest + intersections.insert( intersections.end(), backwardIntersections.begin(), backwardIntersections.end() ); + return intersections; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +std::vector + RimFractureModel::generateBarrierIntersectionsBetweenPoints( RigEclipseCaseData* eclipseCaseData, + const cvf::Vec3d& startPosition, + const cvf::Vec3d& endPosition ) +{ + // Create a fake well path from the anchor point to + // a point far away in the direction barrier direction + std::vector pathCoords; + pathCoords.push_back( startPosition ); + pathCoords.push_back( endPosition ); + + RigSimulationWellCoordsAndMD helper( pathCoords ); + return RigWellPathIntersectionTools::findCellIntersectionInfosAlongPath( eclipseCaseData, + helper.wellPathPoints(), + helper.measuredDepths() ); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RimFractureModel::clearBarrierAnnotation() +{ + auto existingAnnotation = m_barrierAnnotation.value(); + if ( existingAnnotation ) + { + delete existingAnnotation; + m_barrierAnnotation = nullptr; + } +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RimFractureModel::addBarrierAnnotation( const cvf::Vec3d& startPosition, const cvf::Vec3d& endPosition ) +{ + RimAnnotationCollection* coll = annotationCollection(); + if ( !coll ) return; + + auto newAnnotation = new RimUserDefinedPolylinesAnnotation(); + + RimPolylineTarget* startTarget = new RimPolylineTarget(); + startTarget->setAsPointXYZ( startPosition ); + newAnnotation->insertTarget( nullptr, startTarget ); + + RimPolylineTarget* endTarget = new RimPolylineTarget(); + endTarget->setAsPointXYZ( endPosition ); + newAnnotation->insertTarget( nullptr, endTarget ); + + m_barrierAnnotation = newAnnotation; + + coll->addAnnotation( newAnnotation ); + coll->scheduleRedrawOfRelevantViews(); + coll->updateConnectedEditors(); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +RimAnnotationCollection* RimFractureModel::annotationCollection() +{ + const auto project = RimProject::current(); + auto oilField = project->activeOilField(); + return oilField ? oilField->annotationCollection() : nullptr; +} + //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- @@ -559,6 +766,7 @@ void RimFractureModel::defineUiOrdering( QString uiConfigName, caf::PdmUiOrderin { m_thicknessDirectionWellPath.uiCapability()->setUiHidden( true ); m_elasticProperties.uiCapability()->setUiHidden( false ); + m_barrierAnnotation.uiCapability()->setUiHidden( true ); uiOrdering.add( nameField() ); uiOrdering.add( &m_MD ); @@ -613,6 +821,7 @@ void RimFractureModel::defineUiOrdering( QString uiConfigName, caf::PdmUiOrderin caf::PdmUiOrdering* asymmetricGroup = uiOrdering.addNewGroup( "Asymmetric" ); asymmetricGroup->add( &m_formationDip ); asymmetricGroup->add( &m_hasBarrier ); + asymmetricGroup->add( &m_autoComputeBarrier ); asymmetricGroup->add( &m_distanceToBarrier ); asymmetricGroup->add( &m_barrierDip ); asymmetricGroup->add( &m_wellPenetrationLayer ); @@ -625,7 +834,8 @@ void RimFractureModel::defineEditorAttribute( const caf::PdmFieldHandle* field, QString uiConfigName, caf::PdmUiEditorAttribute* attribute ) { - if ( field == &m_stressDepth || field == &m_verticalStress || field == &m_formationDip ) + if ( field == &m_stressDepth || field == &m_verticalStress || field == &m_formationDip || field == &m_barrierDip || + field == &m_distanceToBarrier ) { auto doubleAttr = dynamic_cast( attribute ); if ( doubleAttr ) diff --git a/ApplicationCode/ProjectDataModel/Completions/RimFractureModel.h b/ApplicationCode/ProjectDataModel/Completions/RimFractureModel.h index 6ec79485cd..8f068cb0f6 100644 --- a/ApplicationCode/ProjectDataModel/Completions/RimFractureModel.h +++ b/ApplicationCode/ProjectDataModel/Completions/RimFractureModel.h @@ -24,6 +24,8 @@ #include "RimCheckableNamedObject.h" #include "RimWellPathComponentInterface.h" +#include "RigWellLogExtractor.h" + #include "cafPdmChildField.h" #include "cafPdmFieldCvfVec3d.h" #include "cafPdmProxyValueField.h" @@ -34,6 +36,8 @@ class RimWellPath; class RimModeledWellPath; class RimElasticProperties; class RigEclipseCaseData; +class RimAnnotationCollection; +class RimUserDefinedPolylinesAnnotation; //================================================================================================== /// @@ -143,11 +147,13 @@ protected: caf::PdmUiEditorAttribute* attribute ) override; private: - void updatePositionFromMeasuredDepth(); - void updateThicknessDirection(); - cvf::Vec3d calculateTSTDirection() const; - void findThicknessTargetPoints( cvf::Vec3d& topPosition, cvf::Vec3d& bottomPosition ); - static double calculateFormationDip( const cvf::Vec3d& direction ); + void updatePositionFromMeasuredDepth(); + void updateThicknessDirection(); + void updateDistanceToBarrierAndDip(); + cvf::Vec3d calculateTSTDirection() const; + void findThicknessTargetPoints( cvf::Vec3d& topPosition, cvf::Vec3d& bottomPosition ); + static double calculateFormationDip( const cvf::Vec3d& direction ); + static QString vecToString( const cvf::Vec3d& vec ); void updateThicknessDirectionWellPathName(); static double computeDefaultStressDepth(); @@ -155,6 +161,19 @@ private: static RigEclipseCaseData* getEclipseCaseData(); static RimEclipseCase* getEclipseCase(); + void addBarrierAnnotation( const cvf::Vec3d& startPosition, const cvf::Vec3d& endPosition ); + void clearBarrierAnnotation(); + RimAnnotationCollection* annotationCollection(); + + static std::vector generateBarrierIntersections( RigEclipseCaseData* eclipseCaseData, + const cvf::Vec3d& position, + const cvf::Vec3d& directionToBarrier ); + + static std::vector + generateBarrierIntersectionsBetweenPoints( RigEclipseCaseData* eclipseCaseData, + const cvf::Vec3d& startPosition, + const cvf::Vec3d& endPosition ); + protected: caf::PdmField m_MD; caf::PdmField> m_extractionType; @@ -192,9 +211,11 @@ protected: caf::PdmField> m_fractureOrientation; caf::PdmField m_perforationLength; - caf::PdmField m_formationDip; - caf::PdmField m_hasBarrier; - caf::PdmField m_distanceToBarrier; - caf::PdmField m_barrierDip; - caf::PdmField m_wellPenetrationLayer; + caf::PdmField m_formationDip; + caf::PdmField m_autoComputeBarrier; + caf::PdmField m_hasBarrier; + caf::PdmField m_distanceToBarrier; + caf::PdmField m_barrierDip; + caf::PdmField m_wellPenetrationLayer; + caf::PdmPtrField m_barrierAnnotation; };