2017-05-16 16:18:56 +02:00
/////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2017 Statoil 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 "RigEclipseToStimPlanCellTransmissibilityCalculator.h"
# include "RigStimPlanFracTemplateCell.h"
# include "RigResultAccessorFactory.h"
# include "RigEclipseCaseData.h"
# include "RigActiveCellInfo.h"
# include "RigCellGeometryTools.h"
# include "RigMainGrid.h"
# include "RimEclipseCase.h"
# include "RimReservoirCellResultsStorage.h"
# include "cvfGeometryTools.h"
2017-05-16 16:40:37 +02:00
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RigEclipseToStimPlanCellTransmissibilityCalculator : : RigEclipseToStimPlanCellTransmissibilityCalculator ( const RimEclipseCase * caseToApply ,
cvf : : Mat4f fractureTransform ,
double skinFactor ,
2017-05-16 16:18:56 +02:00
double cDarcy ,
2017-05-16 16:40:37 +02:00
const RigStimPlanFracTemplateCell & stimPlanCell )
: m_case ( caseToApply ) ,
m_fractureTransform ( fractureTransform ) ,
m_fractureSkinFactor ( skinFactor ) ,
m_cDarcy ( cDarcy ) ,
m_stimPlanCell ( stimPlanCell )
2017-05-16 16:18:56 +02:00
{
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
const std : : vector < size_t > & RigEclipseToStimPlanCellTransmissibilityCalculator : : globalIndeciesToContributingEclipseCells ( )
{
if ( m_globalIndeciesToContributingEclipseCells . size ( ) < 1 )
{
calculateStimPlanCellsMatrixTransmissibility ( ) ;
}
return m_globalIndeciesToContributingEclipseCells ;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
const std : : vector < double > & RigEclipseToStimPlanCellTransmissibilityCalculator : : contributingEclipseCellTransmissibilities ( )
{
if ( m_globalIndeciesToContributingEclipseCells . size ( ) < 1 )
{
calculateStimPlanCellsMatrixTransmissibility ( ) ;
}
return m_contributingEclipseCellTransmissibilities ;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RigEclipseToStimPlanCellTransmissibilityCalculator : : calculateStimPlanCellsMatrixTransmissibility ( )
{
2017-05-16 16:40:37 +02:00
// Not calculating flow into fracture if stimPlan cell cond value is 0 (assumed to be outside the fracture):
2017-05-16 16:18:56 +02:00
if ( m_stimPlanCell . getConductivtyValue ( ) < 1e-7 ) return ;
2017-05-16 16:40:37 +02:00
const RigEclipseCaseData * eclipseCaseData = m_case - > eclipseCaseData ( ) ;
2017-05-16 16:18:56 +02:00
RifReaderInterface : : PorosityModelResultType porosityModel = RifReaderInterface : : MATRIX_RESULTS ;
RimReservoirCellResultsStorage * gridCellResults = m_case - > results ( porosityModel ) ;
size_t scalarSetIndex ;
scalarSetIndex = gridCellResults - > findOrLoadScalarResult ( RimDefines : : STATIC_NATIVE , " DX " ) ;
cvf : : ref < RigResultAccessor > dataAccessObjectDx = RigResultAccessorFactory : : createFromUiResultName ( eclipseCaseData , 0 , porosityModel , 0 , " DX " ) ; //assuming 0 time step and main grid (so grid index =0)
scalarSetIndex = gridCellResults - > findOrLoadScalarResult ( RimDefines : : STATIC_NATIVE , " DY " ) ;
cvf : : ref < RigResultAccessor > dataAccessObjectDy = RigResultAccessorFactory : : createFromUiResultName ( eclipseCaseData , 0 , porosityModel , 0 , " DY " ) ; //assuming 0 time step and main grid (so grid index =0)
scalarSetIndex = gridCellResults - > findOrLoadScalarResult ( RimDefines : : STATIC_NATIVE , " DZ " ) ;
cvf : : ref < RigResultAccessor > dataAccessObjectDz = RigResultAccessorFactory : : createFromUiResultName ( eclipseCaseData , 0 , porosityModel , 0 , " DZ " ) ; //assuming 0 time step and main grid (so grid index =0)
scalarSetIndex = gridCellResults - > findOrLoadScalarResult ( RimDefines : : STATIC_NATIVE , " PERMX " ) ;
cvf : : ref < RigResultAccessor > dataAccessObjectPermX = RigResultAccessorFactory : : createFromUiResultName ( eclipseCaseData , 0 , porosityModel , 0 , " PERMX " ) ; //assuming 0 time step and main grid (so grid index =0)
scalarSetIndex = gridCellResults - > findOrLoadScalarResult ( RimDefines : : STATIC_NATIVE , " PERMY " ) ;
cvf : : ref < RigResultAccessor > dataAccessObjectPermY = RigResultAccessorFactory : : createFromUiResultName ( eclipseCaseData , 0 , porosityModel , 0 , " PERMY " ) ; //assuming 0 time step and main grid (so grid index =0)
scalarSetIndex = gridCellResults - > findOrLoadScalarResult ( RimDefines : : STATIC_NATIVE , " PERMZ " ) ;
cvf : : ref < RigResultAccessor > dataAccessObjectPermZ = RigResultAccessorFactory : : createFromUiResultName ( eclipseCaseData , 0 , porosityModel , 0 , " PERMZ " ) ; //assuming 0 time step and main grid (so grid index =0)
scalarSetIndex = gridCellResults - > findOrLoadScalarResult ( RimDefines : : STATIC_NATIVE , " NTG " ) ;
cvf : : ref < RigResultAccessor > dataAccessObjectNTG = RigResultAccessorFactory : : createFromUiResultName ( eclipseCaseData , 0 , porosityModel , 0 , " NTG " ) ; //assuming 0 time step and main grid (so grid index =0)
2017-05-16 16:40:37 +02:00
const RigActiveCellInfo * activeCellInfo = eclipseCaseData - > activeCellInfo ( porosityModel ) ;
2017-05-16 16:18:56 +02:00
std : : vector < cvf : : Vec3d > stimPlanPolygonTransformed ;
for ( cvf : : Vec3d v : m_stimPlanCell . getPolygon ( ) )
{
cvf : : Vec3f stimPlanPolygonNode = static_cast < cvf : : Vec3f > ( v ) ;
stimPlanPolygonNode . transformPoint ( m_fractureTransform ) ;
stimPlanPolygonTransformed . push_back ( static_cast < cvf : : Vec3d > ( stimPlanPolygonNode ) ) ;
}
std : : vector < size_t > fracCells = getPotentiallyFracturedCellsForPolygon ( stimPlanPolygonTransformed ) ;
for ( size_t fracCell : fracCells )
{
bool cellIsActive = activeCellInfo - > isActive ( fracCell ) ;
if ( ! cellIsActive ) continue ;
double permX = dataAccessObjectPermX - > cellScalarGlobIdx ( fracCell ) ;
double permY = dataAccessObjectPermY - > cellScalarGlobIdx ( fracCell ) ;
double permZ = dataAccessObjectPermZ - > cellScalarGlobIdx ( fracCell ) ;
double dx = dataAccessObjectDx - > cellScalarGlobIdx ( fracCell ) ;
double dy = dataAccessObjectDy - > cellScalarGlobIdx ( fracCell ) ;
double dz = dataAccessObjectDz - > cellScalarGlobIdx ( fracCell ) ;
double NTG = dataAccessObjectNTG - > cellScalarGlobIdx ( fracCell ) ;
cvf : : Vec3d localX ;
cvf : : Vec3d localY ;
cvf : : Vec3d localZ ;
std : : vector < std : : vector < cvf : : Vec3d > > planeCellPolygons ;
bool isPlanIntersected = planeCellIntersectionPolygons ( fracCell , planeCellPolygons , localX , localY , localZ ) ;
if ( ! isPlanIntersected | | planeCellPolygons . size ( ) = = 0 ) continue ;
//Transform planCell polygon(s) and averageZdirection to x/y coordinate system (where fracturePolygon already is located)
cvf : : Mat4f invertedTransMatrix = m_fractureTransform . getInverted ( ) ;
for ( std : : vector < cvf : : Vec3d > & planeCellPolygon : planeCellPolygons )
{
for ( cvf : : Vec3d & v : planeCellPolygon )
{
v . transformPoint ( static_cast < cvf : : Mat4d > ( invertedTransMatrix ) ) ;
}
}
cvf : : Vec3d localZinFracPlane ;
localZinFracPlane = localZ ;
localZinFracPlane . transformVector ( static_cast < cvf : : Mat4d > ( invertedTransMatrix ) ) ;
cvf : : Vec3d directionOfLength = cvf : : Vec3d : : ZERO ;
directionOfLength . cross ( localZinFracPlane , cvf : : Vec3d ( 0 , 0 , 1 ) ) ;
directionOfLength . normalize ( ) ;
std : : vector < std : : vector < cvf : : Vec3d > > polygonsForStimPlanCellInEclipseCell ;
cvf : : Vec3d areaVector ;
std : : vector < cvf : : Vec3d > stimPlanPolygon = m_stimPlanCell . getPolygon ( ) ;
for ( std : : vector < cvf : : Vec3d > planeCellPolygon : planeCellPolygons )
{
std : : vector < std : : vector < cvf : : Vec3d > > clippedPolygons = RigCellGeometryTools : : clipPolygons ( planeCellPolygon , stimPlanPolygon ) ;
for ( std : : vector < cvf : : Vec3d > clippedPolygon : clippedPolygons )
{
polygonsForStimPlanCellInEclipseCell . push_back ( clippedPolygon ) ;
}
}
if ( polygonsForStimPlanCellInEclipseCell . size ( ) = = 0 ) continue ;
double area ;
std : : vector < double > areaOfFractureParts ;
double length ;
std : : vector < double > lengthXareaOfFractureParts ;
double Ax = 0.0 , Ay = 0.0 , Az = 0.0 ;
for ( std : : vector < cvf : : Vec3d > fracturePartPolygon : polygonsForStimPlanCellInEclipseCell )
{
areaVector = cvf : : GeometryTools : : polygonAreaNormal3D ( fracturePartPolygon ) ;
area = areaVector . length ( ) ;
areaOfFractureParts . push_back ( area ) ;
//TODO: the l in the sl/pi term in the denominator of the Tmj expression should be the length of the full Eclipse cell
//In the current form the implementation gives correct result only if s=0 (fracture templte skin factor).
length = RigCellGeometryTools : : polygonAreaWeightedLength ( directionOfLength , fracturePartPolygon ) ;
lengthXareaOfFractureParts . push_back ( length * area ) ;
cvf : : Plane fracturePlane ;
bool isCellIntersected = false ;
fracturePlane . setFromPointAndNormal ( static_cast < cvf : : Vec3d > ( m_fractureTransform . translation ( ) ) ,
static_cast < cvf : : Vec3d > ( m_fractureTransform . col ( 2 ) ) ) ;
Ax + = abs ( area * ( fracturePlane . normal ( ) . dot ( localY ) ) ) ;
Ay + = abs ( area * ( fracturePlane . normal ( ) . dot ( localX ) ) ) ;
Az + = abs ( area * ( fracturePlane . normal ( ) . dot ( localZ ) ) ) ;
}
double fractureArea = 0.0 ;
for ( double area : areaOfFractureParts ) fractureArea + = area ;
double totalAreaXLength = 0.0 ;
for ( double lengtXarea : lengthXareaOfFractureParts ) totalAreaXLength + = lengtXarea ;
double fractureAreaWeightedlength = totalAreaXLength / fractureArea ;
2017-05-18 15:14:27 +02:00
double transmissibility_X = calculateMatrixTransmissibility ( permY , NTG , Ay , dx , m_fractureSkinFactor , fractureAreaWeightedlength , m_cDarcy ) ;
double transmissibility_Y = calculateMatrixTransmissibility ( permX , NTG , Ax , dy , m_fractureSkinFactor , fractureAreaWeightedlength , m_cDarcy ) ;
double transmissibility_Z = calculateMatrixTransmissibility ( permZ , 1.0 , Az , dz , m_fractureSkinFactor , fractureAreaWeightedlength , m_cDarcy ) ;
2017-05-16 16:18:56 +02:00
double transmissibility = sqrt ( transmissibility_X * transmissibility_X
+ transmissibility_Y * transmissibility_Y
+ transmissibility_Z * transmissibility_Z ) ;
m_globalIndeciesToContributingEclipseCells . push_back ( fracCell ) ;
m_contributingEclipseCellTransmissibilities . push_back ( transmissibility ) ;
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std : : vector < size_t > RigEclipseToStimPlanCellTransmissibilityCalculator : : getPotentiallyFracturedCellsForPolygon ( std : : vector < cvf : : Vec3d > polygon )
{
2017-05-16 16:40:37 +02:00
std : : vector < size_t > cellIndices ;
2017-05-16 16:18:56 +02:00
2017-05-16 16:40:37 +02:00
const RigMainGrid * mainGrid = m_case - > eclipseCaseData ( ) - > mainGrid ( ) ;
if ( ! mainGrid ) return cellIndices ;
2017-05-16 16:18:56 +02:00
cvf : : BoundingBox polygonBBox ;
for ( cvf : : Vec3d nodeCoord : polygon ) polygonBBox . add ( nodeCoord ) ;
2017-05-16 16:40:37 +02:00
mainGrid - > findIntersectingCells ( polygonBBox , & cellIndices ) ;
2017-05-16 16:18:56 +02:00
2017-05-16 16:40:37 +02:00
return cellIndices ;
2017-05-16 16:18:56 +02:00
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
2017-05-16 16:40:37 +02:00
bool RigEclipseToStimPlanCellTransmissibilityCalculator : : planeCellIntersectionPolygons ( size_t cellindex ,
std : : vector < std : : vector < cvf : : Vec3d > > & polygons ,
cvf : : Vec3d & localX ,
cvf : : Vec3d & localY ,
cvf : : Vec3d & localZ )
2017-05-16 16:18:56 +02:00
{
cvf : : Plane fracturePlane ;
bool isCellIntersected = false ;
fracturePlane . setFromPointAndNormal ( static_cast < cvf : : Vec3d > ( m_fractureTransform . translation ( ) ) ,
static_cast < cvf : : Vec3d > ( m_fractureTransform . col ( 2 ) ) ) ;
const RigMainGrid * mainGrid = m_case - > eclipseCaseData ( ) - > mainGrid ( ) ;
if ( ! mainGrid ) return false ;
RigCell cell = mainGrid - > globalCellArray ( ) [ cellindex ] ;
if ( cell . isInvalid ( ) ) return mainGrid ;
//Copied (and adapted) from RigEclipseWellLogExtractor
cvf : : Vec3d hexCorners [ 8 ] ;
const std : : vector < cvf : : Vec3d > & nodeCoords = mainGrid - > nodes ( ) ;
const caf : : SizeTArray8 & cornerIndices = cell . cornerIndices ( ) ;
hexCorners [ 0 ] = nodeCoords [ cornerIndices [ 0 ] ] ;
hexCorners [ 1 ] = nodeCoords [ cornerIndices [ 1 ] ] ;
hexCorners [ 2 ] = nodeCoords [ cornerIndices [ 2 ] ] ;
hexCorners [ 3 ] = nodeCoords [ cornerIndices [ 3 ] ] ;
hexCorners [ 4 ] = nodeCoords [ cornerIndices [ 4 ] ] ;
hexCorners [ 5 ] = nodeCoords [ cornerIndices [ 5 ] ] ;
hexCorners [ 6 ] = nodeCoords [ cornerIndices [ 6 ] ] ;
hexCorners [ 7 ] = nodeCoords [ cornerIndices [ 7 ] ] ;
//Find line-segments where cell and fracture plane intersects
std : : list < std : : pair < cvf : : Vec3d , cvf : : Vec3d > > intersectionLineSegments ;
isCellIntersected = RigCellGeometryTools : : planeHexCellIntersection ( hexCorners , fracturePlane , intersectionLineSegments ) ;
RigCellGeometryTools : : createPolygonFromLineSegments ( intersectionLineSegments , polygons ) ;
RigCellGeometryTools : : findCellLocalXYZ ( hexCorners , localX , localY , localZ ) ;
return isCellIntersected ;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
2017-05-16 16:40:37 +02:00
double RigEclipseToStimPlanCellTransmissibilityCalculator : : calculateMatrixTransmissibility ( double perm ,
double NTG ,
double A ,
double cellSizeLength ,
double skinfactor ,
2017-05-18 15:14:27 +02:00
double fractureAreaWeightedlength ,
double cDarcy )
2017-05-16 16:18:56 +02:00
{
double transmissibility ;
double slDivPi = ( skinfactor * fractureAreaWeightedlength ) / cvf : : PI_D ;
2017-05-18 15:14:27 +02:00
transmissibility = 8 * cDarcy * ( perm * NTG ) * A / ( cellSizeLength + slDivPi ) ;
2017-05-16 16:18:56 +02:00
return transmissibility ;
}