mirror of
https://github.com/OPM/ResInsight.git
synced 2025-02-25 18:55:39 -06:00
NB! This fix will update the location of well target at sea level, and can potentially change the MD from sea level to first user defined well target. This will affect completions defined for the modelled well, as they are defined by MD. * #9439 Make MD more predictable when toggling sea level target. * #9439 Avoid updating well path tangents when toggling sea level target * Disable IO of well target at sea level Co-authored-by: Magne Sjaastad <magne.sjaastad@ceetronsolutions.com>
854 lines
34 KiB
C++
854 lines
34 KiB
C++
/////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Copyright (C) 2018- 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 "RimWellPathGeometryDef.h"
|
|
|
|
#include "WellPathCommands/PointTangentManipulator/RicWellPathGeometry3dEditor.h"
|
|
#include "WellPathCommands/RicCreateWellTargetsPickEventHandler.h"
|
|
|
|
#include "RiaFieldHandleTools.h"
|
|
#include "RiaJCurveCalculator.h"
|
|
#include "RiaLogging.h"
|
|
#include "RiaOffshoreSphericalCoords.h"
|
|
#include "RiaPolyArcLineSampler.h"
|
|
#include "RiaSCurveCalculator.h"
|
|
|
|
#include "RigWellPath.h"
|
|
|
|
#include "RimModeledWellPath.h"
|
|
#include "RimProject.h"
|
|
#include "RimWellPathGeometryDefTools.h"
|
|
#include "RimWellPathTarget.h"
|
|
|
|
#include "RiuViewerCommands.h"
|
|
|
|
#include "cafCmdFeatureMenuBuilder.h"
|
|
#include "cafPdmFieldScriptingCapabilityCvfVec3d.h"
|
|
#include "cafPdmObjectScriptingCapability.h"
|
|
#include "cafPdmUiDoubleValueEditor.h"
|
|
#include "cafPdmUiLineEditor.h"
|
|
#include "cafPdmUiPushButtonEditor.h"
|
|
#include "cafPdmUiTableViewEditor.h"
|
|
#include "cafPdmUiTreeOrdering.h"
|
|
#include "cvfGeometryTools.h"
|
|
|
|
CAF_PDM_SOURCE_INIT( RimWellPathGeometryDef, "WellPathGeometryDef", "WellPathGeometry" );
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
RimWellPathGeometryDef::RimWellPathGeometryDef()
|
|
: changed( this )
|
|
, m_pickTargetsEventHandler( new RicCreateWellTargetsPickEventHandler( this ) )
|
|
{
|
|
CAF_PDM_InitScriptableObjectWithNameAndComment( "Well Targets",
|
|
":/WellTargets.png",
|
|
"",
|
|
"",
|
|
"WellPathGeometry",
|
|
"Class containing the geometry of a modeled Well Path" );
|
|
|
|
this->setUi3dEditorTypeName( RicWellPathGeometry3dEditor::uiEditorTypeName() );
|
|
CAF_PDM_InitScriptableFieldWithScriptKeyword( &m_referencePointUtmXyd,
|
|
"ReferencePosUtmXyd",
|
|
"ReferencePoint",
|
|
cvf::Vec3d( 0, 0, 0 ),
|
|
"UTM Reference Point" );
|
|
|
|
CAF_PDM_InitScriptableField( &m_airGap, "AirGap", 0.0, "Air Gap" );
|
|
m_airGap.uiCapability()->setUiEditorTypeName( caf::PdmUiDoubleValueEditor::uiEditorTypeName() );
|
|
|
|
CAF_PDM_InitScriptableField( &m_mdAtFirstTarget, "MdAtFirstTarget", 0.0, "MD at First Target" );
|
|
m_mdAtFirstTarget.uiCapability()->setUiEditorTypeName( caf::PdmUiDoubleValueEditor::uiEditorTypeName() );
|
|
|
|
CAF_PDM_InitScriptableFieldNoDefault( &m_wellTargets, "WellPathTargets", "Well Targets" );
|
|
m_wellTargets.uiCapability()->setUiEditorTypeName( caf::PdmUiTableViewEditor::uiEditorTypeName() );
|
|
m_wellTargets.uiCapability()->setUiTreeChildrenHidden( true );
|
|
m_wellTargets.uiCapability()->setUiLabelPosition( caf::PdmUiItemInfo::TOP );
|
|
m_wellTargets.uiCapability()->setCustomContextMenuEnabled( true );
|
|
|
|
CAF_PDM_InitField( &m_showAbsolutePosForWellTargets, "ShowAbsolutePosForWellTargets", false, "Show UTM Coords" );
|
|
CAF_PDM_InitField( &m_useTopLevelWellReferencePoint,
|
|
"UseTopLevelWellReferencePoint",
|
|
false,
|
|
"Use Top Level Well Reference Point" );
|
|
|
|
CAF_PDM_InitScriptableField( &m_useAutoGeneratedTargetAtSeaLevel,
|
|
"UseAutoGeneratedTargetAtSeaLevel",
|
|
true,
|
|
"Generate Target at Sea Level" );
|
|
|
|
CAF_PDM_InitScriptableField( &m_linkReferencePointUpdates, "LinkReferencePointUpdates", false, "Link Reference Point" );
|
|
|
|
CAF_PDM_InitScriptableFieldNoDefault( &m_autoTargetAtSeaLevel, "AutoGeneratedTarget", "Auto Generated Target" );
|
|
m_autoTargetAtSeaLevel = new RimWellPathTarget;
|
|
m_autoTargetAtSeaLevel->setEnabled( false );
|
|
|
|
// Disable IO was introduced to avoid errors related to unstable location of well target at sea level.
|
|
// https://github.com/OPM/ResInsight/issues/9439
|
|
m_autoTargetAtSeaLevel.xmlCapability()->disableIO();
|
|
|
|
CAF_PDM_InitScriptableField( &m_isAttachedToParentWell, "AttachedToParentWell", false, "Attached to Parent Well" );
|
|
CAF_PDM_InitFieldNoDefault( &m_fixedWellPathPoints, "FixedWellPathPoints", "" );
|
|
CAF_PDM_InitFieldNoDefault( &m_fixedMeasuredDepths, "FixedMeasuredDepths", "" );
|
|
|
|
CAF_PDM_InitField( &m_pickPointsEnabled, "m_pickPointsEnabled", false, "" );
|
|
caf::PdmUiPushButtonEditor::configureEditorForField( &m_pickPointsEnabled );
|
|
|
|
CAF_PDM_InitScriptableField( &m_showSpheres, "ShowSpheres", true, "Spheres" );
|
|
CAF_PDM_InitField( &m_sphereColor, "SphereColor", cvf::Color3f( cvf::Color3f::CEETRON ), "Sphere Color" );
|
|
CAF_PDM_InitField( &m_sphereRadiusFactor, "SphereRadiusFactor", 0.15, "Sphere Radius Factor" );
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
RimWellPathGeometryDef::~RimWellPathGeometryDef()
|
|
{
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
cvf::Vec3d RimWellPathGeometryDef::anchorPointXyz() const
|
|
{
|
|
cvf::Vec3d xyz( m_referencePointUtmXyd() );
|
|
xyz.z() = -xyz.z();
|
|
return xyz;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
cvf::Vec3d RimWellPathGeometryDef::anchorPointXyd() const
|
|
{
|
|
return m_referencePointUtmXyd();
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RimWellPathGeometryDef::setReferencePointXyz( const cvf::Vec3d& refPointXyz )
|
|
{
|
|
cvf::Vec3d xyd( refPointXyz );
|
|
xyd.z() = -xyd.z();
|
|
m_referencePointUtmXyd = xyd;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
bool RimWellPathGeometryDef::useReferencePointFromTopLevelWell() const
|
|
{
|
|
return m_useTopLevelWellReferencePoint;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RimWellPathGeometryDef::enableReferencePointFromTopLevelWell( bool enable )
|
|
{
|
|
m_useTopLevelWellReferencePoint = enable;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RimWellPathGeometryDef::enableLinkOfReferencePointUpdates( bool enable )
|
|
{
|
|
m_linkReferencePointUpdates = enable;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
bool RimWellPathGeometryDef::isReferencePointUpdatesLinked() const
|
|
{
|
|
return m_linkReferencePointUpdates;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
double RimWellPathGeometryDef::airGap() const
|
|
{
|
|
return m_airGap;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RimWellPathGeometryDef::setAirGap( double airGap )
|
|
{
|
|
m_airGap = airGap;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
double RimWellPathGeometryDef::mdAtFirstTarget() const
|
|
{
|
|
return m_mdAtFirstTarget;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RimWellPathGeometryDef::setMdAtFirstTarget( double md )
|
|
{
|
|
m_mdAtFirstTarget = md;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RimWellPathGeometryDef::setIsAttachedToParentWell( bool isAttached )
|
|
{
|
|
m_isAttachedToParentWell = isAttached;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RimWellPathGeometryDef::setFixedWellPathPoints( const std::vector<cvf::Vec3d>& points )
|
|
{
|
|
m_fixedWellPathPoints = points;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RimWellPathGeometryDef::setFixedMeasuredDepths( const std::vector<double>& mds )
|
|
{
|
|
m_fixedMeasuredDepths = mds;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
std::vector<RimWellPathTarget*> RimWellPathGeometryDef::createTargets( const std::vector<cvf::Vec3d>& points )
|
|
{
|
|
CAF_ASSERT( points.size() >= 2u );
|
|
|
|
std::vector<RimWellPathTarget*> appendedTargets;
|
|
|
|
for ( size_t i = 0; i < points.size(); ++i )
|
|
{
|
|
auto target = appendTarget();
|
|
target->setAsPointTargetXYZ( points[i] );
|
|
appendedTargets.push_back( target );
|
|
}
|
|
return appendedTargets;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
cvf::ref<RigWellPath> RimWellPathGeometryDef::createWellPathGeometry()
|
|
{
|
|
// User defined well targets can be updated from 3D view or from table. Always update location for well target at
|
|
// sea level.
|
|
updateTargetAtSeaLevel();
|
|
|
|
double offsetMd = m_useAutoGeneratedTargetAtSeaLevel ? 0.0 : m_mdAtFirstTarget;
|
|
return createWellPathGeometry( m_useAutoGeneratedTargetAtSeaLevel, offsetMd, true );
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
cvf::ref<RigWellPath> RimWellPathGeometryDef::createWellPathGeometry( bool useAutoGeneratedTargetAtSeaLevel,
|
|
double offsetMd,
|
|
bool updateTargets ) const
|
|
{
|
|
cvf::ref<RigWellPath> wellPathGeometry = new RigWellPath;
|
|
|
|
std::vector<cvf::Vec3d> wellPathPoints = m_fixedWellPathPoints;
|
|
std::vector<double> measuredDepths = m_fixedMeasuredDepths;
|
|
|
|
RiaLineArcWellPathCalculator wellPathCalculator =
|
|
lineArcWellPathCalculator( useAutoGeneratedTargetAtSeaLevel, updateTargets );
|
|
|
|
if ( wellPathCalculator.lineArcEndpoints().size() >= 2 )
|
|
{
|
|
RiaPolyArcLineSampler arcLineSampler( wellPathCalculator.startTangent(), wellPathCalculator.lineArcEndpoints() );
|
|
auto [sampledWellPathPoints, sampledMeasuredDepths] = arcLineSampler.sampledPointsAndMDs( 30, false );
|
|
wellPathPoints.insert( wellPathPoints.end(), sampledWellPathPoints.begin(), sampledWellPathPoints.end() );
|
|
|
|
double startMD = offsetMd;
|
|
if ( !measuredDepths.empty() )
|
|
{
|
|
startMD = measuredDepths.back();
|
|
}
|
|
|
|
for ( auto md : sampledMeasuredDepths )
|
|
{
|
|
measuredDepths.push_back( md + startMD );
|
|
}
|
|
}
|
|
wellPathGeometry->setWellPathPoints( wellPathPoints );
|
|
wellPathGeometry->setMeasuredDepths( measuredDepths );
|
|
|
|
if ( m_airGap != 0.0 )
|
|
{
|
|
wellPathGeometry->setDatumElevation( m_airGap );
|
|
}
|
|
|
|
return wellPathGeometry;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
std::vector<RiaWellPlanCalculator::WellPlanSegment> RimWellPathGeometryDef::wellPlan() const
|
|
{
|
|
RiaLineArcWellPathCalculator wellPathCalculator =
|
|
lineArcWellPathCalculator( m_useAutoGeneratedTargetAtSeaLevel, false );
|
|
|
|
RiaWellPlanCalculator wpCalc( wellPathCalculator.startTangent(), wellPathCalculator.lineArcEndpoints() );
|
|
|
|
return wpCalc.wellPlan();
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
bool RimWellPathGeometryDef::showSpheres() const
|
|
{
|
|
return m_showSpheres();
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
cvf::Color3f RimWellPathGeometryDef::sphereColor() const
|
|
{
|
|
return m_sphereColor();
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
double RimWellPathGeometryDef::sphereRadiusFactor() const
|
|
{
|
|
return m_sphereRadiusFactor();
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
bool RimWellPathGeometryDef::showAbsoluteCoordinates() const
|
|
{
|
|
return m_showAbsolutePosForWellTargets;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
std::pair<RimWellPathTarget*, RimWellPathTarget*>
|
|
RimWellPathGeometryDef::findActiveTargetsAroundInsertionPoint( const RimWellPathTarget* targetToInsertBefore )
|
|
{
|
|
RimWellPathTarget* before = nullptr;
|
|
RimWellPathTarget* after = nullptr;
|
|
|
|
bool foundTarget = false;
|
|
for ( const auto& wt : m_wellTargets )
|
|
{
|
|
if ( wt == targetToInsertBefore )
|
|
{
|
|
foundTarget = true;
|
|
}
|
|
|
|
if ( wt->isEnabled() && !after && foundTarget ) after = wt;
|
|
|
|
if ( wt->isEnabled() && !foundTarget ) before = wt;
|
|
}
|
|
|
|
return { before, after };
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RimWellPathGeometryDef::insertTarget( const RimWellPathTarget* targetToInsertBefore, RimWellPathTarget* targetToInsert )
|
|
{
|
|
size_t index = m_wellTargets.indexOf( targetToInsertBefore );
|
|
if ( index < m_wellTargets.size() )
|
|
m_wellTargets.insert( index, targetToInsert );
|
|
else
|
|
m_wellTargets.push_back( targetToInsert );
|
|
|
|
targetToInsert->moved.connect( this, &RimWellPathGeometryDef::onTargetMoved );
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RimWellPathGeometryDef::deleteTarget( RimWellPathTarget* targetTodelete )
|
|
{
|
|
m_wellTargets.removeChild( targetTodelete );
|
|
delete targetTodelete;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RimWellPathGeometryDef::deleteAllTargets()
|
|
{
|
|
m_wellTargets.deleteChildren();
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
RimWellPathTarget* RimWellPathGeometryDef::appendTarget()
|
|
{
|
|
RimWellPathTarget* wellPathTarget = nullptr;
|
|
|
|
auto targets = m_wellTargets.children();
|
|
if ( targets.empty() )
|
|
{
|
|
wellPathTarget = new RimWellPathTarget;
|
|
}
|
|
else
|
|
{
|
|
wellPathTarget = dynamic_cast<RimWellPathTarget*>(
|
|
targets.back()->xmlCapability()->copyByXmlSerialization( caf::PdmDefaultObjectFactory::instance() ) );
|
|
}
|
|
|
|
if ( wellPathTarget )
|
|
{
|
|
m_wellTargets.push_back( wellPathTarget );
|
|
}
|
|
wellPathTarget->moved.connect( this, &RimWellPathGeometryDef::onTargetMoved );
|
|
return wellPathTarget;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
const RimWellPathTarget* RimWellPathGeometryDef::firstActiveTarget() const
|
|
{
|
|
for ( const RimWellPathTarget* target : m_wellTargets )
|
|
{
|
|
if ( target->isEnabled() )
|
|
{
|
|
return target;
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
const RimWellPathTarget* RimWellPathGeometryDef::lastActiveTarget() const
|
|
{
|
|
if ( !m_wellTargets.size() ) return nullptr;
|
|
|
|
for ( int tIdx = static_cast<int>( m_wellTargets.size() - 1 ); tIdx >= 0; --tIdx )
|
|
{
|
|
if ( m_wellTargets[tIdx]->isEnabled() )
|
|
{
|
|
return m_wellTargets[tIdx];
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RimWellPathGeometryDef::enableTargetPointPicking( bool isEnabling )
|
|
{
|
|
m_pickPointsEnabled = isEnabling;
|
|
this->updateConnectedEditors();
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RimWellPathGeometryDef::updateWellPathVisualization( bool fullUpdate )
|
|
{
|
|
changed.send( fullUpdate );
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
QList<caf::PdmOptionItemInfo> RimWellPathGeometryDef::calculateValueOptions( const caf::PdmFieldHandle* fieldNeedingOptions )
|
|
{
|
|
QList<caf::PdmOptionItemInfo> options;
|
|
|
|
return options;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RimWellPathGeometryDef::fieldChangedByUi( const caf::PdmFieldHandle* changedField,
|
|
const QVariant& oldValue,
|
|
const QVariant& newValue )
|
|
{
|
|
if ( changedField == &m_pickPointsEnabled )
|
|
{
|
|
this->updateConnectedEditors();
|
|
}
|
|
else if ( changedField == &m_referencePointUtmXyd )
|
|
{
|
|
if ( isReferencePointUpdatesLinked() )
|
|
{
|
|
auto linkedDefs = RimWellPathGeometryDefTools::linkedDefinitions();
|
|
|
|
// Remove this to avoid duplicate updates
|
|
linkedDefs.erase( std::remove_if( linkedDefs.begin(),
|
|
linkedDefs.end(),
|
|
[this]( auto const& geoDef ) { return geoDef == this; } ),
|
|
linkedDefs.end() );
|
|
|
|
cvf::Vec3d oldPos;
|
|
caf::PdmValueFieldSpecialization<cvf::Vec3d>::setFromVariant( oldValue, oldPos );
|
|
|
|
auto delta = m_referencePointUtmXyd() - oldPos;
|
|
|
|
RimWellPathGeometryDefTools::updateLinkedGeometryDefinitions( linkedDefs, delta );
|
|
}
|
|
}
|
|
else if ( changedField == &m_useAutoGeneratedTargetAtSeaLevel )
|
|
{
|
|
updateTargetAtSeaLevel();
|
|
|
|
if ( !m_useAutoGeneratedTargetAtSeaLevel() )
|
|
{
|
|
auto firstTarget = firstActiveTarget();
|
|
if ( firstTarget != nullptr )
|
|
{
|
|
auto firstLocationXYZ = firstTarget->targetPointXYZ() + anchorPointXyz();
|
|
|
|
// Temporarily enable target at sea level to be able to create a complete geometry to find MD of first
|
|
// target of the complete geometry
|
|
auto wellPathGeo = createWellPathGeometry( true, 0.0, false );
|
|
|
|
double mdAtFirstTarget = wellPathGeo->closestMeasuredDepth( firstLocationXYZ );
|
|
m_mdAtFirstTarget = mdAtFirstTarget;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
m_mdAtFirstTarget = 0.0;
|
|
}
|
|
}
|
|
|
|
changed.send( false );
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RimWellPathGeometryDef::defineUiOrdering( QString uiConfigName, caf::PdmUiOrdering& uiOrdering )
|
|
{
|
|
uiOrdering.add( &m_referencePointUtmXyd );
|
|
m_referencePointUtmXyd.uiCapability()->setUiReadOnly( m_useTopLevelWellReferencePoint );
|
|
|
|
if ( !m_isAttachedToParentWell )
|
|
{
|
|
uiOrdering.add( &m_linkReferencePointUpdates );
|
|
uiOrdering.add( &m_airGap );
|
|
uiOrdering.add( &m_mdAtFirstTarget );
|
|
uiOrdering.add( &m_useAutoGeneratedTargetAtSeaLevel );
|
|
}
|
|
else
|
|
{
|
|
uiOrdering.add( &m_useTopLevelWellReferencePoint );
|
|
}
|
|
|
|
auto group = uiOrdering.addNewGroup( "Well Target Appearance" );
|
|
group->add( &m_showSpheres );
|
|
group->add( &m_sphereColor );
|
|
group->add( &m_sphereRadiusFactor );
|
|
|
|
uiOrdering.add( &m_showAbsolutePosForWellTargets );
|
|
uiOrdering.add( &m_wellTargets );
|
|
uiOrdering.add( &m_pickPointsEnabled );
|
|
|
|
m_mdAtFirstTarget.uiCapability()->setUiReadOnly( m_useAutoGeneratedTargetAtSeaLevel() );
|
|
|
|
uiOrdering.skipRemainingFields( true );
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RimWellPathGeometryDef::defineUiTreeOrdering( caf::PdmUiTreeOrdering& uiTreeOrdering, QString uiConfigName )
|
|
{
|
|
uiTreeOrdering.skipRemainingChildren( true );
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
std::vector<RimWellPathTarget*> RimWellPathGeometryDef::activeWellTargets() const
|
|
{
|
|
return activeWellTargets( m_useAutoGeneratedTargetAtSeaLevel );
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
std::vector<RimWellPathTarget*> RimWellPathGeometryDef::activeWellTargets( bool useAutoGeneratedTargetAtSeaLevel ) const
|
|
{
|
|
std::vector<RimWellPathTarget*> active;
|
|
|
|
if ( useAutoGeneratedTargetAtSeaLevel && !m_wellTargets.empty() && m_autoTargetAtSeaLevel )
|
|
{
|
|
active.push_back( m_autoTargetAtSeaLevel );
|
|
}
|
|
|
|
for ( const auto& wt : m_wellTargets )
|
|
{
|
|
if ( wt->isEnabled() )
|
|
{
|
|
active.push_back( wt );
|
|
}
|
|
}
|
|
|
|
return active;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
RiaLineArcWellPathCalculator RimWellPathGeometryDef::lineArcWellPathCalculator( bool useAutoGeneratedTargetAtSeaLevel,
|
|
bool updateTargets ) const
|
|
{
|
|
std::vector<RimWellPathTarget*> activeTargets;
|
|
|
|
auto candidates = activeWellTargets( useAutoGeneratedTargetAtSeaLevel );
|
|
if ( !candidates.empty() )
|
|
{
|
|
activeTargets.push_back( candidates.front() );
|
|
RimWellPathTarget* previousTarget = candidates.front();
|
|
|
|
for ( size_t i = 1; i < candidates.size(); i++ )
|
|
{
|
|
auto candidate = candidates[i];
|
|
if ( previousTarget && ( previousTarget->targetPointXYZ() - candidate->targetPointXYZ() ).length() > 1e-3 )
|
|
{
|
|
activeTargets.push_back( candidate );
|
|
previousTarget = candidate;
|
|
}
|
|
}
|
|
}
|
|
|
|
std::vector<RiaLineArcWellPathCalculator::WellTarget> targetDatas;
|
|
for ( auto wellTarget : activeTargets )
|
|
{
|
|
targetDatas.push_back( wellTarget->wellTargetData() );
|
|
}
|
|
|
|
RiaLineArcWellPathCalculator wellPathCalculator( anchorPointXyz(), targetDatas );
|
|
const std::vector<RiaLineArcWellPathCalculator::WellTargetStatus>& targetStatuses =
|
|
wellPathCalculator.targetStatuses();
|
|
|
|
if ( updateTargets )
|
|
{
|
|
for ( size_t tIdx = 0; tIdx < activeTargets.size(); ++tIdx )
|
|
{
|
|
activeTargets[tIdx]->setDerivedTangent( targetStatuses[tIdx].resultAzimuthRadians,
|
|
targetStatuses[tIdx].resultInclinationRadians );
|
|
|
|
activeTargets[tIdx]->setRadius1Data( targetStatuses[tIdx].isRadius1Editable,
|
|
targetStatuses[tIdx].hasOverriddenRadius1,
|
|
targetStatuses[tIdx].resultRadius1 );
|
|
|
|
activeTargets[tIdx]->setRadius2Data( targetStatuses[tIdx].isRadius2Editable,
|
|
targetStatuses[tIdx].hasOverriddenRadius2,
|
|
targetStatuses[tIdx].resultRadius2 );
|
|
}
|
|
}
|
|
|
|
return wellPathCalculator;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RimWellPathGeometryDef::updateTargetAtSeaLevel()
|
|
{
|
|
if ( m_useAutoGeneratedTargetAtSeaLevel && !m_wellTargets.empty() )
|
|
{
|
|
// Update tangents for existing well targets without the target at sea level.
|
|
bool useAutoGeneratedTargetAtSeaLevel = false;
|
|
double offsetMd = 0.0;
|
|
bool updateTargets = true;
|
|
createWellPathGeometry( useAutoGeneratedTargetAtSeaLevel, offsetMd, updateTargets );
|
|
|
|
cvf::Vec3d newPos = cvf::Vec3d::ZERO;
|
|
|
|
auto firstTarget = m_wellTargets[0];
|
|
|
|
cvf::Vec3d targetTangent = firstTarget->tangent();
|
|
double radius = firstTarget->radius1();
|
|
|
|
cvf::Vec3d tangentInHorizontalPlane = targetTangent;
|
|
tangentInHorizontalPlane[2] = 0.0;
|
|
tangentInHorizontalPlane.normalize();
|
|
|
|
RiaOffshoreSphericalCoords sphTangent( targetTangent );
|
|
double inc = sphTangent.inc();
|
|
double horizontalLengthFromTarget = radius - radius * cvf::Math::cos( inc );
|
|
|
|
newPos = firstTarget->targetPointXYZ() - horizontalLengthFromTarget * tangentInHorizontalPlane;
|
|
newPos.z() = -anchorPointXyz().z();
|
|
|
|
m_autoTargetAtSeaLevel->setAsPointXYZAndTangentTarget( { newPos[0], newPos[1], newPos[2] }, 0, 0 );
|
|
m_autoTargetAtSeaLevel->setEnabled( true );
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RimWellPathGeometryDef::defineCustomContextMenu( const caf::PdmFieldHandle* fieldNeedingMenu,
|
|
QMenu* menu,
|
|
QWidget* fieldEditorWidget )
|
|
{
|
|
caf::CmdFeatureMenuBuilder menuBuilder;
|
|
|
|
menuBuilder << "RicNewWellPathListTargetFeature";
|
|
menuBuilder << "Separator";
|
|
menuBuilder << "RicDeleteWellPathTargetFeature";
|
|
|
|
menuBuilder.appendToMenu( menu );
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RimWellPathGeometryDef::defineEditorAttribute( const caf::PdmFieldHandle* field,
|
|
QString uiConfigName,
|
|
caf::PdmUiEditorAttribute* attribute )
|
|
{
|
|
if ( field == &m_pickPointsEnabled )
|
|
{
|
|
caf::PdmUiPushButtonEditorAttribute* pbAttribute = dynamic_cast<caf::PdmUiPushButtonEditorAttribute*>( attribute );
|
|
if ( pbAttribute )
|
|
{
|
|
if ( !m_pickPointsEnabled )
|
|
{
|
|
pbAttribute->m_buttonText = "Start Picking Targets";
|
|
}
|
|
else
|
|
{
|
|
pbAttribute->m_buttonText = "Stop Picking Targets";
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( field == &m_wellTargets )
|
|
{
|
|
auto tvAttribute = dynamic_cast<caf::PdmUiTableViewEditorAttribute*>( attribute );
|
|
if ( tvAttribute )
|
|
{
|
|
tvAttribute->resizePolicy = caf::PdmUiTableViewEditorAttribute::RESIZE_TO_FIT_CONTENT;
|
|
tvAttribute->heightHint = 1000;
|
|
|
|
if ( m_pickPointsEnabled )
|
|
{
|
|
tvAttribute->baseColor.setRgb( 255, 220, 255 );
|
|
tvAttribute->alwaysEnforceResizePolicy = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( field == &m_referencePointUtmXyd )
|
|
{
|
|
auto uiDisplayStringAttr = dynamic_cast<caf::PdmUiLineEditorAttributeUiDisplayString*>( attribute );
|
|
|
|
if ( uiDisplayStringAttr )
|
|
{
|
|
uiDisplayStringAttr->m_displayString = QString::number( m_referencePointUtmXyd()[0], 'f', 2 ) + " " +
|
|
QString::number( m_referencePointUtmXyd()[1], 'f', 2 ) + " " +
|
|
QString::number( m_referencePointUtmXyd()[2], 'f', 2 );
|
|
}
|
|
}
|
|
|
|
if ( field == &m_airGap )
|
|
{
|
|
auto uiDoubleValueEditorAttr = dynamic_cast<caf::PdmUiDoubleValueEditorAttribute*>( attribute );
|
|
if ( uiDoubleValueEditorAttr )
|
|
{
|
|
uiDoubleValueEditorAttr->m_decimals = 2;
|
|
uiDoubleValueEditorAttr->m_validator = new QDoubleValidator( 0.0, std::numeric_limits<double>::max(), 2 );
|
|
}
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RimWellPathGeometryDef::defineObjectEditorAttribute( QString uiConfigName, caf::PdmUiEditorAttribute* attribute )
|
|
{
|
|
RicWellPathGeometry3dEditorAttribute* attrib = dynamic_cast<RicWellPathGeometry3dEditorAttribute*>( attribute );
|
|
if ( attrib )
|
|
{
|
|
attrib->pickEventHandler = m_pickTargetsEventHandler;
|
|
attrib->enablePicking = m_pickPointsEnabled;
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
caf::PdmFieldHandle* RimWellPathGeometryDef::objectToggleField()
|
|
{
|
|
return &m_showSpheres;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RimWellPathGeometryDef::onTargetMoved( const caf::SignalEmitter* emitter, bool fullUpdate )
|
|
{
|
|
updateWellPathVisualization( fullUpdate );
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RimWellPathGeometryDef::initAfterRead()
|
|
{
|
|
if ( RimProject::current()->isProjectFileVersionEqualOrOlderThan( "2019.12.1" ) )
|
|
{
|
|
m_useAutoGeneratedTargetAtSeaLevel = false;
|
|
}
|
|
|
|
for ( auto wt : m_wellTargets )
|
|
{
|
|
if ( wt ) wt->moved.connect( this, &RimWellPathGeometryDef::onTargetMoved );
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RimWellPathGeometryDef::setUseAutoGeneratedTargetAtSeaLevel( bool autoGenerate )
|
|
{
|
|
m_useAutoGeneratedTargetAtSeaLevel = autoGenerate;
|
|
}
|