mirror of
https://github.com/OPM/ResInsight.git
synced 2026-10-24 22:27:55 -05:00
#14496 MSW export: Assign branch ID to well path laterals before completions
Branch numbers are handed out from two counters. The well path laterals are numbered first, so that the main bore and the laterals occupy the lowest branch numbers. The completion branches (valves, fractures and fishbones) are numbered after all laterals, and are still listed immediately after the lateral they are connected to. The multiple_laterals test project is reduced to a single Eclipse case, and is used by two new unit tests covering the branch numbers and the order the branches are listed in.
This commit is contained in:
1 parent
aa01d5e4dc
commit
f796801a7a
4 files changed
+333
-1094
No files matched your search
+42
-14
@@ -47,6 +47,19 @@ namespace RicWellPathExportMswGeometryPath
|
||||
|
||||
using CompletionType = RicWellPathExportMswTableData::CompletionType;
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
/// Number of well path laterals in the tie-in tree, including the given well path itself.
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
static int lateralCount( const RimWellPath* wellPath )
|
||||
{
|
||||
int count = 1;
|
||||
for ( const auto* childWellPath : RicWellPathExportMswTableData::wellPathsWithTieIn( wellPath ) )
|
||||
{
|
||||
count += lateralCount( childWellPath );
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
/// Recursively build all WELSEGS/COMPSEGS/valve segments for one lateral (child well path)
|
||||
/// and any of its own child laterals.
|
||||
@@ -58,7 +71,8 @@ std::vector<RigMswBranch> buildLateralBranches( RimEclipseCase*
|
||||
CompletionType completionType,
|
||||
const std::optional<QDateTime>& exportDate,
|
||||
int& segmentNumber,
|
||||
int& branchNumber,
|
||||
int& lateralBranchNumber,
|
||||
int& completionBranchNumber,
|
||||
RiaDefines::EclipseUnitSystem unitSystem,
|
||||
RicMswBranchBuilder::FishbonesExportContext& fishbonesContext )
|
||||
{
|
||||
@@ -71,7 +85,7 @@ std::vector<RigMswBranch> buildLateralBranches( RimEclipseCase*
|
||||
const double tieInTVD = -wellPath->wellPathGeometry()->interpolatedPointAlongWellPath( tieInMD ).z();
|
||||
const std::string wellNameForExport = wellPath->completionSettings()->wellNameForExport().toStdString();
|
||||
|
||||
const int lateralBranchNum = ++branchNumber;
|
||||
const int lateralBranchNum = ++lateralBranchNumber;
|
||||
int childOutletSeg = outletSegNum;
|
||||
|
||||
// Optional ICV valve at the tie-in point — stored on the branch, not as a separate branch
|
||||
@@ -217,7 +231,7 @@ std::vector<RigMswBranch> buildLateralBranches( RimEclipseCase*
|
||||
infoType,
|
||||
wellNameForExport,
|
||||
segmentNumber,
|
||||
branchNumber,
|
||||
completionBranchNumber,
|
||||
mswParameters->maxSegmentLength(),
|
||||
{},
|
||||
exportDate,
|
||||
@@ -226,8 +240,13 @@ std::vector<RigMswBranch> buildLateralBranches( RimEclipseCase*
|
||||
|
||||
if ( ( completionType & CompletionType::FRACTURES ) == CompletionType::FRACTURES )
|
||||
{
|
||||
auto fracBranches =
|
||||
RicMswBranchBuilder::buildFractureBranches( eclipseCase, wellPath, mainGrid, childCellSegMap, infoType, segmentNumber, branchNumber );
|
||||
auto fracBranches = RicMswBranchBuilder::buildFractureBranches( eclipseCase,
|
||||
wellPath,
|
||||
mainGrid,
|
||||
childCellSegMap,
|
||||
infoType,
|
||||
segmentNumber,
|
||||
completionBranchNumber );
|
||||
result.insert( result.end(), std::make_move_iterator( fracBranches.begin() ), std::make_move_iterator( fracBranches.end() ) );
|
||||
}
|
||||
|
||||
@@ -241,7 +260,7 @@ std::vector<RigMswBranch> buildLateralBranches( RimEclipseCase*
|
||||
infoType,
|
||||
wellNameForExport,
|
||||
segmentNumber,
|
||||
branchNumber,
|
||||
completionBranchNumber,
|
||||
mswParameters->maxSegmentLength(),
|
||||
{},
|
||||
unitSystem,
|
||||
@@ -261,7 +280,8 @@ std::vector<RigMswBranch> buildLateralBranches( RimEclipseCase*
|
||||
completionType,
|
||||
exportDate,
|
||||
segmentNumber,
|
||||
branchNumber,
|
||||
lateralBranchNumber,
|
||||
completionBranchNumber,
|
||||
unitSystem,
|
||||
fishbonesContext );
|
||||
result.insert( result.end(), std::make_move_iterator( grandchildBranches.begin() ), std::make_move_iterator( grandchildBranches.end() ) );
|
||||
@@ -328,8 +348,15 @@ RigMswWellExportData buildMswWellExportData( RimEclipseCase*
|
||||
}
|
||||
}
|
||||
|
||||
int segmentNumber = 2; // Segment 1 is the implicit well heel.
|
||||
int branchNumber = 1; // Incremented for each new branch.
|
||||
int segmentNumber = 2; // Segment 1 is the implicit well heel.
|
||||
|
||||
// The well path laterals are given their branch number first, so that the main bore and the
|
||||
// laterals occupy the lowest branch numbers. The completion branches (valves, fractures and
|
||||
// fishbones) are numbered after all laterals, but are still listed immediately after the
|
||||
// lateral they are connected to.
|
||||
int lateralBranchNumber = 1; // Main bore is branch 1, incremented for each lateral.
|
||||
int completionBranchNumber = lateralCount( wellPath ); // Incremented for each completion branch.
|
||||
|
||||
std::vector<RicMswBranchBuilder::CellSegmentEntry> cellSegMap;
|
||||
|
||||
// Collected across the main bore and all tie-in laterals, applied once the branches are assembled.
|
||||
@@ -346,7 +373,7 @@ RigMswWellExportData buildMswWellExportData( RimEclipseCase*
|
||||
infoType,
|
||||
initialMD,
|
||||
initialTVD,
|
||||
branchNumber,
|
||||
lateralBranchNumber,
|
||||
segmentNumber,
|
||||
1, // outlet = heel (segment 1)
|
||||
maxSegmentLength,
|
||||
@@ -390,7 +417,7 @@ RigMswWellExportData buildMswWellExportData( RimEclipseCase*
|
||||
infoType,
|
||||
wellNameForExport,
|
||||
segmentNumber,
|
||||
branchNumber,
|
||||
completionBranchNumber,
|
||||
maxSegmentLength,
|
||||
customSegmentIntervals,
|
||||
exportDate,
|
||||
@@ -401,7 +428,7 @@ RigMswWellExportData buildMswWellExportData( RimEclipseCase*
|
||||
if ( includeFractures )
|
||||
{
|
||||
fractureBranches =
|
||||
RicMswBranchBuilder::buildFractureBranches( eclipseCase, wellPath, mainGrid, cellSegMap, infoType, segmentNumber, branchNumber );
|
||||
RicMswBranchBuilder::buildFractureBranches( eclipseCase, wellPath, mainGrid, cellSegMap, infoType, segmentNumber, completionBranchNumber );
|
||||
}
|
||||
|
||||
const bool includeFishbones = ( completionType & CompletionType::FISHBONES ) == CompletionType::FISHBONES;
|
||||
@@ -416,7 +443,7 @@ RigMswWellExportData buildMswWellExportData( RimEclipseCase*
|
||||
infoType,
|
||||
wellNameForExport,
|
||||
segmentNumber,
|
||||
branchNumber,
|
||||
completionBranchNumber,
|
||||
maxSegmentLength,
|
||||
customSegmentIntervals,
|
||||
unitSystem,
|
||||
@@ -436,7 +463,8 @@ RigMswWellExportData buildMswWellExportData( RimEclipseCase*
|
||||
completionType,
|
||||
exportDate,
|
||||
segmentNumber,
|
||||
branchNumber,
|
||||
lateralBranchNumber,
|
||||
completionBranchNumber,
|
||||
unitSystem,
|
||||
fishbonesContext );
|
||||
lateralBranches.insert( lateralBranches.end(),
|
||||
|
||||
+8
-1
@@ -40,6 +40,12 @@ class RigMainGrid;
|
||||
namespace RicWellPathExportMswGeometryPath
|
||||
{
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
/// Branch numbers are handed out from two separate counters. The well path laterals are numbered
|
||||
/// first (lateralBranchNumber), so that the main bore and the laterals occupy the lowest branch
|
||||
/// numbers. The completion branches follow (completionBranchNumber), but are listed immediately
|
||||
/// after the lateral they are connected to.
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
std::vector<RigMswBranch> buildLateralBranches( RimEclipseCase* eclipseCase,
|
||||
const RimWellPath* wellPath,
|
||||
const RigMainGrid* mainGrid,
|
||||
@@ -47,7 +53,8 @@ std::vector<RigMswBranch> buildLateralBranches( RimEclipseCase*
|
||||
RicWellPathExportMswTableData::CompletionType completionType,
|
||||
const std::optional<QDateTime>& exportDate,
|
||||
int& segmentNumber,
|
||||
int& branchNumber,
|
||||
int& lateralBranchNumber,
|
||||
int& completionBranchNumber,
|
||||
RiaDefines::EclipseUnitSystem unitSystem,
|
||||
RicMswBranchBuilder::FishbonesExportContext& fishbonesContext );
|
||||
|
||||
|
||||
@@ -19,12 +19,24 @@
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include "CompletionExportCommands/MswExport/RicWellPathExportMswGeometryPath.h"
|
||||
#include "CompletionExportCommands/RicWellPathExportMswTableData.h"
|
||||
|
||||
#include "CompletionsMsw/RigMswSegment.h"
|
||||
#include "CompletionsMsw/RigMswTableData.h"
|
||||
#include "CompletionsMsw/RigMswTableRows.h"
|
||||
|
||||
#include "RiaApplication.h"
|
||||
#include "RiaDefines.h"
|
||||
#include "RiaTestDataDirectory.h"
|
||||
|
||||
#include "RimEclipseCase.h"
|
||||
#include "RimProject.h"
|
||||
#include "RimWellPath.h"
|
||||
|
||||
#include <QDir>
|
||||
#include <QFile>
|
||||
|
||||
#include <map>
|
||||
|
||||
namespace
|
||||
{
|
||||
@@ -417,3 +429,112 @@ TEST( RicWellPathExportMswGeometryPath, HeaderFieldsPropagated )
|
||||
EXPECT_EQ( "ABS", result.welsegsHeader().infoType );
|
||||
EXPECT_EQ( RiaDefines::EclipseUnitSystem::UNITS_FIELD, result.unitSystem() );
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
// Branch numbering, using the multiple_laterals test project.
|
||||
//
|
||||
// Well-A is a main bore with an ICD-valved perforation interval and two well path laterals tied in
|
||||
// (Y2 and Y3). The main bore and the laterals must be given the lowest branch numbers, and the
|
||||
// completion branches must follow.
|
||||
//==================================================================================================
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
/// The single Eclipse case of the multiple_laterals project, and one of its well paths.
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
struct MswExportInput
|
||||
{
|
||||
RimEclipseCase* eclipseCase = nullptr;
|
||||
RimWellPath* wellPath = nullptr;
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
/// Load the multiple_laterals project and look up the well path with the given name.
|
||||
/// Members are left as nullptr if the project, the case or the well path could not be found.
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
MswExportInput loadMultipleLateralsProject( const QString& wellPathName )
|
||||
{
|
||||
MswExportInput input;
|
||||
|
||||
QDir projectFolder( TEST_MODEL_DIR );
|
||||
if ( !projectFolder.cd( "msw-export/project-files" ) ) return input;
|
||||
|
||||
const QString projectFileName = projectFolder.absoluteFilePath( "multiple_laterals.rsp" );
|
||||
if ( !QFile::exists( projectFileName ) ) return input;
|
||||
|
||||
if ( !RiaApplication::instance()->loadProject( projectFileName ) ) return input;
|
||||
|
||||
RimProject* project = RimProject::current();
|
||||
if ( !project ) return input;
|
||||
|
||||
const auto eclipseCases = project->eclipseCases();
|
||||
if ( !eclipseCases.empty() ) input.eclipseCase = eclipseCases.front();
|
||||
|
||||
for ( auto* wellPath : project->allWellPaths() )
|
||||
{
|
||||
if ( wellPath && wellPath->name() == wellPathName ) input.wellPath = wellPath;
|
||||
}
|
||||
|
||||
return input;
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
/// The main bore and the well path laterals occupy branch 1..N, and every completion branch is
|
||||
/// numbered above them.
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
TEST( RicWellPathExportMswGeometryPath, MultipleLaterals_LateralsNumberedBeforeCompletionBranches )
|
||||
{
|
||||
auto input = loadMultipleLateralsProject( "Well-A Y1" );
|
||||
ASSERT_TRUE( input.eclipseCase != nullptr );
|
||||
ASSERT_TRUE( input.wellPath != nullptr );
|
||||
|
||||
auto tableData = RicWellPathExportMswTableData::extractSingleWellMswData( input.eclipseCase, input.wellPath );
|
||||
ASSERT_TRUE( tableData.has_value() );
|
||||
|
||||
// Branch 1 is the main bore, branches 2 and 3 are the laterals Y2 and Y3. The three ICD
|
||||
// completion branches follow as 4, 5 and 6.
|
||||
std::map<int, std::string> descriptionOfFirstSegmentInBranch;
|
||||
for ( const auto& branch : tableData->mswBranches() )
|
||||
{
|
||||
if ( !branch.segments.empty() ) descriptionOfFirstSegmentInBranch[branch.branchNumber] = branch.segments.front().description;
|
||||
}
|
||||
|
||||
ASSERT_EQ( 6u, descriptionOfFirstSegmentInBranch.size() );
|
||||
EXPECT_EQ( "Segments on main bore", descriptionOfFirstSegmentInBranch[1] );
|
||||
EXPECT_EQ( "Segments on lateral Well-A Y2", descriptionOfFirstSegmentInBranch[2] );
|
||||
EXPECT_EQ( "Segments on lateral Well-A Y3", descriptionOfFirstSegmentInBranch[3] );
|
||||
for ( int branchNumber : { 4, 5, 6 } )
|
||||
{
|
||||
EXPECT_EQ( "1 ICD: 3100 - 3800 #1", descriptionOfFirstSegmentInBranch[branchNumber] );
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
/// The completion branches of a lateral are listed immediately after that lateral, even though
|
||||
/// their branch numbers are higher than the branch numbers of the laterals listed later.
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
TEST( RicWellPathExportMswGeometryPath, MultipleLaterals_CompletionBranchesListedAfterTheirLateral )
|
||||
{
|
||||
auto input = loadMultipleLateralsProject( "Well-A Y1" );
|
||||
ASSERT_TRUE( input.eclipseCase != nullptr );
|
||||
ASSERT_TRUE( input.wellPath != nullptr );
|
||||
|
||||
auto tableData = RicWellPathExportMswTableData::extractSingleWellMswData( input.eclipseCase, input.wellPath );
|
||||
ASSERT_TRUE( tableData.has_value() );
|
||||
|
||||
// The branch number of each WELSEGS row, in the order the rows are written to the export file.
|
||||
std::vector<int> branchNumbersInListedOrder;
|
||||
for ( const auto& row : tableData->welsegsData() )
|
||||
{
|
||||
if ( branchNumbersInListedOrder.empty() || branchNumbersInListedOrder.back() != row.branch )
|
||||
branchNumbersInListedOrder.push_back( row.branch );
|
||||
}
|
||||
|
||||
// Main bore, its three ICD branches, then lateral Y2 and lateral Y3.
|
||||
const std::vector<int> expected = { 1, 4, 5, 6, 2, 3 };
|
||||
EXPECT_EQ( expected, branchNumbersInListedOrder );
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
Reference in new issue
Block a user