mirror of
https://github.com/OPM/ResInsight.git
synced 2026-10-24 14:17:52 -05:00
Sector export: Assign all refined sub-cells to segments in COMPSEGS
Only END_IJK denotes a cell range, DIRECTION alone is a single cell and the MD interval is split. A defaulted start MD is zero. Without an end MD the record is copied to each sub-cell so all refined COMPDAT connections are attached to a segment.
This commit is contained in:
1 parent
2a8cd7856b
commit
66c2cd47d3
3 files changed
+129
-20
No files matched your search
@@ -1582,15 +1582,15 @@ RigSimulationInputTool::ConnectionKeySet RigSimulationInputTool::reversedSegment
|
||||
const auto& record = keyword.getRecord( r );
|
||||
if ( record.size() < 6 ) continue;
|
||||
if ( !record.getItem( 0 ).hasValue( 0 ) || !record.getItem( 1 ).hasValue( 0 ) || !record.getItem( 2 ).hasValue( 0 ) ) continue;
|
||||
if ( !record.getItem( 4 ).hasValue( 0 ) ) continue;
|
||||
|
||||
const int i = record.getItem( 0 ).get<int>( 0 ) - 1;
|
||||
const int j = record.getItem( 1 ).get<int>( 0 ) - 1;
|
||||
const int k = record.getItem( 2 ).get<int>( 0 ) - 1;
|
||||
if ( i < 0 || j < 0 || k < 0 ) continue;
|
||||
|
||||
const int branch = record.getItem( 3 ).hasValue( 0 ) ? record.getItem( 3 ).get<int>( 0 ) : 0;
|
||||
cellsPerBranch[branch].push_back( { record.getItem( 4 ).get<double>( 0 ), caf::VecIjk0( i, j, k ) } );
|
||||
const int branch = record.getItem( 3 ).hasValue( 0 ) ? record.getItem( 3 ).get<int>( 0 ) : 0;
|
||||
const double startMd = record.getItem( 4 ).hasValue( 0 ) ? record.getItem( 4 ).get<double>( 0 ) : 0.0;
|
||||
cellsPerBranch[branch].push_back( { startMd, caf::VecIjk0( i, j, k ) } );
|
||||
}
|
||||
|
||||
for ( auto& [branch, cells] : cellsPerBranch )
|
||||
@@ -1879,12 +1879,37 @@ std::expected<std::vector<Opm::DeckRecord>, QString> RigSimulationInputTool::pro
|
||||
|
||||
std::vector<RefinedSubcell> subcells = { { ( *transformResult )[dim], 1.0 } };
|
||||
|
||||
const bool hasMdInterval = record.size() > 5 && record.getItem( 4 ).hasValue( 0 ) && record.getItem( 5 ).hasValue( 0 );
|
||||
const bool isCellRange = record.size() > 6 && record.getItem( 6 ).hasValue( 0 );
|
||||
using C = Opm::ParserKeywords::COMPSEGS;
|
||||
|
||||
auto hasItemValue = [&record]( const std::string& itemName )
|
||||
{ return record.hasItem( itemName ) && record.getItem( itemName ).hasValue( 0 ); };
|
||||
|
||||
// A cell range is given by END_IJK. DIRECTION alone (with an MD interval) describes a single cell.
|
||||
const bool isCellRange = hasItemValue( C::END_IJK::itemName );
|
||||
const bool hasEndMd = hasItemValue( C::DISTANCE_END::itemName );
|
||||
|
||||
// A defaulted start MD is zero (only allowed for the first segment record)
|
||||
const double startMd = hasItemValue( C::DISTANCE_START::itemName ) ? record.getItem( C::DISTANCE_START::itemName ).get<double>( 0 ) : 0.0;
|
||||
const double endMd = hasEndMd ? record.getItem( C::DISTANCE_END::itemName ).get<double>( 0 ) : 0.0;
|
||||
|
||||
// Whether the MD interval is divided between the sub-cells, otherwise the record is copied unchanged to each sub-cell
|
||||
bool splitMdInterval = false;
|
||||
|
||||
if ( refinement.subcellCount( dim, sectorIndex ) > 1 )
|
||||
{
|
||||
if ( hasMdInterval && !isCellRange )
|
||||
if ( isCellRange )
|
||||
{
|
||||
RiaLogging::warning(
|
||||
std::format( "COMPSEGS record for well {} at ({}, {}, {}) uses a cell range (END_IJK), which is not supported "
|
||||
"for refined sector export. Only the center refined sub-cell is assigned to the segment.",
|
||||
wellName,
|
||||
origIjk.x() + 1,
|
||||
origIjk.y() + 1,
|
||||
origIjk.z() + 1 ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
// All refined sub-cells along the connection are completed by COMPDAT, so all must be assigned to a segment
|
||||
subcells = allSubcells( refinement, dim, sectorIndex );
|
||||
|
||||
// Assign the MD interval in the order the well travels through the sub-cells
|
||||
@@ -1892,15 +1917,17 @@ std::expected<std::vector<Opm::DeckRecord>, QString> RigSimulationInputTool::pro
|
||||
{
|
||||
std::reverse( subcells.begin(), subcells.end() );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
RiaLogging::warning( std::format( "COMPSEGS record for well {} at ({}, {}, {}) has no MD interval or uses a cell range. "
|
||||
"Only the center refined sub-cell is assigned to the segment.",
|
||||
wellName,
|
||||
origIjk.x() + 1,
|
||||
origIjk.y() + 1,
|
||||
origIjk.z() + 1 ) );
|
||||
|
||||
splitMdInterval = hasEndMd;
|
||||
if ( !hasEndMd )
|
||||
{
|
||||
RiaLogging::warning( std::format( "COMPSEGS record for well {} at ({}, {}, {}) has no end MD. "
|
||||
"The record is copied unchanged to each refined sub-cell.",
|
||||
wellName,
|
||||
origIjk.x() + 1,
|
||||
origIjk.y() + 1,
|
||||
origIjk.z() + 1 ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1920,11 +1947,10 @@ std::expected<std::vector<Opm::DeckRecord>, QString> RigSimulationInputTool::pro
|
||||
const double endFraction = startFraction + subcell.fraction;
|
||||
for ( size_t i = 3; i < record.size(); ++i )
|
||||
{
|
||||
if ( subcells.size() > 1 && ( i == 4 || i == 5 ) )
|
||||
const auto& itemName = record.getItem( i ).name();
|
||||
if ( splitMdInterval && ( itemName == C::DISTANCE_START::itemName || itemName == C::DISTANCE_END::itemName ) )
|
||||
{
|
||||
const double startMd = record.getItem( 4 ).get<double>( 0 );
|
||||
const double endMd = record.getItem( 5 ).get<double>( 0 );
|
||||
const double fraction = ( i == 4 ) ? startFraction : endFraction;
|
||||
const double fraction = ( itemName == C::DISTANCE_START::itemName ) ? startFraction : endFraction;
|
||||
items.push_back( RifOpmDeckTools::item( record.getItem( i ).name(), startMd + ( endMd - startMd ) * fraction ) );
|
||||
}
|
||||
else
|
||||
|
||||
@@ -143,7 +143,8 @@ public:
|
||||
const ConnectionCellPropertiesProvider& cellProperties = {} );
|
||||
|
||||
// Segment records are split into one record per sub-cell along the connection penetration direction,
|
||||
// with the MD interval divided by the sub-cell length fractions in the order the well travels through them
|
||||
// with the MD interval divided by the sub-cell length fractions in the order the well travels through them.
|
||||
// Without an end MD the record is copied unchanged to each sub-cell. Cell ranges (END_IJK) use the center sub-cell.
|
||||
static std::expected<std::vector<Opm::DeckRecord>, QString> processCompsegsRecord( const Opm::DeckRecord& record,
|
||||
const std::string& wellName,
|
||||
bool isWellNameRecord,
|
||||
|
||||
@@ -3346,3 +3346,85 @@ TEST( RigSimulationInputTool, ReversedSegmentCells_ForwardTraversalNotReversed )
|
||||
|
||||
EXPECT_TRUE( reversedCells.empty() );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
/// DIRECTION without END_IJK is a single cell, so the MD interval is still split between the sub-cells
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
TEST( RigSimulationInputTool, ProcessCompsegsRecord_DirectionWithoutRangeIsSplit )
|
||||
{
|
||||
auto settings = createSectorSettings( cvf::Vec3st( 1, 1, 2 ) );
|
||||
|
||||
Opm::Parser parser;
|
||||
auto deck = parser.parseString( "COMPSEGS\n 'W' /\n 3 4 2 1 100.0 120.0 'K' /\n/\n" );
|
||||
|
||||
const auto& compsegs = deck["COMPSEGS"].back();
|
||||
auto result = RigSimulationInputTool::processCompsegsRecord( compsegs.getRecord( 1 ), "W", false, settings, {} );
|
||||
ASSERT_TRUE( result.has_value() ) << result.error().toStdString();
|
||||
ASSERT_EQ( 2u, result->size() );
|
||||
EXPECT_EQ( 3, result->at( 0 ).getItem( 2 ).get<int>( 0 ) );
|
||||
EXPECT_DOUBLE_EQ( 110.0, result->at( 0 ).getItem( 5 ).get<double>( 0 ) );
|
||||
EXPECT_EQ( 4, result->at( 1 ).getItem( 2 ).get<int>( 0 ) );
|
||||
EXPECT_DOUBLE_EQ( 110.0, result->at( 1 ).getItem( 4 ).get<double>( 0 ) );
|
||||
EXPECT_EQ( "K", result->at( 1 ).getItem( 6 ).get<std::string>( 0 ) );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
/// A defaulted start MD is zero
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
TEST( RigSimulationInputTool, ProcessCompsegsRecord_DefaultedStartMdIsZero )
|
||||
{
|
||||
auto settings = createSectorSettings( cvf::Vec3st( 1, 1, 2 ) );
|
||||
|
||||
Opm::Parser parser;
|
||||
auto deck = parser.parseString( "COMPSEGS\n 'W' /\n 3 4 2 1 1* 20.0 /\n/\n" );
|
||||
|
||||
const auto& compsegs = deck["COMPSEGS"].back();
|
||||
auto result = RigSimulationInputTool::processCompsegsRecord( compsegs.getRecord( 1 ), "W", false, settings, {} );
|
||||
ASSERT_TRUE( result.has_value() ) << result.error().toStdString();
|
||||
ASSERT_EQ( 2u, result->size() );
|
||||
EXPECT_DOUBLE_EQ( 0.0, result->at( 0 ).getItem( 4 ).get<double>( 0 ) );
|
||||
EXPECT_DOUBLE_EQ( 10.0, result->at( 0 ).getItem( 5 ).get<double>( 0 ) );
|
||||
EXPECT_DOUBLE_EQ( 10.0, result->at( 1 ).getItem( 4 ).get<double>( 0 ) );
|
||||
EXPECT_DOUBLE_EQ( 20.0, result->at( 1 ).getItem( 5 ).get<double>( 0 ) );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
/// Without an end MD every sub-cell still gets a segment record, with the MD items unchanged
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
TEST( RigSimulationInputTool, ProcessCompsegsRecord_NoEndMdCopiedToAllSubcells )
|
||||
{
|
||||
auto settings = createSectorSettings( cvf::Vec3st( 1, 1, 2 ) );
|
||||
|
||||
Opm::Parser parser;
|
||||
auto deck = parser.parseString( "COMPSEGS\n 'W' /\n 3 4 2 1 100.0 1* 'K' /\n/\n" );
|
||||
|
||||
const auto& compsegs = deck["COMPSEGS"].back();
|
||||
auto result = RigSimulationInputTool::processCompsegsRecord( compsegs.getRecord( 1 ), "W", false, settings, {} );
|
||||
ASSERT_TRUE( result.has_value() ) << result.error().toStdString();
|
||||
ASSERT_EQ( 2u, result->size() );
|
||||
for ( size_t s = 0; s < 2; ++s )
|
||||
{
|
||||
EXPECT_EQ( 3 + static_cast<int>( s ), result->at( s ).getItem( 2 ).get<int>( 0 ) );
|
||||
EXPECT_DOUBLE_EQ( 100.0, result->at( s ).getItem( 4 ).get<double>( 0 ) );
|
||||
EXPECT_FALSE( result->at( s ).getItem( 5 ).hasValue( 0 ) );
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
/// A cell range (END_IJK) is not split, only the center sub-cell is used
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
TEST( RigSimulationInputTool, ProcessCompsegsRecord_CellRangeUsesCenterSubcell )
|
||||
{
|
||||
auto settings = createSectorSettings( cvf::Vec3st( 1, 1, 2 ) );
|
||||
|
||||
Opm::Parser parser;
|
||||
auto deck = parser.parseString( "COMPSEGS\n 'W' /\n 3 4 2 1 100.0 120.0 'K' 3 /\n/\n" );
|
||||
|
||||
const auto& compsegs = deck["COMPSEGS"].back();
|
||||
auto result = RigSimulationInputTool::processCompsegsRecord( compsegs.getRecord( 1 ), "W", false, settings, {} );
|
||||
ASSERT_TRUE( result.has_value() ) << result.error().toStdString();
|
||||
ASSERT_EQ( 1u, result->size() );
|
||||
EXPECT_EQ( 4, result->at( 0 ).getItem( 2 ).get<int>( 0 ) );
|
||||
EXPECT_DOUBLE_EQ( 100.0, result->at( 0 ).getItem( 4 ).get<double>( 0 ) );
|
||||
EXPECT_DOUBLE_EQ( 120.0, result->at( 0 ).getItem( 5 ).get<double>( 0 ) );
|
||||
}
|
||||
Reference in new issue
Block a user