2017-05-16 09:18:56 -05: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 "RigActiveCellInfo.h"
# include "RigCellGeometryTools.h"
2017-06-14 00:25:34 -05:00
# include "RigEclipseCaseData.h"
# include "RigFractureCell.h"
# include "RigFractureTransmissibilityEquations.h"
2017-05-16 09:18:56 -05:00
# include "RigMainGrid.h"
2017-06-14 00:25:34 -05:00
# include "RigResultAccessorFactory.h"
2017-06-16 02:44:28 -05:00
# include "RigHexIntersectionTools.h"
2017-05-16 09:18:56 -05:00
# include "RimEclipseCase.h"
# include "RimReservoirCellResultsStorage.h"
# include "cvfGeometryTools.h"
2017-05-16 09:40:37 -05:00
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
2017-06-22 03:42:07 -05:00
RigEclipseToStimPlanCellTransmissibilityCalculator : : RigEclipseToStimPlanCellTransmissibilityCalculator ( RimEclipseCase * caseToApply ,
2017-06-23 03:16:39 -05:00
cvf : : Mat4d fractureTransform ,
2017-05-16 09:40:37 -05:00
double skinFactor ,
2017-05-16 09:18:56 -05:00
double cDarcy ,
2017-05-30 04:52:19 -05:00
const RigFractureCell & stimPlanCell )
2017-05-16 09:40:37 -05:00
: m_case ( caseToApply ) ,
m_fractureTransform ( fractureTransform ) ,
m_fractureSkinFactor ( skinFactor ) ,
m_cDarcy ( cDarcy ) ,
m_stimPlanCell ( stimPlanCell )
2017-05-16 09:18:56 -05: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 09:40:37 -05:00
// Not calculating flow into fracture if stimPlan cell cond value is 0 (assumed to be outside the fracture):
2017-05-16 09:18:56 -05:00
if ( m_stimPlanCell . getConductivtyValue ( ) < 1e-7 ) return ;
2017-05-16 09:40:37 -05:00
const RigEclipseCaseData * eclipseCaseData = m_case - > eclipseCaseData ( ) ;
2017-05-16 09:18:56 -05:00
2017-08-11 09:14:00 -05:00
RiaDefines : : PorosityModelType porosityModel = RiaDefines : : MATRIX_MODEL ;
2017-05-16 09:18:56 -05:00
RimReservoirCellResultsStorage * gridCellResults = m_case - > results ( porosityModel ) ;
size_t scalarSetIndex ;
2017-06-14 00:25:34 -05:00
scalarSetIndex = gridCellResults - > findOrLoadScalarResult ( RiaDefines : : STATIC_NATIVE , " DX " ) ;
2017-05-16 09:18:56 -05:00
cvf : : ref < RigResultAccessor > dataAccessObjectDx = RigResultAccessorFactory : : createFromUiResultName ( eclipseCaseData , 0 , porosityModel , 0 , " DX " ) ; //assuming 0 time step and main grid (so grid index =0)
2017-06-14 00:25:34 -05:00
scalarSetIndex = gridCellResults - > findOrLoadScalarResult ( RiaDefines : : STATIC_NATIVE , " DY " ) ;
2017-05-16 09:18:56 -05:00
cvf : : ref < RigResultAccessor > dataAccessObjectDy = RigResultAccessorFactory : : createFromUiResultName ( eclipseCaseData , 0 , porosityModel , 0 , " DY " ) ; //assuming 0 time step and main grid (so grid index =0)
2017-06-14 00:25:34 -05:00
scalarSetIndex = gridCellResults - > findOrLoadScalarResult ( RiaDefines : : STATIC_NATIVE , " DZ " ) ;
2017-05-16 09:18:56 -05:00
cvf : : ref < RigResultAccessor > dataAccessObjectDz = RigResultAccessorFactory : : createFromUiResultName ( eclipseCaseData , 0 , porosityModel , 0 , " DZ " ) ; //assuming 0 time step and main grid (so grid index =0)
2017-06-14 00:25:34 -05:00
scalarSetIndex = gridCellResults - > findOrLoadScalarResult ( RiaDefines : : STATIC_NATIVE , " PERMX " ) ;
2017-05-16 09:18:56 -05:00
cvf : : ref < RigResultAccessor > dataAccessObjectPermX = RigResultAccessorFactory : : createFromUiResultName ( eclipseCaseData , 0 , porosityModel , 0 , " PERMX " ) ; //assuming 0 time step and main grid (so grid index =0)
2017-06-14 00:25:34 -05:00
scalarSetIndex = gridCellResults - > findOrLoadScalarResult ( RiaDefines : : STATIC_NATIVE , " PERMY " ) ;
2017-05-16 09:18:56 -05:00
cvf : : ref < RigResultAccessor > dataAccessObjectPermY = RigResultAccessorFactory : : createFromUiResultName ( eclipseCaseData , 0 , porosityModel , 0 , " PERMY " ) ; //assuming 0 time step and main grid (so grid index =0)
2017-06-14 00:25:34 -05:00
scalarSetIndex = gridCellResults - > findOrLoadScalarResult ( RiaDefines : : STATIC_NATIVE , " PERMZ " ) ;
2017-05-16 09:18:56 -05:00
cvf : : ref < RigResultAccessor > dataAccessObjectPermZ = RigResultAccessorFactory : : createFromUiResultName ( eclipseCaseData , 0 , porosityModel , 0 , " PERMZ " ) ; //assuming 0 time step and main grid (so grid index =0)
2017-06-14 00:25:34 -05:00
scalarSetIndex = gridCellResults - > findOrLoadScalarResult ( RiaDefines : : STATIC_NATIVE , " NTG " ) ;
2017-05-16 09:18:56 -05:00
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 09:40:37 -05:00
const RigActiveCellInfo * activeCellInfo = eclipseCaseData - > activeCellInfo ( porosityModel ) ;
2017-05-16 09:18:56 -05:00
std : : vector < cvf : : Vec3d > stimPlanPolygonTransformed ;
for ( cvf : : Vec3d v : m_stimPlanCell . getPolygon ( ) )
{
2017-06-23 03:16:39 -05:00
v . transformPoint ( m_fractureTransform ) ;
stimPlanPolygonTransformed . push_back ( v ) ;
2017-05-16 09:18:56 -05:00
}
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 ) ;
2017-05-19 03:56:06 -05:00
const RigMainGrid * mainGrid = m_case - > eclipseCaseData ( ) - > mainGrid ( ) ;
2017-06-08 07:09:03 -05:00
std : : array < cvf : : Vec3d , 8 > hexCorners ;
mainGrid - > cellCornerVertices ( fracCell , hexCorners . data ( ) ) ;
2017-05-19 03:56:06 -05:00
std : : vector < std : : vector < cvf : : Vec3d > > planeCellPolygons ;
2017-06-16 02:44:28 -05:00
bool isPlanIntersected = RigHexIntersectionTools : : planeHexIntersectionPolygons ( hexCorners , m_fractureTransform , planeCellPolygons ) ;
2017-05-19 03:56:06 -05:00
if ( ! isPlanIntersected | | planeCellPolygons . size ( ) = = 0 ) continue ;
2017-05-16 09:18:56 -05:00
cvf : : Vec3d localX ;
cvf : : Vec3d localY ;
cvf : : Vec3d localZ ;
2017-05-19 03:56:06 -05:00
RigCellGeometryTools : : findCellLocalXYZ ( hexCorners , localX , localY , localZ ) ;
2017-05-16 09:18:56 -05:00
//Transform planCell polygon(s) and averageZdirection to x/y coordinate system (where fracturePolygon already is located)
2017-06-23 03:16:39 -05:00
cvf : : Mat4d invertedTransMatrix = m_fractureTransform . getInverted ( ) ;
2017-05-16 09:18:56 -05:00
for ( std : : vector < cvf : : Vec3d > & planeCellPolygon : planeCellPolygons )
{
for ( cvf : : Vec3d & v : planeCellPolygon )
{
2017-06-23 03:16:39 -05:00
v . transformPoint ( invertedTransMatrix ) ;
2017-05-16 09:18:56 -05:00
}
}
cvf : : Vec3d localZinFracPlane ;
localZinFracPlane = localZ ;
2017-06-23 03:16:39 -05:00
localZinFracPlane . transformVector ( invertedTransMatrix ) ;
2017-05-16 09:18:56 -05:00
cvf : : Vec3d directionOfLength = cvf : : Vec3d : : ZERO ;
directionOfLength . cross ( localZinFracPlane , cvf : : Vec3d ( 0 , 0 , 1 ) ) ;
directionOfLength . normalize ( ) ;
2017-07-04 04:34:21 -05:00
//Fracture plane is XY - so localZinFracPlane is in this plane.
//Crossing this vector with a vector normal to this plane (0,0,1) gives a vector for in the ij-direction in the frac plane
//This is the direction in which we calculate the length of the fracture element in the cell,
//to use in the skinfactor contribution (S*l/pi) to the transmissibility.
2017-05-16 09:18:56 -05:00
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 )
{
2017-05-22 08:33:53 -05:00
std : : vector < std : : vector < cvf : : Vec3d > > clippedPolygons = RigCellGeometryTools : : intersectPolygons ( planeCellPolygon , stimPlanPolygon ) ;
2017-05-16 09:18:56 -05:00
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 ) ;
2017-07-04 04:34:21 -05:00
//TODO: should the l in the sl/pi term in the denominator of the Tmj expression be the length of the full Eclipse cell or fracture?
2017-05-16 09:18:56 -05:00
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-31 02:16:49 -05:00
double transmissibility_X = RigFractureTransmissibilityEquations : : matrixToFractureTrans ( permY , NTG , Ay , dx , m_fractureSkinFactor , fractureAreaWeightedlength , m_cDarcy ) ;
double transmissibility_Y = RigFractureTransmissibilityEquations : : matrixToFractureTrans ( permX , NTG , Ax , dy , m_fractureSkinFactor , fractureAreaWeightedlength , m_cDarcy ) ;
double transmissibility_Z = RigFractureTransmissibilityEquations : : matrixToFractureTrans ( permZ , 1.0 , Az , dz , m_fractureSkinFactor , fractureAreaWeightedlength , m_cDarcy ) ;
2017-05-16 09:18:56 -05: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 09:40:37 -05:00
std : : vector < size_t > cellIndices ;
2017-05-16 09:18:56 -05:00
2017-05-16 09:40:37 -05:00
const RigMainGrid * mainGrid = m_case - > eclipseCaseData ( ) - > mainGrid ( ) ;
if ( ! mainGrid ) return cellIndices ;
2017-05-16 09:18:56 -05:00
cvf : : BoundingBox polygonBBox ;
for ( cvf : : Vec3d nodeCoord : polygon ) polygonBBox . add ( nodeCoord ) ;
2017-05-16 09:40:37 -05:00
mainGrid - > findIntersectingCells ( polygonBBox , & cellIndices ) ;
2017-05-16 09:18:56 -05:00
2017-05-16 09:40:37 -05:00
return cellIndices ;
2017-05-16 09:18:56 -05:00
}