#12957 Sector Model Export: add non-uniform refinement support to faults code path

Add RigNonUniformRefinement overloads for extractFaults and faultsKeyword,
following the existing dual-overload pattern. The Vec3st overloads now
delegate via fromUniform() to preserve backward compatibility. Update
addFaultsToDeckFile to call effectiveRefinement() instead of refinement().
This commit is contained in:
Kristian Bendiksen
2026-03-30 11:24:56 +02:00
parent 5d6aba0f70
commit 4aa0910db3
5 changed files with 189 additions and 14 deletions
@@ -569,6 +569,28 @@ std::vector<RigFault::CellAndFace> RifEclipseInputFileTools::extractFaults( cons
const cvf::Vec3st& maxIn /*= cvf::Vec3st::UNDEFINED*/,
const cvf::Vec3st& refinement /*= cvf::Vec3st( 1, 1, 1 )*/ )
{
cvf::Vec3st max = maxIn;
if ( max == cvf::Vec3st::UNDEFINED )
{
max = cvf::Vec3st( mainGrid->cellCountI() - 1, mainGrid->cellCountJ() - 1, mainGrid->cellCountK() - 1 );
}
auto sectorSize = cvf::Vec3st( max.x() - min.x() + 1, max.y() - min.y() + 1, max.z() - min.z() + 1 );
auto nonUniform = RigNonUniformRefinement::fromUniform( refinement, sectorSize );
return extractFaults( mainGrid, faultFaces, min, max, nonUniform );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::vector<RigFault::CellAndFace> RifEclipseInputFileTools::extractFaults( const RigMainGrid* mainGrid,
const std::vector<RigFault::FaultFace>& faultFaces,
const cvf::Vec3st& min,
const cvf::Vec3st& maxIn,
const RigNonUniformRefinement& refinement )
{
using Dim = RigNonUniformRefinement::Dimension;
std::vector<RigFault::CellAndFace> faultCellAndFaces;
cvf::Vec3st max = maxIn;
@@ -585,11 +607,15 @@ std::vector<RigFault::CellAndFace> RifEclipseInputFileTools::extractFaults( cons
if ( i < min.x() || i > max.x() || j < min.y() || j > max.y() || k < min.z() || k > max.z() ) continue;
size_t shifted_i = ( i - min.x() ) * refinement.x();
size_t shifted_j = ( j - min.y() ) * refinement.y();
size_t shifted_k = ( k - min.z() ) * refinement.z();
size_t sectorI = i - min.x();
size_t sectorJ = j - min.y();
size_t sectorK = k - min.z();
if ( refinement != cvf::Vec3st( 1, 1, 1 ) )
size_t shifted_i = refinement.cumulativeOffset( Dim::DimI, sectorI );
size_t shifted_j = refinement.cumulativeOffset( Dim::DimJ, sectorJ );
size_t shifted_k = refinement.cumulativeOffset( Dim::DimK, sectorK );
if ( refinement.hasNonUniformRefinement() )
{
auto gridAxis = cvf::StructGridInterface::gridAxisFromFace( faultCellAndFace.m_nativeFace );
@@ -597,11 +623,11 @@ std::vector<RigFault::CellAndFace> RifEclipseInputFileTools::extractFaults( cons
{
if ( faultCellAndFace.m_nativeFace == cvf::StructGridInterface::POS_I )
{
shifted_i += refinement.x() - 1;
shifted_i += refinement.subcellCount( Dim::DimI, sectorI ) - 1;
}
for ( size_t refineK = 0; refineK < refinement.z(); ++refineK )
for ( size_t refineK = 0; refineK < refinement.subcellCount( Dim::DimK, sectorK ); ++refineK )
{
for ( size_t refineJ = 0; refineJ < refinement.y(); ++refineJ )
for ( size_t refineJ = 0; refineJ < refinement.subcellCount( Dim::DimJ, sectorJ ); ++refineJ )
{
faultCellAndFaces.push_back(
std::make_tuple( shifted_i, shifted_j + refineJ, shifted_k + refineK, faultCellAndFace.m_nativeFace ) );
@@ -612,12 +638,12 @@ std::vector<RigFault::CellAndFace> RifEclipseInputFileTools::extractFaults( cons
{
if ( faultCellAndFace.m_nativeFace == cvf::StructGridInterface::POS_J )
{
shifted_j += refinement.y() - 1;
shifted_j += refinement.subcellCount( Dim::DimJ, sectorJ ) - 1;
}
for ( size_t refineK = 0; refineK < refinement.z(); ++refineK )
for ( size_t refineK = 0; refineK < refinement.subcellCount( Dim::DimK, sectorK ); ++refineK )
{
for ( size_t refineI = 0; refineI < refinement.x(); ++refineI )
for ( size_t refineI = 0; refineI < refinement.subcellCount( Dim::DimI, sectorI ); ++refineI )
{
faultCellAndFaces.push_back(
std::make_tuple( shifted_i + refineI, shifted_j, shifted_k + refineK, faultCellAndFace.m_nativeFace ) );
@@ -628,12 +654,12 @@ std::vector<RigFault::CellAndFace> RifEclipseInputFileTools::extractFaults( cons
{
if ( faultCellAndFace.m_nativeFace == cvf::StructGridInterface::POS_K )
{
shifted_k += refinement.z() - 1;
shifted_k += refinement.subcellCount( Dim::DimK, sectorK ) - 1;
}
for ( size_t refineJ = 0; refineJ < refinement.y(); ++refineJ )
for ( size_t refineJ = 0; refineJ < refinement.subcellCount( Dim::DimJ, sectorJ ); ++refineJ )
{
for ( size_t refineI = 0; refineI < refinement.x(); ++refineI )
for ( size_t refineI = 0; refineI < refinement.subcellCount( Dim::DimI, sectorI ); ++refineI )
{
faultCellAndFaces.push_back(
std::make_tuple( shifted_i + refineI, shifted_j + refineJ, shifted_k, faultCellAndFace.m_nativeFace ) );
@@ -117,6 +117,12 @@ public:
const cvf::Vec3st& max = cvf::Vec3st::UNDEFINED,
const cvf::Vec3st& refinement = cvf::Vec3st( 1, 1, 1 ) );
static std::vector<RigFault::CellAndFace> extractFaults( const RigMainGrid* mainGrid,
const std::vector<RigFault::FaultFace>& faultFaces,
const cvf::Vec3st& min,
const cvf::Vec3st& max,
const RigNonUniformRefinement& refinement );
static bool importFaultsFromFile( RigEclipseCaseData* eclipseCase, const QString& fileName );
static void readFaultsInGridSection( const QString& fileName,
@@ -558,6 +558,143 @@ Opm::DeckKeyword faultsKeyword( const RigMainGrid* mainGrid, const cvf::Vec3st&
return kw;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
Opm::DeckKeyword faultsKeyword( const RigMainGrid* mainGrid,
const cvf::Vec3st& min,
const cvf::Vec3st& max,
const RigNonUniformRefinement& refinement )
{
if ( mainGrid == nullptr )
{
return Opm::DeckKeyword();
}
// Helper lambda to convert FaceType to Eclipse face string
auto faceTypeToString = []( cvf::StructGridInterface::FaceType faceType ) -> std::string
{
switch ( faceType )
{
case cvf::StructGridInterface::POS_I:
return "I";
case cvf::StructGridInterface::NEG_I:
return "I-";
case cvf::StructGridInterface::POS_J:
return "J";
case cvf::StructGridInterface::NEG_J:
return "J-";
case cvf::StructGridInterface::POS_K:
return "K";
case cvf::StructGridInterface::NEG_K:
return "K-";
default:
return "";
}
};
using F = Opm::ParserKeywords::FAULTS;
Opm::DeckKeyword kw{ Opm::ParserKeywords::FAULTS() };
// Process all faults in the grid
const cvf::Collection<RigFault>& faults = mainGrid->faults();
for ( const auto& fault : faults )
{
// Skip undefined faults
if ( fault->name() == RiaResultNames::undefinedGridFaultName() || fault->name() == RiaResultNames::undefinedGridFaultWithInactiveName() )
{
continue;
}
// Extract fault cell and face data for this fault
std::vector<RigFault::CellAndFace> faultCellAndFaces =
RifEclipseInputFileTools::extractFaults( mainGrid, fault->faultFaces(), min, max, refinement );
// Group consecutive cells in K direction and create fault records
size_t lastI = std::numeric_limits<size_t>::max();
size_t lastJ = std::numeric_limits<size_t>::max();
size_t lastK = std::numeric_limits<size_t>::max();
size_t startK = std::numeric_limits<size_t>::max();
cvf::StructGridInterface::FaceType lastFaceType = cvf::StructGridInterface::FaceType::NO_FACE;
for ( const RigFault::CellAndFace& faultCellAndFace : faultCellAndFaces )
{
size_t i, j, k;
cvf::StructGridInterface::FaceType faceType;
std::tie( i, j, k, faceType ) = faultCellAndFace;
// Check if we need to write out the previous range
if ( i != lastI || j != lastJ || lastFaceType != faceType || k != lastK + 1 )
{
// Write out previous fault line if valid
if ( lastFaceType != cvf::StructGridInterface::FaceType::NO_FACE )
{
// Convert from 0-based to 1-based Eclipse indexing
int i1 = static_cast<int>( lastI ) + 1;
int j1 = static_cast<int>( lastJ ) + 1;
int k1 = static_cast<int>( startK ) + 1;
int k2 = static_cast<int>( lastK ) + 1;
std::string faceStr = faceTypeToString( lastFaceType );
// Create a record for this fault line
std::vector<Opm::DeckItem> items;
items.push_back( RifOpmDeckTools::item( F::NAME::itemName, fault->name().toStdString() ) );
items.push_back( RifOpmDeckTools::item( F::IX1::itemName, i1 ) );
items.push_back( RifOpmDeckTools::item( F::IX2::itemName, i1 ) );
items.push_back( RifOpmDeckTools::item( F::IY1::itemName, j1 ) );
items.push_back( RifOpmDeckTools::item( F::IY2::itemName, j1 ) );
items.push_back( RifOpmDeckTools::item( F::IZ1::itemName, k1 ) );
items.push_back( RifOpmDeckTools::item( F::IZ2::itemName, k2 ) );
items.push_back( RifOpmDeckTools::item( F::FACE::itemName, faceStr ) );
kw.addRecord( Opm::DeckRecord{ std::move( items ) } );
}
// Start new range
lastI = i;
lastJ = j;
lastK = k;
lastFaceType = faceType;
startK = k;
}
else
{
// Continue current range
lastK = k;
}
}
// Write out final fault line for this fault if valid
if ( lastFaceType != cvf::StructGridInterface::FaceType::NO_FACE )
{
// Convert from 0-based to 1-based Eclipse indexing
int i1 = static_cast<int>( lastI ) + 1;
int j1 = static_cast<int>( lastJ ) + 1;
int k1 = static_cast<int>( startK ) + 1;
int k2 = static_cast<int>( lastK ) + 1;
std::string faceStr = faceTypeToString( lastFaceType );
// Create a record for this fault line
std::vector<Opm::DeckItem> items;
items.push_back( RifOpmDeckTools::item( F::NAME::itemName, fault->name().toStdString() ) );
items.push_back( RifOpmDeckTools::item( F::IX1::itemName, i1 ) );
items.push_back( RifOpmDeckTools::item( F::IX2::itemName, i1 ) );
items.push_back( RifOpmDeckTools::item( F::IY1::itemName, j1 ) );
items.push_back( RifOpmDeckTools::item( F::IY2::itemName, j1 ) );
items.push_back( RifOpmDeckTools::item( F::IZ1::itemName, k1 ) );
items.push_back( RifOpmDeckTools::item( F::IZ2::itemName, k2 ) );
items.push_back( RifOpmDeckTools::item( F::FACE::itemName, faceStr ) );
kw.addRecord( Opm::DeckRecord{ std::move( items ) } );
}
}
return kw;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
@@ -22,6 +22,8 @@
#include "cvfVector3.h"
#include "RigNonUniformRefinement.h"
#include <QDateTime>
#include <QString>
@@ -68,6 +70,10 @@ Opm::DeckKeyword faultsKeyword( const RigMainGrid* mainGrid,
const cvf::Vec3st& min = cvf::Vec3st::ZERO,
const cvf::Vec3st& max = cvf::Vec3st::UNDEFINED,
const cvf::Vec3st& refinement = cvf::Vec3st( 1, 1, 1 ) );
Opm::DeckKeyword faultsKeyword( const RigMainGrid* mainGrid,
const cvf::Vec3st& min,
const cvf::Vec3st& max,
const RigNonUniformRefinement& refinement );
Opm::DeckKeyword bcconKeyword( const std::vector<RigEclipseResultTools::BorderCellFace>& borderCellFaces );
Opm::DeckKeyword bcpropKeyword( const std::vector<RigEclipseResultTools::BorderCellFace>& boundaryConditions,
const std::vector<Opm::DeckRecord>& boundaryConditionProperties );
@@ -1066,7 +1066,7 @@ std::expected<void, QString> RigSimulationInputTool::addFaultsToDeckFile( RimEcl
// Create FAULTS keyword using the factory
Opm::DeckKeyword faultsKw =
RimKeywordFactory::faultsKeyword( eclipseCase->mainGrid(), settings.min(), settings.max(), settings.refinement() );
RimKeywordFactory::faultsKeyword( eclipseCase->mainGrid(), settings.min(), settings.max(), settings.effectiveRefinement() );
// Replace FAULTS keyword in GRID section
if ( !deckFile.replaceKeyword( "GRID", faultsKw ) )