mirror of
https://github.com/OPM/ResInsight.git
synced 2026-08-27 13:47:12 -05:00
Simulation Input Export: handle faults.
This commit is contained in:
+208
-116
@@ -30,6 +30,7 @@
|
||||
#include "RifReaderEclipseOutput.h"
|
||||
|
||||
#include "ProjectDataModel/Jobs/RimKeywordBcprop.h"
|
||||
#include "ProjectDataModel/Jobs/RimKeywordFactory.h"
|
||||
#include "Rim3dView.h"
|
||||
#include "RimDialogData.h"
|
||||
#include "RimEclipseCase.h"
|
||||
@@ -144,7 +145,10 @@ void RicExportEclipseSectorModelFeature::executeCommand( RimEclipseView*
|
||||
// Export simulation input if enabled
|
||||
if ( exportSettings.m_exportSimulationInput() )
|
||||
{
|
||||
exportSimulationInput( view, exportSettings );
|
||||
if ( auto result = exportSimulationInput( *view->eclipseCase(), exportSettings ); !result )
|
||||
{
|
||||
RiaLogging::error( QString( "Failed to export simulation input: %1" ).arg( result.error() ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -293,146 +297,231 @@ void RicExportEclipseSectorModelFeature::exportFaults( RimEclipseView* view, con
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RicExportEclipseSectorModelFeature::exportSimulationInput( RimEclipseView* view, const RicExportEclipseSectorModelUi& exportSettings )
|
||||
std::expected<void, QString> RicExportEclipseSectorModelFeature::exportSimulationInput( RimEclipseCase& eclipseCase,
|
||||
const RicExportEclipseSectorModelUi& exportSettings )
|
||||
{
|
||||
// Load the deck file
|
||||
QFileInfo fi( view->eclipseCase()->gridFileName() );
|
||||
QFileInfo fi( eclipseCase.gridFileName() );
|
||||
QString dataFileName = fi.absolutePath() + "/" + fi.completeBaseName() + ".DATA";
|
||||
|
||||
RifOpmFlowDeckFile deckFile;
|
||||
if ( !deckFile.loadDeck( dataFileName.toStdString() ) )
|
||||
{
|
||||
RiaLogging::error( QString( "Unable to load deck file '%1'" ).arg( dataFileName ) );
|
||||
return;
|
||||
return std::unexpected( QString( "Unable to load deck file '%1'" ).arg( dataFileName ) );
|
||||
}
|
||||
|
||||
QFileInfo exportGridInfo( exportSettings.exportGridFilename() );
|
||||
QString outputFolder = exportGridInfo.absolutePath();
|
||||
QString outputFile = exportGridInfo.completeBaseName() + ".DATA";
|
||||
|
||||
// Only change values when exporting to modified box: original values should just work (tm) for full grid box
|
||||
if ( exportSettings.exportGridBox() != RicExportEclipseSectorModelUi::GridBoxSelection::FULL_GRID_BOX )
|
||||
{
|
||||
// Get grid bounds for extraction
|
||||
RigGridExportAdapter gridAdapter( view->eclipseCase()->eclipseCaseData(),
|
||||
exportSettings.min(),
|
||||
exportSettings.max(),
|
||||
exportSettings.refinement() );
|
||||
// Grid dimensions (after refinement)
|
||||
std::vector<float> coordArray;
|
||||
std::vector<float> zcornArray;
|
||||
std::vector<int> actnumArray;
|
||||
|
||||
RigResdataGridConverter::convertGridToCornerPointArrays( gridAdapter, coordArray, zcornArray, actnumArray );
|
||||
|
||||
std::vector<int> dimens = { static_cast<int>( gridAdapter.cellCountI() ),
|
||||
static_cast<int>( gridAdapter.cellCountJ() ),
|
||||
static_cast<int>( gridAdapter.cellCountK() ) };
|
||||
|
||||
deckFile.replaceKeywordData( "DIMENS", dimens );
|
||||
|
||||
auto convertToDoubleVector = []( const std::vector<float>& vec )
|
||||
if ( auto result = updateCornerPointGridInDeckFile( &eclipseCase, exportSettings, deckFile ); !result )
|
||||
{
|
||||
std::vector<double> outVec;
|
||||
outVec.reserve( vec.size() );
|
||||
for ( float f : vec )
|
||||
outVec.push_back( f );
|
||||
return outVec;
|
||||
};
|
||||
std::vector<double> coords = convertToDoubleVector( coordArray );
|
||||
std::vector<double> zcorn = convertToDoubleVector( zcornArray );
|
||||
|
||||
deckFile.replaceKeywordData( "COORD", coords );
|
||||
deckFile.replaceKeywordData( "ZCORN", zcorn );
|
||||
deckFile.replaceKeywordData( "ACTNUM", actnumArray );
|
||||
|
||||
// TODO: deal with map axis
|
||||
|
||||
// Extract and replace keyword data for all keywords in the deck
|
||||
auto keywords = deckFile.keywords( false );
|
||||
RiaLogging::info( QString( "Processing %1 keywords from deck file" ).arg( keywords.size() ) );
|
||||
|
||||
for ( const auto& keywordStdStr : keywords )
|
||||
{
|
||||
QString keyword = QString::fromStdString( keywordStdStr );
|
||||
|
||||
// Skip special keywords that aren't cell properties
|
||||
if ( keyword.startsWith( "DATES" ) || keyword == "SCHEDULE" || keyword == "GRID" || keyword == "PROPS" ||
|
||||
keyword == "SOLUTION" || keyword == "RUNSPEC" || keyword == "SUMMARY" )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Try to extract keyword data
|
||||
auto result = RifEclipseInputFileTools::extractKeywordData( view->eclipseCase()->eclipseCaseData(),
|
||||
keyword,
|
||||
exportSettings.min(),
|
||||
exportSettings.max(),
|
||||
exportSettings.refinement() );
|
||||
if ( result )
|
||||
{
|
||||
// Replace keyword values in deck with extracted data
|
||||
if ( deckFile.replaceKeywordData( keywordStdStr, result.value() ) )
|
||||
{
|
||||
RiaLogging::info(
|
||||
QString( "Successfully replaced data for keyword '%1' (%2 values)" ).arg( keyword ).arg( result.value().size() ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
RiaLogging::warning( QString( "Failed to replace keyword '%1' in deck" ).arg( keyword ) );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Not all keywords will have data - this is expected
|
||||
RiaLogging::debug(
|
||||
QString( "Could not extract data for keyword '%1': %2" ).arg( keyword ).arg( QString::fromStdString( result.error() ) ) );
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// Generate border cell faces
|
||||
auto borderCellFaces = RigEclipseResultTools::generateBorderCellFaces( view->eclipseCase() );
|
||||
|
||||
if ( !borderCellFaces.empty() )
|
||||
if ( auto result = replaceKeywordValuesInDeckFile( &eclipseCase, exportSettings, deckFile ); !result )
|
||||
{
|
||||
// Add BCCON keyword
|
||||
if ( !deckFile.addBcconKeyword( "GRID", borderCellFaces ) )
|
||||
{
|
||||
RiaLogging::error( "Failed to add BCCON keyword to deck file" );
|
||||
}
|
||||
|
||||
// Build BCPROP records from the UI configuration
|
||||
std::vector<Opm::DeckRecord> bcpropRecords;
|
||||
for ( const auto& bcprop : exportSettings.m_bcpropKeywords )
|
||||
{
|
||||
if ( bcprop != nullptr )
|
||||
{
|
||||
Opm::DeckKeyword kw = bcprop->keyword();
|
||||
const auto& record = kw.getRecord( 0 );
|
||||
bcpropRecords.push_back( record );
|
||||
}
|
||||
}
|
||||
|
||||
// Add BCPROP keyword
|
||||
if ( !deckFile.addBcpropKeyword( "GRID", borderCellFaces, bcpropRecords ) )
|
||||
{
|
||||
RiaLogging::error( "Failed to add BCPROP keyword to deck file" );
|
||||
}
|
||||
return result;
|
||||
}
|
||||
else
|
||||
|
||||
if ( auto result = addBorderBoundaryConditions( &eclipseCase, exportSettings, deckFile ); !result )
|
||||
{
|
||||
RiaLogging::warning( "No border cells found - skipping BCCON/BCPROP keyword generation" );
|
||||
return result;
|
||||
}
|
||||
|
||||
if ( auto result = addFaultsToDeckFile( &eclipseCase, exportSettings, deckFile ); !result )
|
||||
{
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
// Save the modified deck file to the export directory
|
||||
QFileInfo exportGridInfo( exportSettings.exportGridFilename() );
|
||||
QString outputFolder = exportGridInfo.absolutePath();
|
||||
QString outputFile = exportGridInfo.completeBaseName() + ".DATA";
|
||||
if ( !deckFile.saveDeck( outputFolder.toStdString(), outputFile.toStdString() ) )
|
||||
{
|
||||
RiaLogging::error( QString( "Failed to save modified deck file to '%1/%2'" ).arg( outputFolder ).arg( outputFile ) );
|
||||
return std::unexpected( QString( "Failed to save modified deck file to '%1/%2'" ).arg( outputFolder ).arg( outputFile ) );
|
||||
}
|
||||
|
||||
RiaLogging::info( QString( "Saved modified deck file to '%1/%2'" ).arg( outputFolder ).arg( outputFile ) );
|
||||
return {};
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
std::expected<void, QString>
|
||||
RicExportEclipseSectorModelFeature::updateCornerPointGridInDeckFile( RimEclipseCase* eclipseCase,
|
||||
const RicExportEclipseSectorModelUi& exportSettings,
|
||||
RifOpmFlowDeckFile& deckFile )
|
||||
{
|
||||
// Get grid bounds for extraction
|
||||
|
||||
RigGridExportAdapter gridAdapter( eclipseCase->eclipseCaseData(), exportSettings.min(), exportSettings.max(), exportSettings.refinement() );
|
||||
|
||||
std::vector<float> coordArray;
|
||||
std::vector<float> zcornArray;
|
||||
std::vector<int> actnumArray;
|
||||
|
||||
RigResdataGridConverter::convertGridToCornerPointArrays( gridAdapter, coordArray, zcornArray, actnumArray );
|
||||
|
||||
// Sector dimensions (after refinement)
|
||||
std::vector<int> dimens = { static_cast<int>( gridAdapter.cellCountI() ),
|
||||
static_cast<int>( gridAdapter.cellCountJ() ),
|
||||
static_cast<int>( gridAdapter.cellCountK() ) };
|
||||
|
||||
if ( !deckFile.replaceKeywordData( "DIMENS", dimens ) )
|
||||
{
|
||||
return std::unexpected( "Failed to replace DIMENS keyword in deck file" );
|
||||
}
|
||||
|
||||
auto convertToDoubleVector = []( const std::vector<float>& vec )
|
||||
{
|
||||
std::vector<double> outVec;
|
||||
outVec.reserve( vec.size() );
|
||||
for ( float f : vec )
|
||||
outVec.push_back( f );
|
||||
return outVec;
|
||||
};
|
||||
std::vector<double> coords = convertToDoubleVector( coordArray );
|
||||
std::vector<double> zcorn = convertToDoubleVector( zcornArray );
|
||||
|
||||
if ( !deckFile.replaceKeywordData( "COORD", coords ) )
|
||||
{
|
||||
return std::unexpected( "Failed to replace COORD keyword in deck file" );
|
||||
}
|
||||
|
||||
if ( !deckFile.replaceKeywordData( "ZCORN", zcorn ) )
|
||||
{
|
||||
return std::unexpected( "Failed to replace ZCORN keyword in deck file" );
|
||||
}
|
||||
|
||||
if ( !deckFile.replaceKeywordData( "ACTNUM", actnumArray ) )
|
||||
{
|
||||
return std::unexpected( "Failed to replace ACTNUM keyword in deck file" );
|
||||
}
|
||||
|
||||
// TODO: deal with map axis
|
||||
return {};
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
std::expected<void, QString>
|
||||
RicExportEclipseSectorModelFeature::replaceKeywordValuesInDeckFile( RimEclipseCase* eclipseCase,
|
||||
const RicExportEclipseSectorModelUi& exportSettings,
|
||||
RifOpmFlowDeckFile& deckFile )
|
||||
{
|
||||
// Extract and replace keyword data for all keywords in the deck
|
||||
auto keywords = deckFile.keywords( false );
|
||||
RiaLogging::info( QString( "Processing %1 keywords from deck file" ).arg( keywords.size() ) );
|
||||
|
||||
for ( const auto& keywordStdStr : keywords )
|
||||
{
|
||||
QString keyword = QString::fromStdString( keywordStdStr );
|
||||
|
||||
// Skip special keywords that aren't cell properties
|
||||
if ( keyword.startsWith( "DATES" ) || keyword == "SCHEDULE" || keyword == "GRID" || keyword == "PROPS" || keyword == "SOLUTION" ||
|
||||
keyword == "RUNSPEC" || keyword == "SUMMARY" )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Try to extract keyword data
|
||||
auto result = RifEclipseInputFileTools::extractKeywordData( eclipseCase->eclipseCaseData(),
|
||||
keyword,
|
||||
exportSettings.min(),
|
||||
exportSettings.max(),
|
||||
exportSettings.refinement() );
|
||||
if ( result )
|
||||
{
|
||||
// Replace keyword values in deck with extracted data
|
||||
if ( deckFile.replaceKeywordData( keywordStdStr, result.value() ) )
|
||||
{
|
||||
RiaLogging::info(
|
||||
QString( "Successfully replaced data for keyword '%1' (%2 values)" ).arg( keyword ).arg( result.value().size() ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
RiaLogging::warning( QString( "Failed to replace keyword '%1' in deck" ).arg( keyword ) );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Not all keywords will have data - this is expected
|
||||
RiaLogging::debug(
|
||||
QString( "Could not extract data for keyword '%1': %2" ).arg( keyword ).arg( QString::fromStdString( result.error() ) ) );
|
||||
}
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
std::expected<void, QString> RicExportEclipseSectorModelFeature::addBorderBoundaryConditions( RimEclipseCase* eclipseCase,
|
||||
const RicExportEclipseSectorModelUi& exportSettings,
|
||||
RifOpmFlowDeckFile& deckFile )
|
||||
{
|
||||
// Generate border cell faces
|
||||
auto borderCellFaces = RigEclipseResultTools::generateBorderCellFaces( eclipseCase );
|
||||
|
||||
if ( !borderCellFaces.empty() )
|
||||
{
|
||||
// Add BCCON keyword
|
||||
if ( !deckFile.addBcconKeyword( "GRID", borderCellFaces ) )
|
||||
{
|
||||
return std::unexpected( "Failed to add BCCON keyword to deck file" );
|
||||
}
|
||||
|
||||
// Build BCPROP records from the UI configuration
|
||||
std::vector<Opm::DeckRecord> bcpropRecords;
|
||||
for ( const auto& bcprop : exportSettings.m_bcpropKeywords )
|
||||
{
|
||||
if ( bcprop != nullptr )
|
||||
{
|
||||
Opm::DeckKeyword kw = bcprop->keyword();
|
||||
const auto& record = kw.getRecord( 0 );
|
||||
bcpropRecords.push_back( record );
|
||||
}
|
||||
}
|
||||
|
||||
// Add BCPROP keyword
|
||||
if ( !deckFile.addBcpropKeyword( "GRID", borderCellFaces, bcpropRecords ) )
|
||||
{
|
||||
return std::unexpected( "Failed to add BCPROP keyword to deck file" );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
RiaLogging::info( QString( "Saved modified deck file to '%1/%2'" ).arg( outputFolder ).arg( outputFile ) );
|
||||
RiaLogging::warning( "No border cells found - skipping BCCON/BCPROP keyword generation" );
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
std::expected<void, QString> RicExportEclipseSectorModelFeature::addFaultsToDeckFile( RimEclipseCase* eclipseCase,
|
||||
const RicExportEclipseSectorModelUi& exportSettings,
|
||||
RifOpmFlowDeckFile& deckFile )
|
||||
{
|
||||
// Create FAULTS keyword using the factory
|
||||
Opm::DeckKeyword faultsKw =
|
||||
RimKeywordFactory::faultsKeyword( eclipseCase->mainGrid(), exportSettings.min(), exportSettings.max(), exportSettings.refinement() );
|
||||
|
||||
// Replace FAULTS keyword in GRID section
|
||||
if ( !deckFile.replaceKeyword( "GRID", faultsKw ) )
|
||||
{
|
||||
return std::unexpected( "Failed to replace FAULTS keyword in deck file" );
|
||||
}
|
||||
|
||||
RiaLogging::info( "Successfully replaced FAULTS keyword in deck file" );
|
||||
return {};
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
@@ -524,7 +613,8 @@ cvf::ref<cvf::UByteArray>
|
||||
}
|
||||
case RicExportEclipseSectorModelUi::ACTIVE_CELLS_BOX:
|
||||
{
|
||||
// For active cells, we need to create a visibility array based on active cells
|
||||
// For active cells, we need to create a
|
||||
// visibility array based on active cells
|
||||
auto activeCellInfo = caseData->activeCellInfo( RiaDefines::PorosityModelType::MATRIX_MODEL );
|
||||
auto activeReservoirCells = activeCellInfo->activeReservoirCellIndices();
|
||||
size_t totalCellCount = caseData->mainGrid()->cellCount();
|
||||
@@ -540,7 +630,8 @@ cvf::ref<cvf::UByteArray>
|
||||
}
|
||||
case RicExportEclipseSectorModelUi::VISIBLE_CELLS_BOX:
|
||||
{
|
||||
// Use the current total cell visibility from the view
|
||||
// Use the current total cell visibility
|
||||
// from the view
|
||||
cvf::ref<cvf::UByteArray> cellVisibility = new cvf::UByteArray();
|
||||
view->calculateCurrentTotalCellVisibility( cellVisibility.p(), view->currentTimeStep() );
|
||||
return cellVisibility;
|
||||
@@ -551,7 +642,8 @@ cvf::ref<cvf::UByteArray>
|
||||
}
|
||||
case RicExportEclipseSectorModelUi::FULL_GRID_BOX:
|
||||
{
|
||||
// For full grid, create visibility for all cells
|
||||
// For full grid, create visibility for
|
||||
// all cells
|
||||
const RigMainGrid* mainGrid = caseData->mainGrid();
|
||||
const cvf::Vec3st minIjk = cvf::Vec3st::ZERO;
|
||||
const cvf::Vec3st maxIjk = mainGrid->cellCounts();
|
||||
|
||||
@@ -23,8 +23,12 @@
|
||||
#include "cvfArray.h"
|
||||
#include "cvfVector3.h"
|
||||
|
||||
#include <expected>
|
||||
|
||||
class RimEclipseView;
|
||||
class RimEclipseCase;
|
||||
class RicExportEclipseSectorModelUi;
|
||||
class RifOpmFlowDeckFile;
|
||||
|
||||
//==================================================================================================
|
||||
///
|
||||
@@ -45,11 +49,27 @@ protected:
|
||||
void setupActionLook( QAction* actionToSetup ) override;
|
||||
|
||||
private:
|
||||
RimEclipseView* selectedView() const;
|
||||
static cvf::ref<cvf::UByteArray> createVisibilityBasedOnBoxSelection( RimEclipseView* view,
|
||||
const RicExportEclipseSectorModelUi& exportSettings );
|
||||
static void exportSimulationInput( RimEclipseView* view, const RicExportEclipseSectorModelUi& exportSettings );
|
||||
static void exportGrid( RimEclipseView* view, const RicExportEclipseSectorModelUi& exportSettings );
|
||||
static void exportFaults( RimEclipseView* view, const RicExportEclipseSectorModelUi& exportSettings );
|
||||
static void exportParameters( RimEclipseView* view, const RicExportEclipseSectorModelUi& exportSettings );
|
||||
RimEclipseView* selectedView() const;
|
||||
static cvf::ref<cvf::UByteArray> createVisibilityBasedOnBoxSelection( RimEclipseView* view,
|
||||
const RicExportEclipseSectorModelUi& exportSettings );
|
||||
static std::expected<void, QString> exportSimulationInput( RimEclipseCase& eclipseCase,
|
||||
const RicExportEclipseSectorModelUi& exportSettings );
|
||||
static void exportGrid( RimEclipseView* view, const RicExportEclipseSectorModelUi& exportSettings );
|
||||
static void exportFaults( RimEclipseView* view, const RicExportEclipseSectorModelUi& exportSettings );
|
||||
static void exportParameters( RimEclipseView* view, const RicExportEclipseSectorModelUi& exportSettings );
|
||||
|
||||
static std::expected<void, QString>
|
||||
addFaultsToDeckFile( RimEclipseCase* eclipseCase, const RicExportEclipseSectorModelUi& exportSettings, RifOpmFlowDeckFile& deckFile );
|
||||
|
||||
static std::expected<void, QString> addBorderBoundaryConditions( RimEclipseCase* eclipseCase,
|
||||
const RicExportEclipseSectorModelUi& exportSettings,
|
||||
RifOpmFlowDeckFile& deckFile );
|
||||
|
||||
static std::expected<void, QString> replaceKeywordValuesInDeckFile( RimEclipseCase* eclipseCase,
|
||||
const RicExportEclipseSectorModelUi& exportSettings,
|
||||
RifOpmFlowDeckFile& deckFile );
|
||||
|
||||
static std::expected<void, QString> updateCornerPointGridInDeckFile( RimEclipseCase* eclipseCase,
|
||||
const RicExportEclipseSectorModelUi& exportSettings,
|
||||
RifOpmFlowDeckFile& deckFile );
|
||||
};
|
||||
|
||||
@@ -445,6 +445,19 @@ std::vector<std::string> RifOpmFlowDeckFile::keywords( bool includeDates )
|
||||
return values;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
std::optional<Opm::DeckKeyword> RifOpmFlowDeckFile::findKeyword( const std::string& keyword )
|
||||
{
|
||||
if ( m_fileDeck.get() == nullptr ) return std::nullopt;
|
||||
|
||||
auto keywordIdx = m_fileDeck->find( keyword );
|
||||
if ( !keywordIdx.has_value() ) return std::nullopt;
|
||||
|
||||
return m_fileDeck->operator[]( keywordIdx.value() );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
@@ -731,6 +744,8 @@ bool RifOpmFlowDeckFile::addIncludeKeyword( std::string section, std::string key
|
||||
|
||||
// Create the INCLUDE keyword
|
||||
Opm::DeckKeyword includeKw( ( Opm::ParserKeywords::INCLUDE() ) );
|
||||
m_fileDeck->erase( insertIdx.value() );
|
||||
|
||||
includeKw.addRecord( Opm::DeckRecord{ { RifOpmDeckTools::item( Opm::ParserKeywords::INCLUDE::IncludeFile::itemName, filePath ) } } );
|
||||
|
||||
m_fileDeck->insert( insertIdx.value(), includeKw );
|
||||
@@ -920,6 +935,35 @@ bool RifOpmFlowDeckFile::addBcpropKeyword( std::string
|
||||
return true;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
bool RifOpmFlowDeckFile::replaceKeyword( const std::string& section, const Opm::DeckKeyword& keyword )
|
||||
{
|
||||
if ( m_fileDeck.get() == nullptr ) return false;
|
||||
|
||||
// Find and replace keyword in deck
|
||||
auto keywordIdx = m_fileDeck->find( keyword.name() );
|
||||
if ( keywordIdx.has_value() )
|
||||
{
|
||||
// Replace existing keyword
|
||||
m_fileDeck->erase( keywordIdx.value() );
|
||||
m_fileDeck->insert( keywordIdx.value(), keyword );
|
||||
}
|
||||
else
|
||||
{
|
||||
// Add keyword to specified section if it doesn't exist
|
||||
auto insertPos = internal::findSectionInsertionPoint( m_fileDeck, section );
|
||||
if ( !insertPos.has_value() )
|
||||
{
|
||||
return false; // Section not found
|
||||
}
|
||||
m_fileDeck->insert( insertPos.value(), keyword );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
|
||||
@@ -63,12 +63,13 @@ public:
|
||||
bool restartAtTimeStep( int timeStep, std::string deckName );
|
||||
bool stopAtTimeStep( int timeStep );
|
||||
|
||||
std::vector<std::string> keywords( bool includeDates = true );
|
||||
bool hasDatesKeyword();
|
||||
bool isRestartFile();
|
||||
std::vector<std::string> dateStrings();
|
||||
std::vector<std::time_t> dates();
|
||||
bool appendDateKeywords( const std::vector<std::time_t>& dates );
|
||||
std::vector<std::string> keywords( bool includeDates = true );
|
||||
std::optional<Opm::DeckKeyword> findKeyword( const std::string& keyword );
|
||||
bool hasDatesKeyword();
|
||||
bool isRestartFile();
|
||||
std::vector<std::string> dateStrings();
|
||||
std::vector<std::time_t> dates();
|
||||
bool appendDateKeywords( const std::vector<std::time_t>& dates );
|
||||
|
||||
std::set<std::string> wellGroupsInFile();
|
||||
|
||||
@@ -96,6 +97,8 @@ public:
|
||||
bool replaceKeywordData( const std::string& keyword, const std::vector<double>& data );
|
||||
bool replaceKeywordData( const std::string& keyword, const std::vector<int>& data );
|
||||
|
||||
bool replaceKeyword( const std::string& section, const Opm::DeckKeyword& keyword );
|
||||
|
||||
private:
|
||||
void splitDatesIfNecessary();
|
||||
|
||||
|
||||
@@ -20,17 +20,26 @@
|
||||
|
||||
#include "Commands/CompletionExportCommands/RicWellPathExportCompletionDataFeatureImpl.h"
|
||||
|
||||
#include "RiaResultNames.h"
|
||||
|
||||
#include "RifEclipseInputFileTools.h"
|
||||
#include "RifOpmDeckTools.h"
|
||||
|
||||
#include "RigFault.h"
|
||||
#include "RigMainGrid.h"
|
||||
|
||||
#include "RimEclipseCase.h"
|
||||
#include "RimWellPath.h"
|
||||
#include "RimWellPathCompletionSettings.h"
|
||||
|
||||
#include "cvfStructGrid.h"
|
||||
|
||||
#include "opm/input/eclipse/Deck/DeckItem.hpp"
|
||||
#include "opm/input/eclipse/Deck/DeckKeyword.hpp"
|
||||
#include "opm/input/eclipse/Deck/DeckRecord.hpp"
|
||||
#include "opm/input/eclipse/Parser/ParserKeyword.hpp"
|
||||
#include "opm/input/eclipse/Parser/ParserKeywords/C.hpp"
|
||||
#include "opm/input/eclipse/Parser/ParserKeywords/F.hpp"
|
||||
#include "opm/input/eclipse/Parser/ParserKeywords/W.hpp"
|
||||
|
||||
//==================================================================================================
|
||||
@@ -146,4 +155,138 @@ Opm::DeckKeyword compdatKeyword( RimEclipseCase* eCase, RimWellPath* wellPath )
|
||||
return kw;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
Opm::DeckKeyword faultsKeyword( const RigMainGrid* mainGrid, const cvf::Vec3st& min, const cvf::Vec3st& max, const cvf::Vec3st& 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;
|
||||
}
|
||||
|
||||
} // namespace RimKeywordFactory
|
||||
|
||||
@@ -18,8 +18,11 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "cvfVector3.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
class RigMainGrid;
|
||||
class RimEclipseCase;
|
||||
class RimWellPath;
|
||||
|
||||
@@ -37,5 +40,9 @@ namespace RimKeywordFactory
|
||||
|
||||
Opm::DeckKeyword welspecsKeyword( const std::string wellGrpName, RimEclipseCase* eCase, RimWellPath* wellPath );
|
||||
Opm::DeckKeyword compdatKeyword( RimEclipseCase* eCase, RimWellPath* wellPath );
|
||||
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 ) );
|
||||
|
||||
} // namespace RimKeywordFactory
|
||||
|
||||
Reference in New Issue
Block a user