ResInsight/ApplicationLibCode/ModelVisualization/RivWellPathPartMgr.cpp
2022-06-27 19:25:27 +02:00

1101 lines
49 KiB
C++

/////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2011- Statoil ASA
// Copyright (C) 2013- Ceetron Solutions AS
// Copyright (C) 2011-2012 Ceetron AS
//
// 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 "RivWellPathPartMgr.h"
#include "RiaGuiApplication.h"
#include "RigEclipseCaseData.h"
#include "RigMainGrid.h"
#include "RigVirtualPerforationTransmissibilities.h"
#include "RigWellPath.h"
#include "Rim3dWellLogCurveCollection.h"
#include "RimEclipseCase.h"
#include "RimEclipseView.h"
#include "RimFishbones.h"
#include "RimFishbonesCollection.h"
#include "RimModeledWellPath.h"
#include "RimPerforationCollection.h"
#include "RimPerforationInterval.h"
#include "RimRegularLegendConfig.h"
#include "RimTools.h"
#include "RimWellIASettings.h"
#include "RimWellIASettingsCollection.h"
#include "RimWellMeasurement.h"
#include "RimWellMeasurementCollection.h"
#include "RimWellMeasurementFilter.h"
#include "RimWellMeasurementInView.h"
#include "RimWellMeasurementInViewCollection.h"
#include "RimWellPath.h"
#include "RimWellPathAttribute.h"
#include "RimWellPathAttributeCollection.h"
#include "RimWellPathCollection.h"
#include "RimWellPathFracture.h"
#include "RimWellPathFractureCollection.h"
#include "RimWellPathGeometryDef.h"
#include "RimWellPathTarget.h"
#include "RimWellPathValve.h"
#include "Riv3dWellLogPlanePartMgr.h"
#include "RivBoxGeometryGenerator.h"
#include "RivDrawableSpheres.h"
#include "RivFishbonesSubsPartMgr.h"
#include "RivObjectSourceInfo.h"
#include "RivPartPriority.h"
#include "RivPipeGeometryGenerator.h"
#include "RivSectionFlattener.h"
#include "RivTextLabelSourceInfo.h"
#include "RivWellConnectionFactorPartMgr.h"
#include "RivWellFracturePartMgr.h"
#include "RivWellPathPartMgr.h"
#include "RivWellPathSourceInfo.h"
#include "RiuViewer.h"
#include "cafDisplayCoordTransform.h"
#include "cafEffectGenerator.h"
#include "cvfDrawableGeo.h"
#include "cvfDrawableText.h"
#include "cvfDrawableVectors.h"
#include "cvfFont.h"
#include "cvfGeometryBuilderTriangles.h"
#include "cvfGeometryUtils.h"
#include "cvfModelBasicList.h"
#include "cvfOpenGLResourceManager.h"
#include "cvfPart.h"
#include "cvfScalarMapperContinuousLinear.h"
#include "cvfShaderProgram.h"
#include "cvfTransform.h"
#include "cvfqtUtils.h"
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RivWellPathPartMgr::RivWellPathPartMgr( RimWellPath* wellPath, Rim3dView* view )
{
m_rimWellPath = wellPath;
m_rimView = view;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RivWellPathPartMgr::~RivWellPathPartMgr()
{
clearAllBranchData();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
bool RivWellPathPartMgr::isWellPathWithinBoundingBox( const cvf::BoundingBox& wellPathClipBoundingBox ) const
{
if ( !m_rimWellPath->wellPathGeometry() ) return false;
const std::vector<cvf::Vec3d>& wellpathCenterLine = m_rimWellPath->wellPathGeometry()->wellPathPoints();
if ( wellpathCenterLine.size() < 2 ) return false;
// Skip visualization if outside the domain of this case
{
cvf::Vec3d casemax = wellPathClipBoundingBox.max();
cvf::Vec3d casemin = wellPathClipBoundingBox.min();
cvf::Vec3d caseext = wellPathClipBoundingBox.extent();
// Add up to the sealevel
cvf::BoundingBox relevantWellpathBBox = wellPathClipBoundingBox;
relevantWellpathBBox.add( cvf::Vec3d( casemax.x(), casemax.y(), 0.0 ) );
// Add some sideways leeway
cvf::Vec3d addSize = 3.0 * cvf::Vec3d( caseext.x(), caseext.y(), 0.0 );
relevantWellpathBBox.add( casemax + addSize );
relevantWellpathBBox.add( casemin - addSize );
if ( !RigWellPath::isAnyPointInsideBoundingBox( wellpathCenterLine, relevantWellpathBBox ) )
{
return false;
}
}
return true;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
bool RivWellPathPartMgr::isWellPathEnabled( const cvf::BoundingBox& wellPathClipBoundingBox ) const
{
RimWellPathCollection* wellPathCollection = this->wellPathCollection();
if ( !wellPathCollection ) return false;
if ( !wellPathCollection->isActive() ) return false;
if ( wellPathCollection->wellPathVisibility() == RimWellPathCollection::FORCE_ALL_OFF ) return false;
if ( wellPathCollection->wellPathVisibility() == RimWellPathCollection::ALL_ON && m_rimWellPath->showWellPath() == false )
return false;
if ( !isWellPathWithinBoundingBox( wellPathClipBoundingBox ) ) return false;
return true;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RivWellPathPartMgr::appendStaticFracturePartsToModel( cvf::ModelBasicList* model,
const cvf::BoundingBox& wellPathClipBoundingBox )
{
if ( m_rimView.isNull() ) return;
const RimEclipseView* eclView = dynamic_cast<const RimEclipseView*>( m_rimView.p() );
if ( !eclView ) return;
if ( !m_rimWellPath || !m_rimWellPath->showWellPath() || !m_rimWellPath->fractureCollection()->isChecked() ) return;
if ( !isWellPathWithinBoundingBox( wellPathClipBoundingBox ) ) return;
for ( RimWellPathFracture* f : m_rimWellPath->fractureCollection()->activeFractures() )
{
CVF_ASSERT( f );
f->fracturePartManager()->appendGeometryPartsToModel( model, *eclView );
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RivWellPathPartMgr::appendFishboneSubsPartsToModel( cvf::ModelBasicList* model,
const caf::DisplayCoordTransform* displayCoordTransform,
double characteristicCellSize )
{
if ( !m_rimWellPath || !m_rimWellPath->fishbonesCollection()->isChecked() ) return;
for ( const auto& rimFishboneSubs : m_rimWellPath->fishbonesCollection()->activeFishbonesSubs() )
{
cvf::ref<RivFishbonesSubsPartMgr> fishbSubPartMgr = new RivFishbonesSubsPartMgr( rimFishboneSubs );
fishbSubPartMgr->appendGeometryPartsToModel( model, displayCoordTransform, characteristicCellSize );
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RivWellPathPartMgr::appendWellPathAttributesToModel( cvf::ModelBasicList* model,
const caf::DisplayCoordTransform* displayCoordTransform,
double characteristicCellSize )
{
if ( !m_rimWellPath ) return;
RivPipeGeometryGenerator geoGenerator;
std::vector<RimWellPathAttribute*> attributes = m_rimWellPath->attributeCollection()->attributes();
for ( RimWellPathAttribute* attribute : attributes )
{
if ( attribute->isEnabled() )
{
if ( attribute->componentType() == RiaDefines::WellPathComponentType::CASING )
{
double wellPathRadius = this->wellPathRadius( characteristicCellSize, this->wellPathCollection() );
double endMD = attribute->endMD();
double shoeLength = wellPathRadius;
double shoeStartMD = endMD - shoeLength;
std::vector<cvf::Vec3d> displayCoords;
displayCoords.push_back( displayCoordTransform->transformToDisplayCoord(
m_rimWellPath->wellPathGeometry()->interpolatedPointAlongWellPath( shoeStartMD ) ) );
displayCoords.push_back( displayCoordTransform->transformToDisplayCoord(
m_rimWellPath->wellPathGeometry()->interpolatedPointAlongWellPath( endMD ) ) );
displayCoords.push_back( displayCoordTransform->transformToDisplayCoord(
m_rimWellPath->wellPathGeometry()->interpolatedPointAlongWellPath( endMD ) ) );
std::vector<double> radii;
radii.push_back( wellPathRadius );
radii.push_back( wellPathRadius * 2.5 );
radii.push_back( wellPathRadius * 1.1 );
cvf::ref<RivObjectSourceInfo> objectSourceInfo = new RivObjectSourceInfo( attribute );
cvf::Collection<cvf::Part> parts;
geoGenerator.tubeWithCenterLinePartsAndVariableWidth( &parts,
displayCoords,
radii,
attribute->defaultComponentColor() );
for ( auto part : parts )
{
part->setSourceInfo( objectSourceInfo.p() );
model->addPart( part.p() );
}
}
else if ( attribute->componentType() == RiaDefines::WellPathComponentType::PACKER )
{
double wellPathRadius = this->wellPathRadius( characteristicCellSize, this->wellPathCollection() );
double startMD = attribute->startMD();
double endMD = attribute->endMD();
std::vector<cvf::Vec3d> displayCoords;
displayCoords.push_back( displayCoordTransform->transformToDisplayCoord(
m_rimWellPath->wellPathGeometry()->interpolatedPointAlongWellPath( startMD ) ) );
displayCoords.push_back( displayCoordTransform->transformToDisplayCoord(
m_rimWellPath->wellPathGeometry()->interpolatedPointAlongWellPath( startMD ) ) );
displayCoords.push_back( displayCoordTransform->transformToDisplayCoord(
m_rimWellPath->wellPathGeometry()->interpolatedPointAlongWellPath( endMD ) ) );
displayCoords.push_back( displayCoordTransform->transformToDisplayCoord(
m_rimWellPath->wellPathGeometry()->interpolatedPointAlongWellPath( endMD ) ) );
std::vector<double> radii;
radii.push_back( wellPathRadius );
radii.push_back( wellPathRadius * 1.5 );
radii.push_back( wellPathRadius * 1.5 );
radii.push_back( wellPathRadius );
cvf::ref<RivObjectSourceInfo> objectSourceInfo = new RivObjectSourceInfo( attribute );
cvf::Collection<cvf::Part> parts;
geoGenerator.tubeWithCenterLinePartsAndVariableWidth( &parts,
displayCoords,
radii,
attribute->defaultComponentColor() );
for ( auto part : parts )
{
part->setSourceInfo( objectSourceInfo.p() );
model->addPart( part.p() );
}
}
}
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RivWellPathPartMgr::appendWellMeasurementsToModel( cvf::ModelBasicList* model,
const caf::DisplayCoordTransform* displayCoordTransform,
double characteristicCellSize )
{
if ( !m_rimWellPath ) return;
RimGridView* gridView = dynamic_cast<RimGridView*>( m_rimView.p() );
if ( !gridView ) return;
RimWellPathCollection* wellPathCollection = RimTools::wellPathCollection();
if ( !wellPathCollection ) return;
RimWellMeasurementCollection* wellMeasurementCollection = wellPathCollection->measurementCollection();
if ( !wellMeasurementCollection ) return;
if ( !gridView->measurementCollection()->isChecked() ) return;
for ( RimWellMeasurementInView* wellMeasurementInView : gridView->measurementCollection()->measurements() )
{
if ( wellMeasurementInView->isChecked() && wellMeasurementInView->isWellChecked( m_rimWellPath->name() ) )
{
std::vector<QString> measurementKinds;
measurementKinds.push_back( wellMeasurementInView->measurementKind() );
double lowerBound = 0.0;
double upperBound = 0.0;
wellMeasurementInView->rangeValues( &lowerBound, &upperBound );
std::vector<int> qualityFilter = wellMeasurementInView->qualityFilter();
std::vector<RimWellMeasurement*> wellMeasurements =
RimWellMeasurementFilter::filterMeasurements( wellMeasurementCollection->measurements(),
*wellPathCollection,
*m_rimWellPath,
measurementKinds,
lowerBound,
upperBound,
qualityFilter );
RivPipeGeometryGenerator geoGenerator;
for ( RimWellMeasurement* wellMeasurement : wellMeasurements )
{
double wellPathRadius = this->wellPathRadius( characteristicCellSize, this->wellPathCollection() );
double startMD = wellMeasurement->MD() - wellPathRadius * 0.5;
double endMD = wellMeasurement->MD() + wellPathRadius * 0.5;
double wellMeasurementRadius = this->wellMeasurementRadius( characteristicCellSize,
this->wellPathCollection(),
wellMeasurementInView );
std::vector<cvf::Vec3d> displayCoords;
displayCoords.push_back( displayCoordTransform->transformToDisplayCoord(
m_rimWellPath->wellPathGeometry()->interpolatedPointAlongWellPath( startMD ) ) );
displayCoords.push_back( displayCoordTransform->transformToDisplayCoord(
m_rimWellPath->wellPathGeometry()->interpolatedPointAlongWellPath( startMD ) ) );
displayCoords.push_back( displayCoordTransform->transformToDisplayCoord(
m_rimWellPath->wellPathGeometry()->interpolatedPointAlongWellPath( endMD ) ) );
displayCoords.push_back( displayCoordTransform->transformToDisplayCoord(
m_rimWellPath->wellPathGeometry()->interpolatedPointAlongWellPath( endMD ) ) );
std::vector<double> radii;
radii.push_back( std::min( wellPathRadius, wellMeasurementRadius ) );
radii.push_back( wellMeasurementRadius );
radii.push_back( wellMeasurementRadius );
radii.push_back( std::min( wellPathRadius, wellMeasurementRadius ) );
cvf::ref<RivObjectSourceInfo> objectSourceInfo = new RivObjectSourceInfo( wellMeasurement );
cvf::Collection<cvf::Part> parts;
// Use the view legend config to find color, if only one type of measurement is selected.
cvf::Color3f color = cvf::Color3f(
wellMeasurementInView->legendConfig()->scalarMapper()->mapToColor( wellMeasurement->value() ) );
geoGenerator.tubeWithCenterLinePartsAndVariableWidth( &parts, displayCoords, radii, color );
for ( auto part : parts )
{
part->setSourceInfo( objectSourceInfo.p() );
model->addPart( part.p() );
}
}
}
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RivWellPathPartMgr::appendPerforationsToModel( cvf::ModelBasicList* model,
size_t timeStepIndex,
const caf::DisplayCoordTransform* displayCoordTransform,
double characteristicCellSize,
bool doFlatten )
{
if ( !m_rimWellPath || !m_rimWellPath->perforationIntervalCollection()->isChecked() ) return;
RimWellPathCollection* wellPathCollection = this->wellPathCollection();
if ( !wellPathCollection ) return;
RigWellPath* wellPathGeometry = m_rimWellPath->wellPathGeometry();
if ( !wellPathGeometry ) return;
QDateTime currentTimeStamp;
if ( m_rimView )
{
RimCase* rimCase = nullptr;
m_rimView->firstAncestorOrThisOfType( rimCase );
if ( rimCase )
{
std::vector<QDateTime> timeStamps = rimCase->timeStepDates();
if ( timeStepIndex < timeStamps.size() )
{
currentTimeStamp = timeStamps[timeStepIndex];
}
}
}
// Since we're using the index of measured depths to find the index of a point, ensure they're equal
CVF_ASSERT( wellPathGeometry->measuredDepths().size() == wellPathGeometry->wellPathPoints().size() );
double wellPathRadius = this->wellPathRadius( characteristicCellSize, wellPathCollection );
double perforationRadius = wellPathRadius * 1.1;
RivPipeGeometryGenerator geoGenerator;
std::vector<RimPerforationInterval*> perforations;
m_rimWellPath->descendantsIncludingThisOfType( perforations );
for ( RimPerforationInterval* perforation : perforations )
{
using namespace std;
if ( !perforation->isChecked() ) continue;
if ( perforation->startMD() > perforation->endMD() ) continue;
if ( !perforation->isActiveOnDate( currentTimeStamp ) ) continue;
double horizontalLengthAlongWellPath = 0.0;
vector<cvf::Vec3d> perfIntervalCL;
{
pair<vector<cvf::Vec3d>, vector<double>> perfintervalCoordsAndMD =
wellPathGeometry->clippedPointSubset( perforation->startMD(),
perforation->endMD(),
&horizontalLengthAlongWellPath );
perfIntervalCL = perfintervalCoordsAndMD.first;
}
if ( perfIntervalCL.size() < 2 ) continue;
vector<cvf::Vec3d> perfIntervalCLDiplayCS;
if ( doFlatten )
{
cvf::Vec3d dummy;
vector<cvf::Mat4d> flatningCSs =
RivSectionFlattener::calculateFlatteningCSsForPolyline( perfIntervalCL,
cvf::Vec3d::Z_AXIS,
{ horizontalLengthAlongWellPath,
0.0,
perfIntervalCL[0].z() },
&dummy );
for ( size_t cIdx = 0; cIdx < perfIntervalCL.size(); ++cIdx )
{
auto clpoint = perfIntervalCL[cIdx].getTransformedPoint( flatningCSs[cIdx] );
perfIntervalCLDiplayCS.push_back( displayCoordTransform->scaleToDisplaySize( clpoint ) );
}
}
else
{
for ( cvf::Vec3d& point : perfIntervalCL )
{
perfIntervalCLDiplayCS.push_back( displayCoordTransform->transformToDisplayCoord( point ) );
}
}
{
cvf::ref<RivObjectSourceInfo> objectSourceInfo = new RivObjectSourceInfo( perforation );
cvf::Collection<cvf::Part> parts;
geoGenerator.cylinderWithCenterLineParts( &parts, perfIntervalCLDiplayCS, cvf::Color3f::GREEN, perforationRadius );
for ( auto part : parts )
{
part->setSourceInfo( objectSourceInfo.p() );
model->addPart( part.p() );
}
}
appendPerforationValvesToModel( model, perforation, wellPathRadius, displayCoordTransform, geoGenerator );
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RivWellPathPartMgr::appendPerforationValvesToModel( cvf::ModelBasicList* model,
RimPerforationInterval* perforation,
double wellPathRadius,
const caf::DisplayCoordTransform* displayCoordTransform,
RivPipeGeometryGenerator& geoGenerator )
{
// Valves
{
for ( RimWellPathValve* valve : perforation->valves() )
{
if ( !valve->isChecked() ) continue;
std::vector<double> measuredDepthsRelativeToStartMD;
std::vector<double> radii;
cvf::Color3f valveColor = valve->defaultComponentColor();
if ( valve->componentType() == RiaDefines::WellPathComponentType::ICV )
{
measuredDepthsRelativeToStartMD = { 0.0, 1.0, 1.5, 4.0, 5.0, 5.5, 8.0, 9.0 };
radii = { wellPathRadius,
wellPathRadius * 1.8,
wellPathRadius * 2.0,
wellPathRadius * 2.0,
wellPathRadius * 1.8,
wellPathRadius * 1.7,
wellPathRadius * 1.7,
wellPathRadius };
double startMD = valve->startMD();
std::vector<cvf::Vec3d> displayCoords;
for ( double mdRelativeToStart : measuredDepthsRelativeToStartMD )
{
displayCoords.push_back( displayCoordTransform->transformToDisplayCoord(
m_rimWellPath->wellPathGeometry()->interpolatedPointAlongWellPath(
mdRelativeToStart * 0.333 * wellPathRadius + startMD ) ) );
}
cvf::ref<RivObjectSourceInfo> objectSourceInfo = new RivObjectSourceInfo( valve );
cvf::Collection<cvf::Part> parts;
geoGenerator.tubeWithCenterLinePartsAndVariableWidth( &parts, displayCoords, radii, valveColor );
for ( auto part : parts )
{
part->setSourceInfo( objectSourceInfo.p() );
model->addPart( part.p() );
}
}
else if ( valve->componentType() == RiaDefines::WellPathComponentType::ICD ||
valve->componentType() == RiaDefines::WellPathComponentType::AICD )
{
std::vector<double> valveLocations = valve->valveLocations();
for ( double startMD : valveLocations )
{
int size = 16;
measuredDepthsRelativeToStartMD.resize( size );
radii.resize( size );
for ( int i = 0; i < size; i += 2 )
{
measuredDepthsRelativeToStartMD[i] = double( i / 2 );
measuredDepthsRelativeToStartMD[i + 1] = double( i / 2 + 0.5 );
}
radii[0] = wellPathRadius;
bool inner = false;
int nInners = 0;
for ( int i = 1; i < size - 1; i += 2 )
{
if ( inner && valve->componentType() == RiaDefines::WellPathComponentType::AICD && nInners > 0 )
{
radii[i + 1] = radii[i] = wellPathRadius * 1.7;
nInners = 0;
}
else if ( inner )
{
radii[i + 1] = radii[i] = wellPathRadius * 1.9;
nInners++;
}
else
{
radii[i + 1] = radii[i] = wellPathRadius * 2.0;
}
inner = !inner;
}
radii[size - 1] = wellPathRadius;
std::vector<cvf::Vec3d> displayCoords;
for ( double mdRelativeToStart : measuredDepthsRelativeToStartMD )
{
displayCoords.push_back( displayCoordTransform->transformToDisplayCoord(
m_rimWellPath->wellPathGeometry()->interpolatedPointAlongWellPath(
mdRelativeToStart * 0.333 * wellPathRadius + startMD ) ) );
}
cvf::ref<RivObjectSourceInfo> objectSourceInfo = new RivObjectSourceInfo( valve );
cvf::Collection<cvf::Part> parts;
geoGenerator.tubeWithCenterLinePartsAndVariableWidth( &parts, displayCoords, radii, valveColor );
for ( auto part : parts )
{
part->setSourceInfo( objectSourceInfo.p() );
model->addPart( part.p() );
}
}
}
}
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RivWellPathPartMgr::appendVirtualTransmissibilitiesToModel( cvf::ModelBasicList* model,
size_t timeStepIndex,
const caf::DisplayCoordTransform* displayCoordTransform,
double characteristicCellSize )
{
RimEclipseView* eclView = dynamic_cast<RimEclipseView*>( m_rimView.p() );
if ( !eclView ) return;
if ( !eclView->isVirtualConnectionFactorGeometryVisible() ) return;
RimEclipseCase* eclipseCase = nullptr;
eclView->firstAncestorOrThisOfType( eclipseCase );
if ( !eclipseCase ) return;
const RigVirtualPerforationTransmissibilities* trans =
eclipseCase->computeAndGetVirtualPerforationTransmissibilities();
if ( trans )
{
m_wellConnectionFactorPartMgr =
new RivWellConnectionFactorPartMgr( m_rimWellPath, eclView->virtualPerforationResult() );
m_wellConnectionFactorPartMgr->appendDynamicGeometryPartsToModel( model, timeStepIndex );
}
}
//--------------------------------------------------------------------------------------------------
/// The pipe geometry needs to be rebuilt on scale change to keep the pipes round
//--------------------------------------------------------------------------------------------------
void RivWellPathPartMgr::buildWellPathParts( const caf::DisplayCoordTransform* displayCoordTransform,
double characteristicCellSize,
const cvf::BoundingBox& wellPathClipBoundingBox,
bool doFlatten )
{
RimWellPathCollection* wellPathCollection = this->wellPathCollection();
if ( !wellPathCollection ) return;
RigWellPath* wellPathGeometry = m_rimWellPath->wellPathGeometry();
if ( !wellPathGeometry ) return;
std::vector<cvf::Vec3d> wellpathCenterLine = wellPathGeometry->uniqueWellPathPoints();
if ( wellpathCenterLine.size() < 2 ) return;
clearAllBranchData();
double wellPathRadius = this->wellPathRadius( characteristicCellSize, wellPathCollection );
std::vector<cvf::Vec3d> clippedWellPathCenterLine;
// Generate the well path geometry as a line and pipe structure
m_pipeGeomGenerator = new RivPipeGeometryGenerator;
m_pipeGeomGenerator->setRadius( wellPathRadius );
m_pipeGeomGenerator->setCrossSectionVertexCount( wellPathCollection->wellPathCrossSectionVertexCount() );
double horizontalLengthAlongWellToClipPoint = 0.0;
size_t idxToFirstVisibleSegment = 0;
if ( wellPathCollection->wellPathClip )
{
double maxZClipHeight = wellPathClipBoundingBox.max().z() + wellPathCollection->wellPathClipZDistance;
clippedWellPathCenterLine = RigWellPath::clipPolylineStartAboveZ( wellpathCenterLine,
maxZClipHeight,
&horizontalLengthAlongWellToClipPoint,
&idxToFirstVisibleSegment );
}
else
{
clippedWellPathCenterLine = wellpathCenterLine;
}
if ( clippedWellPathCenterLine.size() < 2 ) return;
cvf::ref<cvf::Vec3dArray> cvfCoords = new cvf::Vec3dArray( clippedWellPathCenterLine.size() );
// Scale the centerline coordinates using the Z-scale transform of the grid and correct for the display offset.
if ( doFlatten )
{
cvf::Vec3d dummy;
std::vector<cvf::Mat4d> flatningCSs =
RivSectionFlattener::calculateFlatteningCSsForPolyline( clippedWellPathCenterLine,
cvf::Vec3d::Z_AXIS,
{ horizontalLengthAlongWellToClipPoint,
0.0,
clippedWellPathCenterLine[0].z() },
&dummy );
for ( size_t cIdx = 0; cIdx < cvfCoords->size(); ++cIdx )
{
auto clpoint = clippedWellPathCenterLine[cIdx].getTransformedPoint( flatningCSs[cIdx] );
( *cvfCoords )[cIdx] = displayCoordTransform->scaleToDisplaySize( clpoint );
}
}
else
{
for ( size_t cIdx = 0; cIdx < cvfCoords->size(); ++cIdx )
{
( *cvfCoords )[cIdx] = displayCoordTransform->transformToDisplayCoord( clippedWellPathCenterLine[cIdx] );
}
}
m_pipeGeomGenerator->setFirstVisibleSegmentIndex( idxToFirstVisibleSegment );
m_pipeGeomGenerator->setPipeCenterCoords( cvfCoords.p() );
m_surfaceDrawable = m_pipeGeomGenerator->createPipeSurface();
m_centerLineDrawable = m_pipeGeomGenerator->createCenterLine();
if ( m_surfaceDrawable.notNull() )
{
m_surfacePart = new cvf::Part;
m_surfacePart->setName( "RivWellPathPartMgr::surface" );
m_surfacePart->setDrawable( m_surfaceDrawable.p() );
RivWellPathSourceInfo* sourceInfo = new RivWellPathSourceInfo( m_rimWellPath, m_pipeGeomGenerator.p() );
m_surfacePart->setSourceInfo( sourceInfo );
caf::SurfaceEffectGenerator surfaceGen( cvf::Color4f( m_rimWellPath->wellPathColor() ), caf::PO_1 );
cvf::ref<cvf::Effect> eff = surfaceGen.generateCachedEffect();
m_surfacePart->setEffect( eff.p() );
}
if ( m_centerLineDrawable.notNull() )
{
m_centerLinePart = new cvf::Part;
m_centerLinePart->setName( "RivWellPathPartMgr::centerLinePart" );
m_centerLinePart->setDrawable( m_centerLineDrawable.p() );
caf::MeshEffectGenerator gen( m_rimWellPath->wellPathColor() );
cvf::ref<cvf::Effect> eff = gen.generateCachedEffect();
m_centerLinePart->setEffect( eff.p() );
}
// Generate label with well-path name at a position that is slightly offset towards the end of the well path
// This is to avoid overlap between well path laterals.
cvf::Vec3d textPosition = cvfCoords->get( 0 );
cvf::Vec3d tangent = ( cvfCoords->get( cvfCoords->size() - 1 ) - cvfCoords->get( 0 ) ).getNormalized();
textPosition.z() += 2.2 * characteristicCellSize;
textPosition += tangent * 2.2 * characteristicCellSize;
if ( wellPathCollection->showWellPathLabel() && m_rimWellPath->showWellPathLabel() && !m_rimWellPath->name().isEmpty() )
{
cvf::Font* font = RiaGuiApplication::instance()->defaultWellLabelFont();
cvf::ref<cvf::DrawableText> drawableText = new cvf::DrawableText;
drawableText->setFont( font );
drawableText->setCheckPosVisible( false );
drawableText->setDrawBorder( false );
drawableText->setDrawBackground( false );
drawableText->setVerticalAlignment( cvf::TextDrawer::CENTER );
drawableText->setTextColor( wellPathCollection->wellPathLabelColor() );
cvf::String cvfString = cvfqt::Utils::toString( m_rimWellPath->name() );
cvf::Vec3f textCoord( textPosition );
drawableText->addText( cvfString, textCoord );
cvf::ref<cvf::Part> part = new cvf::Part;
part->setName( "RivWellHeadPartMgr: text " + cvfString );
part->setDrawable( drawableText.p() );
cvf::ref<cvf::Effect> eff = new cvf::Effect;
part->setEffect( eff.p() );
part->setPriority( RivPartPriority::Text );
part->setSourceInfo( new RivTextLabelSourceInfo( m_rimWellPath, cvfString, textCoord ) );
m_wellLabelPart = part;
}
auto modeledWellPath = dynamic_cast<RimModeledWellPath*>( m_rimWellPath.p() );
if ( modeledWellPath )
{
bool showWellTargetSpheres = modeledWellPath->geometryDefinition()->showSpheres();
if ( showWellTargetSpheres )
{
auto geoDef = modeledWellPath->geometryDefinition();
auto sphereColor = geoDef->sphereColor();
double sphereRadiusFactor = geoDef->sphereRadiusFactor();
cvf::ref<cvf::Vec3fArray> vertices = new cvf::Vec3fArray;
cvf::ref<cvf::Vec3fArray> vecRes = new cvf::Vec3fArray;
cvf::ref<cvf::Color3fArray> colors = new cvf::Color3fArray;
auto wellTargets = geoDef->activeWellTargets();
size_t pointCount = wellTargets.size();
vertices->reserve( pointCount );
vecRes->reserve( pointCount );
colors->reserve( pointCount );
for ( const auto target : wellTargets )
{
auto domainCoord = target->targetPointXYZ() + modeledWellPath->geometryDefinition()->anchorPointXyz();
auto displayCoord = displayCoordTransform->transformToDisplayCoord( domainCoord );
vertices->add( cvf::Vec3f( displayCoord ) );
vecRes->add( cvf::Vec3f::X_AXIS );
colors->add( sphereColor );
}
cvf::ref<RivDrawableSpheres> vectorDrawable;
if ( RiaGuiApplication::instance()->useShaders() )
{
vectorDrawable = new RivDrawableSpheres( "u_transformationMatrix", "u_color" );
}
else
{
vectorDrawable = new RivDrawableSpheres();
}
vectorDrawable->setVectors( vertices.p(), vecRes.p() );
vectorDrawable->setColors( colors.p() );
double cellRadius = 15.0;
auto eclipseView = dynamic_cast<RimEclipseView*>( m_rimView.p() );
if ( eclipseView && eclipseView->mainGrid() )
{
double characteristicCellSize = eclipseView->mainGrid()->characteristicIJCellSize();
cellRadius = sphereRadiusFactor * characteristicCellSize;
}
cvf::GeometryBuilderTriangles builder;
cvf::GeometryUtils::createSphere( cellRadius, 15, 15, &builder );
vectorDrawable->setGlyph( builder.trianglesUShort().p(), builder.vertices().p() );
vectorDrawable->setRadius( cellRadius );
vectorDrawable->setCenterCoords( vertices.p() );
cvf::ref<cvf::Part> part = new cvf::Part;
part->setName( "RivWellPathPartMgr_WellTargetSpheres" );
part->setDrawable( vectorDrawable.p() );
part->setEffect( new cvf::Effect() );
auto sourceInfo = new RivObjectSourceInfo( geoDef );
part->setSourceInfo( sourceInfo );
m_spherePart = part;
}
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RivWellPathPartMgr::appendStaticGeometryPartsToModel( cvf::ModelBasicList* model,
const caf::DisplayCoordTransform* displayCoordTransform,
double characteristicCellSize,
const cvf::BoundingBox& wellPathClipBoundingBox )
{
if ( !isWellPathEnabled( wellPathClipBoundingBox ) ) return;
// The pipe geometry needs to be rebuilt on scale change to keep the pipes round
buildWellPathParts( displayCoordTransform, characteristicCellSize, wellPathClipBoundingBox, false );
if ( m_surfacePart.notNull() )
{
model->addPart( m_surfacePart.p() );
}
if ( m_centerLinePart.notNull() )
{
model->addPart( m_centerLinePart.p() );
}
if ( m_wellLabelPart.notNull() )
{
model->addPart( m_wellLabelPart.p() );
}
if ( m_spherePart.notNull() )
{
model->addPart( m_spherePart.p() );
}
appendFishboneSubsPartsToModel( model, displayCoordTransform, characteristicCellSize );
appendWellPathAttributesToModel( model, displayCoordTransform, characteristicCellSize );
appendWellIntegrityIntervalsToModel( model, displayCoordTransform, characteristicCellSize );
RimGridView* gridView = dynamic_cast<RimGridView*>( m_rimView.p() );
if ( gridView )
{
m_3dWellLogPlanePartMgr = new Riv3dWellLogPlanePartMgr( m_rimWellPath, gridView );
m_3dWellLogPlanePartMgr->appendPlaneToModel( model, displayCoordTransform, wellPathClipBoundingBox, true );
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RivWellPathPartMgr::appendFlattenedStaticGeometryPartsToModel( cvf::ModelBasicList* model,
const caf::DisplayCoordTransform* displayCoordTransform,
double characteristicCellSize,
const cvf::BoundingBox& wellPathClipBoundingBox )
{
if ( !isWellPathWithinBoundingBox( wellPathClipBoundingBox ) ) return;
// The pipe geometry needs to be rebuilt on scale change to keep the pipes round
buildWellPathParts( displayCoordTransform, characteristicCellSize, wellPathClipBoundingBox, true );
if ( m_surfacePart.notNull() )
{
model->addPart( m_surfacePart.p() );
}
if ( m_centerLinePart.notNull() )
{
model->addPart( m_centerLinePart.p() );
}
if ( m_wellLabelPart.notNull() )
{
model->addPart( m_wellLabelPart.p() );
}
/*
// TODO: Currently not supported.
// Require coordinate transformations of the spheres similar to RivWellPathPartMgr::buildWellPathParts
// https://github.com/OPM/ResInsight/issues/7891
//
if ( m_spherePart.notNull() )
{
model->addPart( m_spherePart.p() );
}
*/
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RivWellPathPartMgr::appendDynamicGeometryPartsToModel( cvf::ModelBasicList* model,
size_t timeStepIndex,
const caf::DisplayCoordTransform* displayCoordTransform,
double characteristicCellSize,
const cvf::BoundingBox& wellPathClipBoundingBox )
{
CVF_ASSERT( model );
bool showWellPath = isWellPathEnabled( wellPathClipBoundingBox );
if ( showWellPath )
{
// Only show perforations and virtual transmissibilities when well path is shown
appendPerforationsToModel( model, timeStepIndex, displayCoordTransform, characteristicCellSize, false );
appendVirtualTransmissibilitiesToModel( model, timeStepIndex, displayCoordTransform, characteristicCellSize );
}
// Well measurements visibility is independent of well path visibility
appendWellMeasurementsToModel( model, displayCoordTransform, characteristicCellSize );
if ( showWellPath )
{
RimGridView* gridView = dynamic_cast<RimGridView*>( m_rimView.p() );
if ( gridView )
{
if ( m_3dWellLogPlanePartMgr.isNull() )
{
m_3dWellLogPlanePartMgr = new Riv3dWellLogPlanePartMgr( m_rimWellPath, gridView );
}
m_3dWellLogPlanePartMgr->appendPlaneToModel( model, displayCoordTransform, wellPathClipBoundingBox, false );
}
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RivWellPathPartMgr::appendFlattenedDynamicGeometryPartsToModel( cvf::ModelBasicList* model,
size_t timeStepIndex,
const caf::DisplayCoordTransform* displayCoordTransform,
double characteristicCellSize,
const cvf::BoundingBox& wellPathClipBoundingBox )
{
CVF_ASSERT( model );
RimWellPathCollection* wellPathCollection = this->wellPathCollection();
if ( !wellPathCollection ) return;
if ( m_rimWellPath.isNull() ) return;
if ( !isWellPathWithinBoundingBox( wellPathClipBoundingBox ) ) return;
appendPerforationsToModel( model, timeStepIndex, displayCoordTransform, characteristicCellSize, true );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RivWellPathPartMgr::clearAllBranchData()
{
m_pipeGeomGenerator = nullptr;
m_surfacePart = nullptr;
m_surfaceDrawable = nullptr;
m_centerLinePart = nullptr;
m_centerLineDrawable = nullptr;
m_wellLabelPart = nullptr;
m_spherePart = nullptr;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RimWellPathCollection* RivWellPathPartMgr::wellPathCollection() const
{
if ( !m_rimWellPath ) return nullptr;
RimWellPathCollection* wellPathCollection = nullptr;
m_rimWellPath->firstAncestorOrThisOfType( wellPathCollection );
return wellPathCollection;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
double RivWellPathPartMgr::wellPathRadius( double characteristicCellSize, RimWellPathCollection* wellPathCollection )
{
return wellPathCollection->wellPathRadiusScaleFactor() * m_rimWellPath->wellPathRadiusScaleFactor() *
characteristicCellSize;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
double RivWellPathPartMgr::wellMeasurementRadius( double characteristicCellSize,
const RimWellPathCollection* wellPathCollection,
const RimWellMeasurementInView* wellMeasurementInView )
{
return wellPathCollection->wellPathRadiusScaleFactor() * wellMeasurementInView->radiusScaleFactor() *
characteristicCellSize;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RivWellPathPartMgr::appendWellIntegrityIntervalsToModel( cvf::ModelBasicList* model,
const caf::DisplayCoordTransform* displayCoordTransform,
double characteristicCellSize )
{
if ( !m_rimWellPath ) return;
RimWellPathCollection* wellPathCollection = this->wellPathCollection();
if ( !wellPathCollection ) return;
RigWellPath* wellPathGeometry = m_rimWellPath->wellPathGeometry();
if ( !wellPathGeometry ) return;
// Since we're using the index of measured depths to find the index of a point, ensure they're equal
CVF_ASSERT( wellPathGeometry->measuredDepths().size() == wellPathGeometry->wellPathPoints().size() );
double wellPathRadius = this->wellPathRadius( characteristicCellSize, wellPathCollection );
double wiaIntervalRadius = wellPathRadius * 1.15;
RivPipeGeometryGenerator geoGenerator;
for ( auto wiaModel : m_rimWellPath->wellIASettingsCollection()->settings() )
{
if ( !wiaModel->isChecked() ) continue;
if ( wiaModel->startMD() > wiaModel->endMD() ) continue;
double horizontalLengthAlongWellPath = 0.0;
std::vector<cvf::Vec3d> intervalCL;
{
std::pair<std::vector<cvf::Vec3d>, std::vector<double>> intervalCoordsAndMD =
wellPathGeometry->clippedPointSubset( wiaModel->startMD(), wiaModel->endMD(), &horizontalLengthAlongWellPath );
intervalCL = intervalCoordsAndMD.first;
}
if ( intervalCL.size() < 2 ) continue;
std::vector<cvf::Vec3d> intervalCLDisplayCS;
for ( cvf::Vec3d& point : intervalCL )
{
intervalCLDisplayCS.push_back( displayCoordTransform->transformToDisplayCoord( point ) );
}
cvf::ref<RivObjectSourceInfo> objectSourceInfo = new RivObjectSourceInfo( wiaModel );
cvf::Collection<cvf::Part> parts;
geoGenerator.cylinderWithCenterLineParts( &parts, intervalCLDisplayCS, cvf::Color3f::ORCHID, wiaIntervalRadius );
for ( auto& part : parts )
{
part->setSourceInfo( objectSourceInfo.p() );
model->addPart( part.p() );
}
if ( wiaModel->showBox() )
{
const auto& vertices = wiaModel->modelBoxVertices();
std::vector<cvf::Vec3f> transformedVertices;
for ( auto& v : vertices )
{
transformedVertices.push_back( cvf::Vec3f( displayCoordTransform->transformToDisplayCoord( v ) ) );
}
cvf::ref<cvf::Part> boxpart =
RivBoxGeometryGenerator::createBoxFromVertices( transformedVertices, cvf::Color3f::ORCHID );
boxpart->setSourceInfo( objectSourceInfo.p() );
model->addPart( boxpart.p() );
}
}
}