2018-09-28 14:06:56 +02:00
/////////////////////////////////////////////////////////////////////////////////
//
2019-01-09 15:15:34 +01:00
// Copyright (C) 2018- Equinor ASA
//
2018-09-28 14:06:56 +02:00
// 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.
2019-01-09 15:15:34 +01:00
//
2018-09-28 14:06:56 +02:00
// 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.
2019-01-09 15:15:34 +01:00
//
// See the GNU General Public License at <http://www.gnu.org/licenses/gpl.html>
2018-09-28 14:06:56 +02:00
// for more details.
//
/////////////////////////////////////////////////////////////////////////////////
# include "RiaLineArcWellPathCalculator.h"
# include "cvfBase.h"
# include "cvfAssert.h"
# include "RiaOffshoreSphericalCoords.h"
# include "RiaJCurveCalculator.h"
# include "RiaSCurveCalculator.h"
# define M_PI 3.14159265358979323846 // pi
cvf : : Vec3d smootheningTargetTangent ( const cvf : : Vec3d & p1 , const cvf : : Vec3d & p2 , const cvf : : Vec3d & p3 ) ;
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RiaLineArcWellPathCalculator : : RiaLineArcWellPathCalculator ( const cvf : : Vec3d & referencePointXyz ,
const std : : vector < WellTarget > & activeWellPathTargets )
{
// Handle incomplete input
if ( activeWellPathTargets . size ( ) < 2 )
{
m_startTangent = cvf : : Vec3d : : ZERO ;
if ( activeWellPathTargets . size ( ) = = 1 )
{
m_lineArcEndpoints . push_back ( activeWellPathTargets [ 0 ] . targetPointXYZ + referencePointXyz ) ;
m_targetStatuses . resize ( activeWellPathTargets . size ( ) ,
{ ! activeWellPathTargets [ 0 ] . isTangentConstrained , 0.0 , 0.0 ,
true , std : : numeric_limits < double > : : infinity ( ) ,
true , std : : numeric_limits < double > : : infinity ( ) } ) ;
}
return ;
}
m_targetStatuses . resize ( activeWellPathTargets . size ( ) , { false , 0.0 , 0.0 ,
false , std : : numeric_limits < double > : : infinity ( ) ,
false , std : : numeric_limits < double > : : infinity ( ) } ) ;
std : : vector < WellTarget > adjustedWellPathTargets = activeWellPathTargets ;
// Calculate sensible tangents for targets without a fixed one
if ( activeWellPathTargets . size ( ) > 2 )
{
for ( size_t tIdx = 0 ; tIdx < activeWellPathTargets . size ( ) - 2 ; + + tIdx )
{
if ( ! activeWellPathTargets [ tIdx + 1 ] . isTangentConstrained )
{
cvf : : Vec3d tangent = smootheningTargetTangent ( activeWellPathTargets [ tIdx ] . targetPointXYZ ,
activeWellPathTargets [ tIdx + 1 ] . targetPointXYZ ,
activeWellPathTargets [ tIdx + 2 ] . targetPointXYZ ) ;
RiaOffshoreSphericalCoords tangentSphCS ( tangent ) ;
adjustedWellPathTargets [ tIdx + 1 ] . azimuth = tangentSphCS . azi ( ) ;
adjustedWellPathTargets [ tIdx + 1 ] . inclination = tangentSphCS . inc ( ) ;
adjustedWellPathTargets [ tIdx + 1 ] . isTangentConstrained = true ;
m_targetStatuses [ tIdx + 1 ] . hasDerivedTangent = true ;
m_targetStatuses [ tIdx + 1 ] . resultAzimuth = tangentSphCS . azi ( ) ;
m_targetStatuses [ tIdx + 1 ] . resultInclination = tangentSphCS . inc ( ) ;
}
}
}
m_lineArcEndpoints . push_back ( activeWellPathTargets [ 0 ] . targetPointXYZ + referencePointXyz ) ;
// Handle first segment if it is not an S-Curve
size_t startSSegmentIdx = 0 ;
size_t endSSegementIdx = activeWellPathTargets . size ( ) - 1 ;
if ( ! adjustedWellPathTargets [ 0 ] . isTangentConstrained )
{
startSSegmentIdx = 1 ;
const WellTarget & target1 = adjustedWellPathTargets [ 0 ] ;
const WellTarget & target2 = adjustedWellPathTargets [ 1 ] ;
WellTargetStatus & target1Status = m_targetStatuses [ 0 ] ;
WellTargetStatus & target2Status = m_targetStatuses [ 1 ] ;
if ( adjustedWellPathTargets [ 1 ] . isTangentConstrained )
{
// Create an upside down J curve from target 2 back to 1
RiaJCurveCalculator jCurve ( target2 . targetPointXYZ ,
target2 . azimuth + M_PI ,
M_PI - target2 . inclination ,
target2 . radius1 ,
target1 . targetPointXYZ ) ;
if ( jCurve . curveStatus ( ) = = RiaJCurveCalculator : : OK )
{
m_lineArcEndpoints . push_back ( jCurve . firstArcEndpoint ( ) + referencePointXyz ) ;
}
else if ( jCurve . curveStatus ( ) = = RiaJCurveCalculator : : FAILED_RADIUS_TOO_LARGE )
{
target2Status . hasOverriddenRadius1 = true ;
target2Status . resultRadius1 = jCurve . radius ( ) ;
}
m_lineArcEndpoints . push_back ( target2 . targetPointXYZ + referencePointXyz ) ;
target1Status . hasDerivedTangent = true ;
target1Status . resultAzimuth = jCurve . endAzimuth ( ) + M_PI ;
target1Status . resultInclination = M_PI - jCurve . endInclination ( ) ;
}
else // The complete wellpath is a straight line from target 1 to 2
{
m_lineArcEndpoints . push_back ( target2 . targetPointXYZ + referencePointXyz ) ;
cvf : : Vec3d t12 = target2 . targetPointXYZ - target1 . targetPointXYZ ;
RiaOffshoreSphericalCoords t12Sph ( t12 ) ;
target1Status . hasDerivedTangent = true ;
target1Status . resultAzimuth = t12Sph . azi ( ) ;
target1Status . resultInclination = t12Sph . inc ( ) ;
target2Status . hasDerivedTangent = true ;
target2Status . resultAzimuth = t12Sph . azi ( ) ;
target2Status . resultInclination = t12Sph . inc ( ) ;
}
m_startTangent = RiaOffshoreSphericalCoords : : unitVectorFromAziInc ( target1Status . resultAzimuth , target1Status . resultInclination ) ;
}
else
{
m_startTangent = RiaOffshoreSphericalCoords : : unitVectorFromAziInc ( activeWellPathTargets [ 0 ] . azimuth , activeWellPathTargets [ 0 ] . inclination ) ;
}
if ( ! adjustedWellPathTargets . back ( ) . isTangentConstrained )
{
endSSegementIdx - = 1 ;
}
// Calculate S-curves
if ( activeWellPathTargets . size ( ) > 1 )
{
for ( size_t tIdx = startSSegmentIdx ; tIdx < endSSegementIdx ; + + tIdx )
{
const WellTarget & target1 = adjustedWellPathTargets [ tIdx ] ;
const WellTarget & target2 = adjustedWellPathTargets [ tIdx + 1 ] ;
WellTargetStatus & target1Status = m_targetStatuses [ tIdx ] ;
WellTargetStatus & target2Status = m_targetStatuses [ tIdx + 1 ] ;
// Ignore targets in the same place
if ( ( target1 . targetPointXYZ - target2 . targetPointXYZ ) . length ( ) < 1e-6 ) continue ;
if ( target1 . isTangentConstrained
& & target2 . isTangentConstrained )
{
RiaSCurveCalculator sCurveCalc ( target1 . targetPointXYZ ,
target1 . azimuth ,
target1 . inclination ,
target1 . radius2 ,
target2 . targetPointXYZ ,
target2 . azimuth ,
target2 . inclination ,
target2 . radius1 ) ;
if ( sCurveCalc . solveStatus ( ) ! = RiaSCurveCalculator : : CONVERGED )
{
double p1p2Length = ( target2 . targetPointXYZ - target1 . targetPointXYZ ) . length ( ) ;
sCurveCalc = RiaSCurveCalculator : : fromTangentsAndLength ( target1 . targetPointXYZ ,
target1 . azimuth ,
target1 . inclination ,
0.2 * p1p2Length ,
target2 . targetPointXYZ ,
target2 . azimuth ,
target2 . inclination ,
0.2 * p1p2Length ) ;
//RiaLogging::warning("Using fall-back calculation of well path geometry between active target number: " + QString::number(tIdx+1) + " and " + QString::number(tIdx+2));
target1Status . hasOverriddenRadius2 = true ;
target1Status . resultRadius2 = sCurveCalc . firstRadius ( ) ;
target2Status . hasOverriddenRadius1 = true ;
target2Status . resultRadius1 = sCurveCalc . secondRadius ( ) ;
}
m_lineArcEndpoints . push_back ( sCurveCalc . firstArcEndpoint ( ) + referencePointXyz ) ;
m_lineArcEndpoints . push_back ( sCurveCalc . secondArcStartpoint ( ) + referencePointXyz ) ;
m_lineArcEndpoints . push_back ( target2 . targetPointXYZ + referencePointXyz ) ;
}
}
}
// Handle last segment if (its not the same as the first) and it has not been handled as an S-Curve
if ( adjustedWellPathTargets . size ( ) > 2 & & endSSegementIdx < ( adjustedWellPathTargets . size ( ) - 1 ) )
{
size_t targetCount = adjustedWellPathTargets . size ( ) ;
const WellTarget & target1 = adjustedWellPathTargets [ targetCount - 2 ] ;
const WellTarget & target2 = adjustedWellPathTargets [ targetCount - 1 ] ;
WellTargetStatus & target1Status = m_targetStatuses [ targetCount - 2 ] ;
WellTargetStatus & target2Status = m_targetStatuses [ targetCount - 1 ] ;
// Create an ordinary J curve
RiaJCurveCalculator jCurve ( target1 . targetPointXYZ ,
target1 . azimuth ,
target1 . inclination ,
target1 . radius2 ,
target2 . targetPointXYZ ) ;
if ( jCurve . curveStatus ( ) = = RiaJCurveCalculator : : OK )
{
m_lineArcEndpoints . push_back ( jCurve . firstArcEndpoint ( ) + referencePointXyz ) ;
}
else if ( jCurve . curveStatus ( ) = = RiaJCurveCalculator : : FAILED_RADIUS_TOO_LARGE )
{
target1Status . hasOverriddenRadius2 = true ;
target1Status . resultRadius2 = jCurve . radius ( ) ;
}
m_lineArcEndpoints . push_back ( target2 . targetPointXYZ + referencePointXyz ) ;
target2Status . hasDerivedTangent = true ;
target2Status . resultAzimuth = jCurve . endAzimuth ( ) ;
target2Status . resultInclination = jCurve . endInclination ( ) ;
}
}
cvf : : Vec3d smootheningTargetTangent ( const cvf : : Vec3d & p1 , const cvf : : Vec3d & p2 , const cvf : : Vec3d & p3 )
{
cvf : : Vec3d t12 = p2 - p1 ;
cvf : : Vec3d t23 = p3 - p2 ;
double length12 = t12 . length ( ) ;
double length23 = t23 . length ( ) ;
t12 / = length12 ; // Normalize
t23 / = length23 ; // Normalize
cvf : : Vec3d t1t2Hor ( t12 ) ;
t1t2Hor . z ( ) = 0.0 ;
double t12HorLength = t1t2Hor . length ( ) ;
cvf : : Vec3d t23Hor ( t23 ) ;
t23Hor . z ( ) = 0.0 ;
double t23HorLength = t23Hor . length ( ) ;
// Calculate weights as combo of inverse distance and horizontal component
double w12 = t12HorLength * 1.0 / length12 ;
double w23 = t23HorLength * 1.0 / length23 ;
// Weight the tangents
t12 * = w12 ; // Weight
t23 * = w23 ; // Weight
// Sum and normalization of weights
cvf : : Vec3d averageTangent = 1.0 / ( w12 + w23 ) * ( t12 + t23 ) ;
return averageTangent ;
}