mirror of
https://github.com/OPM/ResInsight.git
synced 2025-01-01 03:37:15 -06:00
952e766c2f
* Update to clang-format-15 Removed two custom .clang-format files in subfolders of AppFwk * Fixes by clang-format
617 lines
26 KiB
C++
617 lines
26 KiB
C++
/////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Copyright (C) 2022 - 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 "RigThermalFractureResultUtil.h"
|
|
|
|
#include "RiaEclipseUnitTools.h"
|
|
#include "RiaLogging.h"
|
|
|
|
#include "RiaWeightedMeanCalculator.h"
|
|
#include "RigCellGeometryTools.h"
|
|
#include "RigConvexHull.h"
|
|
#include "RigFractureGrid.h"
|
|
#include "RigStatisticsMath.h"
|
|
#include "RigThermalFractureDefinition.h"
|
|
|
|
#include "cafAssert.h"
|
|
#include "cvfBoundingBox.h"
|
|
#include "cvfGeometryTools.h"
|
|
|
|
#include <cmath>
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
RigThermalFractureResultUtil::RigThermalFractureResultUtil()
|
|
{
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
RigThermalFractureResultUtil::~RigThermalFractureResultUtil()
|
|
{
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
std::vector<std::vector<double>>
|
|
RigThermalFractureResultUtil::getDataAtTimeIndex( std::shared_ptr<const RigThermalFractureDefinition> fractureDefinition,
|
|
const QString& resultName,
|
|
const QString& unitName,
|
|
size_t timeStepIndex )
|
|
{
|
|
std::vector<std::vector<double>> vec;
|
|
|
|
int propertyIndex = fractureDefinition->getPropertyIndex( resultName );
|
|
if ( propertyIndex < 0 ) return vec;
|
|
|
|
std::vector<cvf::Vec3d> relativePos = getRelativeCoordinates( fractureDefinition, timeStepIndex );
|
|
|
|
// Create bounding box
|
|
cvf::BoundingBox boundingBox;
|
|
for ( auto p : relativePos )
|
|
boundingBox.add( p );
|
|
|
|
boundingBox.expand( 1.0 );
|
|
|
|
// Generate a uniform mesh (center points)
|
|
auto [xCoords, depthCoords] = generateUniformMesh( boundingBox, NUM_SAMPLES_X, NUM_SAMPLES_Y );
|
|
|
|
// Fill with invalid value
|
|
for ( int i = 0; i < NUM_SAMPLES_Y; i++ )
|
|
{
|
|
std::vector<double> junk( NUM_SAMPLES_X, std::numeric_limits<double>::infinity() );
|
|
vec.push_back( junk );
|
|
}
|
|
|
|
// Find the boundary of the fracture (i.e. convex hull around the points)
|
|
std::vector<cvf::Vec3d> fractureBoundary = RigConvexHull::compute2d( relativePos );
|
|
fractureBoundary.push_back( fractureBoundary.front() );
|
|
|
|
for ( int i = 0; i < static_cast<int>( xCoords.size() ) - 1; i++ )
|
|
{
|
|
for ( int j = 0; j < static_cast<int>( depthCoords.size() ) - 1; j++ )
|
|
{
|
|
cvf::BoundingBox bb;
|
|
bb.add( cvf::Vec3d( xCoords[i], depthCoords[j], 0.0 ) );
|
|
bb.add( cvf::Vec3d( xCoords[i + 1], depthCoords[j], 0.0 ) );
|
|
bb.add( cvf::Vec3d( xCoords[i + 1], depthCoords[j + 1], 0.0 ) );
|
|
bb.add( cvf::Vec3d( xCoords[i], depthCoords[j + 1], 0.0 ) );
|
|
|
|
if ( RigCellGeometryTools::pointInsidePolygon2D( bb.center(), fractureBoundary ) )
|
|
{
|
|
double value = interpolateProperty( bb.center(), relativePos, fractureDefinition, propertyIndex, timeStepIndex );
|
|
vec[j][i] = value;
|
|
}
|
|
}
|
|
}
|
|
|
|
return vec;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RigThermalFractureResultUtil::createFractureTriangleGeometry( std::shared_ptr<const RigThermalFractureDefinition> fractureDefinition,
|
|
int activeTimeStepIndex,
|
|
double xScaleFactor,
|
|
double yScaleFactor,
|
|
double wellPathIntersectionAtFractureDepth,
|
|
std::vector<cvf::Vec3f>* vertices,
|
|
std::vector<cvf::uint>* triangleIndices )
|
|
{
|
|
// Convert to coordinates relative to center node
|
|
std::vector<cvf::Vec3d> points = getRelativeCoordinates( fractureDefinition, activeTimeStepIndex );
|
|
|
|
// Create bounding box
|
|
cvf::BoundingBox boundingBox;
|
|
for ( auto p : points )
|
|
boundingBox.add( p );
|
|
|
|
boundingBox.expand( 1.0 );
|
|
|
|
// Generate a uniform mesh
|
|
auto [xCoords, depthCoords] = generateUniformMesh( boundingBox, NUM_SAMPLES_X, NUM_SAMPLES_Y );
|
|
|
|
// Code adapted from RigStimPlanFractureDefinition
|
|
std::vector<double> adjustedYs = depthCoords;
|
|
cvf::uint lenXcoords = static_cast<cvf::uint>( xCoords.size() );
|
|
|
|
for ( cvf::uint k = 0; k < adjustedYs.size(); k++ )
|
|
{
|
|
for ( cvf::uint i = 0; i < lenXcoords; i++ )
|
|
{
|
|
cvf::Vec3f node = cvf::Vec3f( xCoords[i], adjustedYs[k], 0 );
|
|
vertices->push_back( node );
|
|
|
|
if ( i < lenXcoords - 1 && k < adjustedYs.size() - 1 )
|
|
{
|
|
double THRESHOLD_VALUE = 1e-5;
|
|
|
|
if ( xCoords[i] < THRESHOLD_VALUE )
|
|
{
|
|
// Upper triangle
|
|
triangleIndices->push_back( i + k * lenXcoords );
|
|
triangleIndices->push_back( ( i + 1 ) + k * lenXcoords );
|
|
triangleIndices->push_back( ( i + 1 ) + ( k + 1 ) * lenXcoords );
|
|
// Lower triangle
|
|
triangleIndices->push_back( i + k * lenXcoords );
|
|
triangleIndices->push_back( ( i + 1 ) + ( k + 1 ) * lenXcoords );
|
|
triangleIndices->push_back( ( i ) + ( k + 1 ) * lenXcoords );
|
|
}
|
|
else
|
|
{
|
|
// Upper triangle
|
|
triangleIndices->push_back( i + k * lenXcoords );
|
|
triangleIndices->push_back( ( i + 1 ) + k * lenXcoords );
|
|
triangleIndices->push_back( ( i ) + ( k + 1 ) * lenXcoords );
|
|
// Lower triangle
|
|
triangleIndices->push_back( ( i + 1 ) + k * lenXcoords );
|
|
triangleIndices->push_back( ( i + 1 ) + ( k + 1 ) * lenXcoords );
|
|
triangleIndices->push_back( ( i ) + ( k + 1 ) * lenXcoords );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
std::vector<double> RigThermalFractureResultUtil::fractureGridResults( std::shared_ptr<const RigThermalFractureDefinition> fractureDefinition,
|
|
const QString& resultName,
|
|
const QString& unitName,
|
|
size_t timeStepIndex )
|
|
{
|
|
std::vector<double> fractureGridResults;
|
|
const std::vector<std::vector<double>>& resultValuesAtTimeStep =
|
|
getDataAtTimeIndex( fractureDefinition, resultName, unitName, timeStepIndex );
|
|
|
|
for ( int i = 0; i < static_cast<int>( NUM_SAMPLES_X ) - 2; i++ )
|
|
{
|
|
for ( int j = 0; j < static_cast<int>( NUM_SAMPLES_Y ) - 2; j++ )
|
|
{
|
|
if ( j + 1 < static_cast<int>( resultValuesAtTimeStep.size() ) && i + 1 < static_cast<int>( resultValuesAtTimeStep[j + 1].size() ) )
|
|
{
|
|
fractureGridResults.push_back( resultValuesAtTimeStep[j + 1][i + 1] );
|
|
}
|
|
else
|
|
{
|
|
fractureGridResults.push_back( HUGE_VAL );
|
|
}
|
|
}
|
|
}
|
|
|
|
return fractureGridResults;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
cvf::cref<RigFractureGrid>
|
|
RigThermalFractureResultUtil::createFractureGrid( std::shared_ptr<const RigThermalFractureDefinition> fractureDefinition,
|
|
const QString& resultName,
|
|
int activeTimeStepIndex,
|
|
double xScaleFactor,
|
|
double yScaleFactor,
|
|
double wellPathIntersectionAtFractureDepth,
|
|
RiaDefines::EclipseUnitSystem requiredUnitSet )
|
|
{
|
|
// Convert to coordinates relative to center node
|
|
std::vector<cvf::Vec3d> points = getRelativeCoordinates( fractureDefinition, activeTimeStepIndex );
|
|
|
|
QString conductivityUnitTextOnFile;
|
|
|
|
std::vector<std::pair<QString, QString>> propertyNamesUnitsOnFile = fractureDefinition->getPropertyNamesUnits();
|
|
for ( auto properyNameUnit : propertyNamesUnitsOnFile )
|
|
{
|
|
if ( resultName == properyNameUnit.first )
|
|
{
|
|
conductivityUnitTextOnFile = properyNameUnit.second;
|
|
}
|
|
}
|
|
|
|
CAF_ASSERT( !conductivityUnitTextOnFile.isEmpty() );
|
|
if ( conductivityUnitTextOnFile.isEmpty() )
|
|
{
|
|
RiaLogging::error( "Did not find unit for conductivity on file" );
|
|
return nullptr;
|
|
}
|
|
|
|
std::vector<std::vector<double>> conductivityValues =
|
|
getDataAtTimeIndex( fractureDefinition, resultName, conductivityUnitTextOnFile, activeTimeStepIndex );
|
|
if ( conductivityValues.empty() )
|
|
{
|
|
RiaLogging::error( QString( "No conductivity values found for result: %1" ).arg( resultName ) );
|
|
return nullptr;
|
|
}
|
|
|
|
// Check that the data is in the required unit system.
|
|
// Convert if not the case.
|
|
if ( requiredUnitSet != fractureDefinition->unitSystem() )
|
|
{
|
|
// Convert to the conductivity unit system used by the fracture template
|
|
// The conductivity value is used in the computations of transmissibility when exporting COMPDAT, and has unit
|
|
// md-m or md-ft This unit must match the unit used to represent coordinates of the grid used for export
|
|
|
|
for ( auto& yValues : conductivityValues )
|
|
{
|
|
for ( auto& xVal : yValues )
|
|
{
|
|
if ( requiredUnitSet == RiaDefines::EclipseUnitSystem::UNITS_FIELD )
|
|
{
|
|
xVal = RiaEclipseUnitTools::convertToFeet( xVal, conductivityUnitTextOnFile, false );
|
|
}
|
|
else if ( requiredUnitSet == RiaDefines::EclipseUnitSystem::UNITS_METRIC )
|
|
{
|
|
xVal = RiaEclipseUnitTools::convertToMeter( xVal, conductivityUnitTextOnFile, false );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Create bounding box
|
|
cvf::BoundingBox boundingBox;
|
|
for ( auto p : points )
|
|
boundingBox.add( p );
|
|
|
|
boundingBox.expand( 1.0 );
|
|
|
|
// Generate a uniform mesh
|
|
auto [Xs, Ys] = generateUniformMesh( boundingBox, NUM_SAMPLES_X, NUM_SAMPLES_Y );
|
|
|
|
double centerZ = fractureDefinition->centerPosition().z();
|
|
double offset = wellPathIntersectionAtFractureDepth - centerZ;
|
|
|
|
std::vector<double> adjustedYs = adjustedYCoordsAroundWellPathPosition( Ys, offset );
|
|
|
|
std::vector<double> scaledXs = scaleVector( Xs, xScaleFactor );
|
|
std::vector<double> scaledYs = scaleVector( adjustedYs, yScaleFactor );
|
|
|
|
std::vector<double> xCoordsAtNodes = scaledXs;
|
|
std::vector<double> yCoordsAtNodes = scaledYs;
|
|
|
|
// Find center points
|
|
std::vector<double> xCoords;
|
|
for ( int i = 0; i < static_cast<int>( xCoordsAtNodes.size() ) - 1; i++ )
|
|
xCoords.push_back( ( xCoordsAtNodes[i] + xCoordsAtNodes[i + 1] ) / 2 );
|
|
std::vector<double> depthCoords;
|
|
for ( int i = 0; i < static_cast<int>( yCoordsAtNodes.size() ) - 1; i++ )
|
|
depthCoords.push_back( ( yCoordsAtNodes[i] + yCoordsAtNodes[i + 1] ) / 2 );
|
|
|
|
std::pair<size_t, size_t> wellCenterStimPlanCellIJ = std::make_pair( 0, 0 );
|
|
std::vector<RigFractureCell> stimPlanCells;
|
|
for ( int i = 0; i < static_cast<int>( xCoords.size() ) - 1; i++ )
|
|
{
|
|
for ( int j = 0; j < static_cast<int>( depthCoords.size() ) - 1; j++ )
|
|
{
|
|
std::vector<cvf::Vec3d> cellPolygon;
|
|
cellPolygon.push_back( cvf::Vec3d( xCoords[i], depthCoords[j + 1], 0.0 ) );
|
|
cellPolygon.push_back( cvf::Vec3d( xCoords[i + 1], depthCoords[j + 1], 0.0 ) );
|
|
cellPolygon.push_back( cvf::Vec3d( xCoords[i + 1], depthCoords[j], 0.0 ) );
|
|
cellPolygon.push_back( cvf::Vec3d( xCoords[i], depthCoords[j], 0.0 ) );
|
|
|
|
RigFractureCell stimPlanCell( cellPolygon, i, j );
|
|
if ( !conductivityValues.empty() ) // Assuming vector to be of correct length, or no values
|
|
{
|
|
stimPlanCell.setConductivityValue( conductivityValues[j + 1][i + 1] );
|
|
}
|
|
else
|
|
{
|
|
stimPlanCell.setConductivityValue( cvf::UNDEFINED_DOUBLE );
|
|
}
|
|
|
|
// The well path is intersecting the fracture at origo in the fracture coordinate system
|
|
// Find the Stimplan cell where the well path is intersecting
|
|
|
|
if ( cellPolygon[0].x() <= 0.0 && cellPolygon[1].x() >= 0.0 )
|
|
{
|
|
if ( cellPolygon[1].y() >= 0.0 && cellPolygon[2].y() <= 0.0 )
|
|
{
|
|
wellCenterStimPlanCellIJ = std::make_pair( stimPlanCell.getI(), stimPlanCell.getJ() );
|
|
}
|
|
}
|
|
|
|
stimPlanCells.push_back( stimPlanCell );
|
|
}
|
|
}
|
|
|
|
cvf::ref<RigFractureGrid> fractureGrid = new RigFractureGrid;
|
|
fractureGrid->setFractureCells( stimPlanCells );
|
|
fractureGrid->setWellCenterFractureCellIJ( wellCenterStimPlanCellIJ );
|
|
fractureGrid->setICellCount( NUM_SAMPLES_X - 2 );
|
|
fractureGrid->setJCellCount( NUM_SAMPLES_Y - 2 );
|
|
fractureGrid->ensureCellSearchTreeIsBuilt();
|
|
|
|
return cvf::cref<RigFractureGrid>( fractureGrid.p() );
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
/// TODO: adapted from RimEnsembleFractureStatistics. Maybe extract?
|
|
//--------------------------------------------------------------------------------------------------
|
|
std::pair<std::vector<double>, std::vector<double>>
|
|
RigThermalFractureResultUtil::generateUniformMesh( const cvf::BoundingBox& bb, int NUM_SAMPLES_X, int NUM_SAMPLES_Y )
|
|
{
|
|
CAF_ASSERT( NUM_SAMPLES_X > 0 );
|
|
CAF_ASSERT( NUM_SAMPLES_Y > 0 );
|
|
|
|
double minX = bb.min().x();
|
|
double maxX = bb.max().x();
|
|
|
|
double minY = bb.min().y();
|
|
double maxY = bb.max().y();
|
|
|
|
std::vector<double> gridXs;
|
|
linearSampling( minX, maxX, NUM_SAMPLES_X, gridXs );
|
|
|
|
std::vector<double> gridYs;
|
|
linearSampling( minY, maxY, NUM_SAMPLES_Y, gridYs );
|
|
|
|
return std::make_pair( gridXs, gridYs );
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
/// TODO: duplicated from RimEnsembleFractureStatistics. Extract to util.
|
|
//--------------------------------------------------------------------------------------------------
|
|
double RigThermalFractureResultUtil::linearSampling( double minValue, double maxValue, int numSamples, std::vector<double>& samples )
|
|
{
|
|
CAF_ASSERT( numSamples > 0 );
|
|
double sampleDistance = ( maxValue - minValue ) / numSamples;
|
|
|
|
for ( int s = 0; s < numSamples; s++ )
|
|
{
|
|
double pos = minValue + s * sampleDistance + sampleDistance * 0.5;
|
|
samples.push_back( pos );
|
|
}
|
|
|
|
return sampleDistance;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
std::vector<double> RigThermalFractureResultUtil::scaleVector( const std::vector<double>& xs, double scaleFactor )
|
|
{
|
|
std::vector<double> scaledXs;
|
|
|
|
// Scale using 0 as scaling anchor
|
|
for ( double x : xs )
|
|
{
|
|
if ( scaleFactor != 1.0 ) x *= scaleFactor;
|
|
scaledXs.push_back( x );
|
|
}
|
|
|
|
return scaledXs;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
std::vector<double> RigThermalFractureResultUtil::adjustedYCoordsAroundWellPathPosition( const std::vector<double>& ys, double offset )
|
|
{
|
|
std::vector<double> adjusted;
|
|
for ( auto p : ys )
|
|
adjusted.push_back( p + offset );
|
|
|
|
return adjusted;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RigThermalFractureResultUtil::appendDataToResultStatistics( std::shared_ptr<const RigThermalFractureDefinition> fractureDefinition,
|
|
const QString& resultName,
|
|
const QString& unit,
|
|
MinMaxAccumulator& minMaxAccumulator,
|
|
PosNegAccumulator& posNegAccumulator )
|
|
{
|
|
int propertyIndex = fractureDefinition->getPropertyIndex( resultName );
|
|
if ( propertyIndex < 0 ) return;
|
|
|
|
int maxTs = static_cast<int>( fractureDefinition->timeSteps().size() );
|
|
for ( int ts = 0; ts < maxTs; ts++ )
|
|
{
|
|
for ( int nodeIndex = 0; nodeIndex < static_cast<int>( fractureDefinition->numNodes() ); nodeIndex++ )
|
|
{
|
|
double value = fractureDefinition->getPropertyValue( propertyIndex, nodeIndex, ts );
|
|
minMaxAccumulator.addValue( value );
|
|
posNegAccumulator.addValue( value );
|
|
}
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
std::vector<cvf::Vec3d>
|
|
RigThermalFractureResultUtil::getRelativeCoordinates( std::shared_ptr<const RigThermalFractureDefinition> fractureDefinition,
|
|
size_t timeStepIndex )
|
|
{
|
|
std::vector<cvf::Vec3d> relativePos = fractureDefinition->relativeCoordinates( static_cast<int>( timeStepIndex ) );
|
|
CAF_ASSERT( relativePos.size() == fractureDefinition->numNodes() );
|
|
|
|
for ( auto& r : relativePos )
|
|
{
|
|
r.z() *= -1.0;
|
|
}
|
|
|
|
// Store the depths of the points
|
|
std::vector<double> depths;
|
|
for ( auto& r : relativePos )
|
|
{
|
|
depths.push_back( r.z() );
|
|
}
|
|
|
|
cvf::Vec3d p0 = relativePos[0];
|
|
cvf::Vec3d p1 = relativePos[1];
|
|
cvf::Vec3d p2 = relativePos[2];
|
|
|
|
cvf::Plane plane;
|
|
plane.setFromPoints( p0, p1, p2 );
|
|
|
|
cvf::Vec3d planeNormal = plane.normal().getNormalized();
|
|
auto rotMat = cvf::GeometryTools::rotationMatrixBetweenVectors( planeNormal, ( cvf::Vec3d::Z_AXIS ).getNormalized() );
|
|
|
|
for ( auto& r : relativePos )
|
|
{
|
|
r.transformVector( rotMat );
|
|
r.z() = 0.0;
|
|
}
|
|
|
|
auto findPointsWithMostSimilarDepth = []( const std::vector<cvf::Vec3d>& points, const std::vector<double>& depths )
|
|
{
|
|
double minDiff = std::numeric_limits<double>::max();
|
|
|
|
cvf::Vec3d e1 = cvf::Vec3d::UNDEFINED;
|
|
cvf::Vec3d e2 = cvf::Vec3d::UNDEFINED;
|
|
for ( size_t i1 = 0; i1 < points.size(); i1++ )
|
|
{
|
|
for ( size_t i2 = 0; i2 < points.size(); i2++ )
|
|
{
|
|
if ( i1 != i2 )
|
|
{
|
|
double diff = std::fabs( depths[i1] - depths[i2] );
|
|
if ( diff < minDiff )
|
|
{
|
|
minDiff = diff;
|
|
e1 = points[i1];
|
|
e2 = points[i2];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return std::make_pair( e1, e2 );
|
|
};
|
|
|
|
// Find the rotation that aligns the data so that depth (z coord) is the most similar.
|
|
auto [e1, e2] = findPointsWithMostSimilarDepth( relativePos, depths );
|
|
cvf::Vec3d direction = e1 - e2;
|
|
cvf::Vec3d directionNormal = direction.getNormalized();
|
|
// Make sure normal is pointing down
|
|
if ( e1.x() < e2.x() ) directionNormal *= -1.0;
|
|
|
|
auto rotMat2 = cvf::GeometryTools::rotationMatrixBetweenVectors( directionNormal, cvf::Vec3d::X_AXIS );
|
|
for ( auto& r : relativePos )
|
|
{
|
|
r.transformVector( rotMat2 );
|
|
}
|
|
|
|
return relativePos;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
std::pair<cvf::Vec3d, cvf::Vec3d>
|
|
RigThermalFractureResultUtil::computePositionAndRotation( std::shared_ptr<const RigThermalFractureDefinition> fractureDefinition,
|
|
size_t timeStepIndex )
|
|
{
|
|
std::vector<cvf::Vec3d> relativePos = fractureDefinition->relativeCoordinates( static_cast<int>( timeStepIndex ) );
|
|
CAF_ASSERT( relativePos.size() == fractureDefinition->numNodes() );
|
|
|
|
cvf::Plane plane;
|
|
cvf::Vec3d p0 = relativePos[0];
|
|
cvf::Vec3d p1 = relativePos[1];
|
|
cvf::Vec3d p2 = relativePos[2];
|
|
|
|
p0.z() *= -1.0;
|
|
p1.z() *= -1.0;
|
|
p2.z() *= -1.0;
|
|
plane.setFromPoints( p0, p1, p2 );
|
|
|
|
cvf::Vec3d planeNormal = plane.normal().getNormalized();
|
|
RiaLogging::info( QString( "Plane normal: [%1 %2 %3]" ).arg( planeNormal.x() ).arg( planeNormal.y() ).arg( planeNormal.z() ) );
|
|
|
|
cvf::Plane xyPlane;
|
|
xyPlane.setFromPointAndNormal( cvf::Vec3d::ZERO, cvf::Vec3d::Z_AXIS );
|
|
|
|
cvf::Vec3d inPlane;
|
|
if ( !xyPlane.projectVector( planeNormal, &inPlane ) ) RiaLogging::info( "Failed to project vector" );
|
|
|
|
double azimuth = cvf::Math::toDegrees( cvf::GeometryTools::getAngle( inPlane, cvf::Vec3d::Y_AXIS ) ) + 90.0;
|
|
double dip = cvf::Math::toDegrees( cvf::GeometryTools::getAngle( inPlane, cvf::Vec3d::Z_AXIS ) ) - 90.0;
|
|
double tilt = cvf::Math::toDegrees( cvf::GeometryTools::getAngle( planeNormal, cvf::Vec3d::Z_AXIS ) ) - 90.0;
|
|
RiaLogging::info( QString( "Dip: %1" ).arg( dip ) );
|
|
RiaLogging::info( QString( "Tilt: %1" ).arg( tilt ) );
|
|
RiaLogging::info( QString( "Azimuth: %1" ).arg( azimuth ) );
|
|
|
|
cvf::Vec3d rotation( azimuth, dip, tilt );
|
|
return std::make_pair( fractureDefinition->centerPosition(), rotation );
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
double RigThermalFractureResultUtil::interpolateProperty( const cvf::Vec3d& position,
|
|
const std::vector<cvf::Vec3d>& points,
|
|
std::shared_ptr<const RigThermalFractureDefinition> fractureDefinition,
|
|
int propertyIndex,
|
|
size_t timeStepIndex )
|
|
{
|
|
// Compute the distance to the other points
|
|
std::vector<std::pair<double, int>> distances;
|
|
|
|
int index = 0;
|
|
for ( auto p : points )
|
|
{
|
|
double distance = position.pointDistance( p );
|
|
distances.push_back( std::make_pair( distance, index ) );
|
|
index++;
|
|
}
|
|
|
|
// Sort by distance
|
|
std::sort( distances.begin(), distances.end() );
|
|
|
|
// Create inverse-distance-weighthed mean of first few points
|
|
size_t numPoints = 3;
|
|
RiaWeightedMeanCalculator<double> calc;
|
|
for ( size_t i = 0; i < numPoints; i++ )
|
|
{
|
|
auto [distance, nodeIndex] = distances[i];
|
|
double value = fractureDefinition->getPropertyValue( propertyIndex, nodeIndex, static_cast<int>( timeStepIndex ) );
|
|
calc.addValueAndWeight( value, std::pow( 1.0 / distance, 2.0 ) );
|
|
}
|
|
|
|
return calc.weightedMean();
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
std::pair<double, double> RigThermalFractureResultUtil::minMaxDepth( std::shared_ptr<const RigThermalFractureDefinition> fractureDefinition,
|
|
int activeTimeStepIndex )
|
|
{
|
|
auto getBoundingBox = []( const std::vector<cvf::Vec3d>& coords )
|
|
{
|
|
cvf::BoundingBox bb;
|
|
for ( auto c : coords )
|
|
bb.add( c );
|
|
return bb;
|
|
};
|
|
|
|
auto relativeCoords = getRelativeCoordinates( fractureDefinition, activeTimeStepIndex );
|
|
auto bb = getBoundingBox( relativeCoords );
|
|
|
|
double centerZ = fractureDefinition->centerPosition().z();
|
|
// Y is depth in fracture coordinate system.
|
|
return std::make_pair( centerZ + bb.min().y(), centerZ + bb.max().y() );
|
|
}
|