2017-05-29 15:39:37 +02:00
/////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2017 Statoil ASA
//
// ResInsight is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// ResInsight is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE.
//
// See the GNU General Public License at <http://www.gnu.org/licenses/gpl.html>
// for more details.
//
/////////////////////////////////////////////////////////////////////////////////
#include "RicExportFishbonesWellSegmentsFeature.h"
#include "RiaApplication.h"
#include "RiaLogging.h"
2017-06-28 10:13:59 +02:00
#include "RicExportFeatureImpl.h"
2017-05-29 15:39:37 +02:00
#include "RimProject.h"
#include "RimFishboneWellPathCollection.h"
#include "RimFishbonesCollection.h"
#include "RimFishbonesMultipleSubs.h"
#include "RimWellPath.h"
#include "RimEclipseCase.h"
#include "RigMainGrid.h"
#include "RigEclipseCaseData.h"
2017-06-02 15:37:58 +02:00
#include "RigWellPath.h"
2017-05-29 15:39:37 +02:00
2018-02-27 16:37:06 +01:00
#include "Riu3DMainWindowTools.h"
2017-05-29 15:39:37 +02:00
#include "cafSelectionManager.h"
#include "cafPdmUiPropertyViewDialog.h"
2017-08-29 09:39:33 +02:00
#include "cafUtils.h"
2017-05-29 15:39:37 +02:00
2017-06-02 12:50:16 +02:00
#include "cvfMath.h"
2017-05-29 15:39:37 +02:00
#include <QAction>
#include <QDir>
CAF_CMD_SOURCE_INIT ( RicExportFishbonesWellSegmentsFeature , "RicExportFishbonesWellSegmentsFeature" );
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RicExportFishbonesWellSegmentsFeature :: onActionTriggered ( bool isChecked )
{
RimFishbonesCollection * fishbonesCollection = selectedFishbonesCollection ();
RimWellPath * wellPath = selectedWellPath ();
CVF_ASSERT ( fishbonesCollection );
CVF_ASSERT ( wellPath );
RiaApplication * app = RiaApplication :: instance ();
QString projectFolder = app -> currentProjectPath ();
QString defaultDir = RiaApplication :: instance () -> lastUsedDialogDirectoryWithFallback ( "COMPLETIONS" , projectFolder );
2017-06-28 20:51:16 +02:00
RicCaseAndFileExportSettingsUi exportSettings ;
2017-05-29 15:39:37 +02:00
std :: vector < RimCase *> cases ;
app -> project () -> allCases ( cases );
for ( auto c : cases )
{
RimEclipseCase * eclipseCase = dynamic_cast < RimEclipseCase *> ( c );
if ( eclipseCase != nullptr )
{
exportSettings . caseToApply = eclipseCase ;
break ;
}
}
2017-06-15 11:21:37 +02:00
exportSettings . folder = defaultDir ;
2017-05-29 15:39:37 +02:00
2018-02-27 16:37:06 +01:00
caf :: PdmUiPropertyViewDialog propertyDialog ( Riu3DMainWindowTools :: mainWindowWidget (), & exportSettings , "Export Well Segments" , "" );
2017-06-28 10:13:59 +02:00
RicExportFeatureImpl :: configureForExport ( & propertyDialog );
2017-05-29 15:39:37 +02:00
if ( propertyDialog . exec () == QDialog :: Accepted )
{
2017-06-15 11:21:37 +02:00
RiaApplication :: instance () -> setLastUsedDialogDirectory ( "COMPLETIONS" , QFileInfo ( exportSettings . folder ). absolutePath ());
2017-05-29 15:39:37 +02:00
std :: vector < RimFishbonesMultipleSubs *> fishbonesSubs ;
for ( RimFishbonesMultipleSubs * subs : fishbonesCollection -> fishbonesSubs )
{
fishbonesSubs . push_back ( subs );
}
exportWellSegments ( wellPath , fishbonesSubs , exportSettings );
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RimFishbonesCollection * RicExportFishbonesWellSegmentsFeature :: selectedFishbonesCollection ()
{
RimFishbonesCollection * objToFind = nullptr ;
caf :: PdmUiItem * pdmUiItem = caf :: SelectionManager :: instance () -> selectedItem ();
caf :: PdmObjectHandle * objHandle = dynamic_cast < caf :: PdmObjectHandle *> ( pdmUiItem );
if ( objHandle )
{
objHandle -> firstAncestorOrThisOfType ( objToFind );
}
return objToFind ;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RimWellPath * RicExportFishbonesWellSegmentsFeature :: selectedWellPath ()
{
RimWellPath * objToFind = nullptr ;
caf :: PdmUiItem * pdmUiItem = caf :: SelectionManager :: instance () -> selectedItem ();
caf :: PdmObjectHandle * objHandle = dynamic_cast < caf :: PdmObjectHandle *> ( pdmUiItem );
if ( objHandle )
{
objHandle -> firstAncestorOrThisOfType ( objToFind );
}
return objToFind ;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RicExportFishbonesWellSegmentsFeature :: setupActionLook ( QAction * actionToSetup )
{
actionToSetup -> setText ( "Export Well Segments" );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
bool RicExportFishbonesWellSegmentsFeature :: isCommandEnabled ()
{
if ( selectedFishbonesCollection ())
{
return true ;
}
return false ;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
2017-06-28 20:51:16 +02:00
void RicExportFishbonesWellSegmentsFeature :: exportWellSegments ( const RimWellPath * wellPath , const std :: vector < RimFishbonesMultipleSubs *>& fishbonesSubs , const RicCaseAndFileExportSettingsUi & settings )
2017-05-29 15:39:37 +02:00
{
if ( settings . caseToApply () == nullptr )
{
RiaLogging :: error ( "Export Well Segments: Cannot export completions data without specified eclipse case" );
return ;
}
2017-08-29 09:39:33 +02:00
QString fileName = QString ( "%1-Welsegs" ). arg ( settings . caseToApply () -> caseUserDescription ());
fileName = caf :: Utils :: makeValidFileBasename ( fileName );
QString filePath = QDir ( settings . folder ()). filePath ( fileName );
QFile exportFile ( filePath );
2017-05-29 15:39:37 +02:00
if ( ! exportFile . open ( QIODevice :: WriteOnly ))
{
2017-06-15 11:21:37 +02:00
RiaLogging :: error ( QString ( "Export Well Segments: Could not open the file: %1" ). arg ( filePath ));
2017-05-29 15:39:37 +02:00
return ;
}
2018-01-31 07:42:19 +01:00
std :: vector < WellSegmentLocation > locations = RicWellPathExportCompletionDataFeatureImpl :: findWellSegmentLocations ( settings . caseToApply , wellPath , fishbonesSubs );
2017-05-29 15:39:37 +02:00
QTextStream stream ( & exportFile );
2017-05-30 15:00:14 +02:00
RifEclipseDataTableFormatter formatter ( stream );
2017-05-29 15:39:37 +02:00
generateWelsegsTable ( formatter , wellPath , settings , locations );
generateCompsegsTable ( formatter , wellPath , settings , locations );
2017-06-02 12:50:16 +02:00
generateWsegvalvTable ( formatter , wellPath , settings , locations );
2017-05-29 15:39:37 +02:00
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
2017-06-02 12:50:16 +02:00
void RicExportFishbonesWellSegmentsFeature :: generateWelsegsTable ( RifEclipseDataTableFormatter & formatter ,
const RimWellPath * wellPath ,
2017-06-28 20:51:16 +02:00
const RicCaseAndFileExportSettingsUi & settings ,
2017-06-02 12:50:16 +02:00
const std :: vector < WellSegmentLocation >& locations )
2017-05-29 15:39:37 +02:00
{
2017-06-20 10:40:39 +02:00
RiaEclipseUnitTools :: UnitSystem unitSystem = settings . caseToApply -> eclipseCaseData () -> unitsType ();
2017-05-29 15:39:37 +02:00
formatter . keyword ( "WELSEGS" );
2017-06-02 15:37:58 +02:00
double startMD = wellPath -> fishbonesCollection () -> startMD ();
double startTVD = - wellPath -> wellPathGeometry () -> interpolatedPointAlongWellPath ( startMD ). z ();
2017-05-29 15:39:37 +02:00
{
std :: vector < RifEclipseOutputTableColumn > header = {
RifEclipseOutputTableColumn ( "Name" ),
RifEclipseOutputTableColumn ( "Dep 1" ),
RifEclipseOutputTableColumn ( "Tlen 1" ),
RifEclipseOutputTableColumn ( "Vol 1" ),
RifEclipseOutputTableColumn ( "Len&Dep" ),
RifEclipseOutputTableColumn ( "PresDrop" ),
};
formatter . header ( header );
formatter . add ( wellPath -> name ());
2017-06-02 15:37:58 +02:00
formatter . add ( startTVD );
formatter . add ( startMD );
2017-05-29 15:39:37 +02:00
formatter . add ( "1*" );
2017-06-28 20:51:16 +02:00
formatter . add ( wellPath -> fishbonesCollection () -> lengthAndDepth (). text ());
formatter . add ( wellPath -> fishbonesCollection () -> pressureDrop (). text ());
2017-05-29 15:39:37 +02:00
formatter . rowCompleted ();
}
{
std :: vector < RifEclipseOutputTableColumn > header = {
RifEclipseOutputTableColumn ( "First Seg" ),
RifEclipseOutputTableColumn ( "Last Seg" ),
RifEclipseOutputTableColumn ( "Branch Num" ),
RifEclipseOutputTableColumn ( "Outlet Seg" ),
RifEclipseOutputTableColumn ( "Length" ),
RifEclipseOutputTableColumn ( "Depth Change" ),
RifEclipseOutputTableColumn ( "Diam" ),
RifEclipseOutputTableColumn ( "Rough" ),
};
formatter . header ( header );
}
{
formatter . comment ( "Main stem" );
double depth = 0 ;
double length = 0 ;
2017-06-02 15:37:58 +02:00
double previousMD = startMD ;
double previousTVD = startTVD ;
2017-05-29 15:39:37 +02:00
2017-06-02 15:37:58 +02:00
for ( const WellSegmentLocation & location : locations )
2017-05-29 15:39:37 +02:00
{
2017-06-28 20:51:16 +02:00
if ( wellPath -> fishbonesCollection () -> lengthAndDepth () == RimFishbonesCollection :: INC )
2017-05-29 15:39:37 +02:00
{
2017-06-02 15:37:58 +02:00
depth = location . trueVerticalDepth - previousTVD ;
length = location . fishbonesSubs -> measuredDepth ( location . subIndex ) - previousMD ;
2017-05-29 15:39:37 +02:00
}
else
{
2017-06-02 15:37:58 +02:00
depth += location . trueVerticalDepth - previousTVD ;
length += location . fishbonesSubs -> measuredDepth ( location . subIndex ) - previousMD ;
2017-05-29 15:39:37 +02:00
}
formatter . comment ( QString ( "Segment for sub %1" ). arg ( location . subIndex ));
formatter . add ( location . segmentNumber ). add ( location . segmentNumber );
formatter . add ( 1 ); // All segments on main stem are branch 1
formatter . add ( location . segmentNumber - 1 ); // All main stem segments are connected to the segment below them
formatter . add ( length );
formatter . add ( depth );
2017-06-26 11:45:20 +02:00
formatter . add ( wellPath -> fishbonesCollection () -> linerDiameter ( unitSystem ));
2017-06-20 10:40:39 +02:00
formatter . add ( wellPath -> fishbonesCollection () -> roughnessFactor ( unitSystem ));
2017-05-29 15:39:37 +02:00
formatter . rowCompleted ();
2017-06-02 15:37:58 +02:00
previousMD = location . measuredDepth ;
previousTVD = location . trueVerticalDepth ;
2017-05-29 15:39:37 +02:00
}
}
{
formatter . comment ( "Laterals" );
formatter . comment ( "Diam: MSW - Tubing Radius" );
formatter . comment ( "Rough: MSW - Open Hole Roughness Factor" );
for ( const WellSegmentLocation & location : locations )
{
2017-06-02 12:50:16 +02:00
formatter . comment ( "ICD" );
formatter . add ( location . icdSegmentNumber ). add ( location . icdSegmentNumber );
formatter . add ( location . icdBranchNumber );
formatter . add ( location . segmentNumber );
formatter . add ( 0.1 ); // ICDs have 0.1 length
formatter . add ( 0 ); // Depth change
2017-06-26 11:45:20 +02:00
formatter . add ( wellPath -> fishbonesCollection () -> linerDiameter ( unitSystem ));
2017-06-20 10:40:39 +02:00
formatter . add ( wellPath -> fishbonesCollection () -> roughnessFactor ( unitSystem ));
2017-06-02 12:50:16 +02:00
formatter . rowCompleted ();
2017-05-29 15:39:37 +02:00
for ( const WellSegmentLateral & lateral : location . laterals )
{
2017-12-19 11:00:26 +01:00
formatter . comment ( QString ( "%1 : Sub index %2 - Lateral %3" ). arg ( location . fishbonesSubs -> generatedName ()). arg ( location . subIndex ). arg ( lateral . lateralIndex ));
2017-05-29 15:39:37 +02:00
double depth = 0 ;
double length = 0 ;
for ( const WellSegmentLateralIntersection & intersection : lateral . intersections )
{
2017-06-28 20:51:16 +02:00
if ( wellPath -> fishbonesCollection () -> lengthAndDepth () == RimFishbonesCollection :: INC )
2017-05-29 15:39:37 +02:00
{
2017-12-12 12:47:05 +01:00
depth = intersection . tvdChangeFromPreviousIntersection ;
length = intersection . mdFromPreviousIntersection ;
2017-05-29 15:39:37 +02:00
}
else
{
2017-12-12 12:47:05 +01:00
depth += intersection . tvdChangeFromPreviousIntersection ;
length += intersection . mdFromPreviousIntersection ;
2017-05-29 15:39:37 +02:00
}
2017-06-20 10:40:39 +02:00
double diameter = computeEffectiveDiameter ( location . fishbonesSubs -> tubingDiameter ( unitSystem ), location . fishbonesSubs -> holeDiameter ( unitSystem ));
2017-05-29 15:39:37 +02:00
formatter . add ( intersection . segmentNumber );
formatter . add ( intersection . segmentNumber );
formatter . add ( lateral . branchNumber );
formatter . add ( intersection . attachedSegmentNumber );
formatter . add ( length );
formatter . add ( depth );
2017-06-09 12:08:15 +02:00
formatter . add ( diameter );
2017-06-20 10:40:39 +02:00
formatter . add ( location . fishbonesSubs -> openHoleRoughnessFactor ( unitSystem ));
2017-05-29 15:39:37 +02:00
formatter . rowCompleted ();
}
}
}
}
formatter . tableCompleted ();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
2017-06-02 12:50:16 +02:00
void RicExportFishbonesWellSegmentsFeature :: generateCompsegsTable ( RifEclipseDataTableFormatter & formatter ,
const RimWellPath * wellPath ,
2017-06-28 20:51:16 +02:00
const RicCaseAndFileExportSettingsUi & settings ,
2017-06-02 12:50:16 +02:00
const std :: vector < WellSegmentLocation >& locations )
2017-05-29 15:39:37 +02:00
{
RigMainGrid * grid = settings . caseToApply -> eclipseCaseData () -> mainGrid ();
formatter . keyword ( "COMPSEGS" );
{
std :: vector < RifEclipseOutputTableColumn > header = {
RifEclipseOutputTableColumn ( "Name" )
};
formatter . header ( header );
formatter . add ( wellPath -> name ());
formatter . rowCompleted ();
}
{
std :: vector < RifEclipseOutputTableColumn > header = {
RifEclipseOutputTableColumn ( "I" ),
RifEclipseOutputTableColumn ( "J" ),
RifEclipseOutputTableColumn ( "K" ),
RifEclipseOutputTableColumn ( "Branch no" ),
RifEclipseOutputTableColumn ( "Start Length" ),
RifEclipseOutputTableColumn ( "End Length" ),
RifEclipseOutputTableColumn ( "Dir Pen" ),
RifEclipseOutputTableColumn ( "End Range" ),
RifEclipseOutputTableColumn ( "Connection Depth" )
};
formatter . header ( header );
}
for ( const WellSegmentLocation & location : locations )
{
for ( const WellSegmentLateral & lateral : location . laterals )
{
2017-06-02 11:42:41 +02:00
double aggregatedLength = 0 ;
2017-05-29 15:39:37 +02:00
for ( const WellSegmentLateralIntersection & intersection : lateral . intersections )
{
size_t i , j , k ;
2018-01-29 15:27:17 +01:00
grid -> ijkFromCellIndex ( intersection . globalCellIndex , & i , & j , & k );
2017-05-29 15:39:37 +02:00
formatter . addZeroBasedCellIndex ( i ). addZeroBasedCellIndex ( j ). addZeroBasedCellIndex ( k );
formatter . add ( lateral . branchNumber );
2017-06-02 11:42:41 +02:00
formatter . add ( aggregatedLength );
2017-12-12 12:47:05 +01:00
formatter . add ( aggregatedLength + intersection . mdFromPreviousIntersection );
2017-05-29 15:39:37 +02:00
formatter . rowCompleted ();
2017-06-02 11:42:41 +02:00
2017-12-12 12:47:05 +01:00
aggregatedLength += intersection . mdFromPreviousIntersection ;
2017-05-29 15:39:37 +02:00
}
}
}
formatter . tableCompleted ();
}
2017-06-02 12:50:16 +02:00
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RicExportFishbonesWellSegmentsFeature :: generateWsegvalvTable ( RifEclipseDataTableFormatter & formatter ,
const RimWellPath * wellPath ,
2017-06-28 20:51:16 +02:00
const RicCaseAndFileExportSettingsUi & settings ,
2017-06-02 12:50:16 +02:00
const std :: vector < WellSegmentLocation >& locations )
{
2017-06-20 10:40:39 +02:00
RiaEclipseUnitTools :: UnitSystem unitSystem = settings . caseToApply -> eclipseCaseData () -> unitsType ();
2017-06-02 12:50:16 +02:00
{
formatter . keyword ( "WSEGVALV" );
std :: vector < RifEclipseOutputTableColumn > header = {
RifEclipseOutputTableColumn ( "Well Name" ),
RifEclipseOutputTableColumn ( "Seg No" ),
RifEclipseOutputTableColumn ( "Cv" ),
RifEclipseOutputTableColumn ( "Ac" ),
};
formatter . header ( header );
}
for ( const WellSegmentLocation & location : locations )
{
formatter . add ( wellPath -> name ());
formatter . add ( location . icdSegmentNumber );
formatter . add ( location . fishbonesSubs -> icdFlowCoefficient ());
2017-06-20 10:40:39 +02:00
double icdOrificeRadius = location . fishbonesSubs -> icdOrificeDiameter ( unitSystem ) / 2 ;
2017-06-02 12:50:16 +02:00
double icdArea = icdOrificeRadius * icdOrificeRadius * cvf :: PI_D ;
formatter . add ( icdArea * static_cast < double > ( location . fishbonesSubs -> icdCount ()));
formatter . rowCompleted ();
}
formatter . tableCompleted ();
}
2017-06-09 12:08:15 +02:00
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
double RicExportFishbonesWellSegmentsFeature :: computeEffectiveDiameter ( double innerDiameter , double outerDiameter )
{
double innerRadius = innerDiameter / 2 ;
double innerArea = cvf :: PI_D * innerRadius * innerRadius ;
double outerRadius = outerDiameter / 2 ;
double outerArea = cvf :: PI_D * outerRadius * outerRadius ;
double effectiveArea = outerArea - innerArea ;
2017-06-09 12:27:21 +02:00
double effectiveRadius = cvf :: Math :: sqrt ( effectiveArea / cvf :: PI_D );
2017-06-09 12:08:15 +02:00
return effectiveRadius * 2 ;
}