Roff support with submodule

* Roff: Add initial roff support (with roffcpp as submodule).
* Roff: Create viewer when importing roff files.
* Roff: add timing for roff import.
* Roff: precompute active cell indexes.
* Roff: parallelize using OpenMP.
* Roff: Handle exceptions when importing.
* Roff: improvements from review.
* Invert ordering

Co-authored-by: Magne Sjaastad <magne.sjaastad@ceetronsolutions.com>
This commit is contained in:
Kristian Bendiksen 2022-12-19 15:28:26 +01:00 committed by GitHub
parent 38bfa9ef1f
commit 8b51160ac4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 968 additions and 2 deletions

3
.gitmodules vendored
View File

@ -13,3 +13,6 @@
[submodule "ThirdParty/custom-opm-common/opm-common"]
path = ThirdParty/custom-opm-common/opm-common
url = https://github.com/CeetronSolutions/opm-common
[submodule "ThirdParty/roffcpp"]
path = ThirdParty/roffcpp
url = https://github.com/CeetronSolutions/roffcpp

View File

@ -271,6 +271,10 @@ RiaDefines::ImportFileType RiaDefines::obtainFileTypeFromFileName( const QString
{
return ImportFileType::ECLIPSE_INPUT_FILE;
}
else if ( fileName.endsWith( "ROFF", Qt::CaseInsensitive ) || fileName.endsWith( "ROFFASC", Qt::CaseInsensitive ) )
{
return ImportFileType::ROFF_FILE;
}
else if ( fileName.endsWith( "SMSPEC", Qt::CaseInsensitive ) )
{
return ImportFileType::ECLIPSE_SUMMARY_FILE;

View File

@ -126,8 +126,9 @@ enum class ImportFileType
ECLIPSE_SUMMARY_FILE = 0x08,
GEOMECH_ODB_FILE = 0x10,
RESINSIGHT_PROJECT_FILE = 0x20,
ROFF_FILE = 0x30,
ECLIPSE_RESULT_GRID = ECLIPSE_GRID_FILE | ECLIPSE_EGRID_FILE,
ANY_ECLIPSE_FILE = ECLIPSE_RESULT_GRID | ECLIPSE_INPUT_FILE | ECLIPSE_SUMMARY_FILE,
ANY_ECLIPSE_FILE = ECLIPSE_RESULT_GRID | ECLIPSE_INPUT_FILE | ECLIPSE_SUMMARY_FILE | ROFF_FILE,
ANY_IMPORT_FILE = 0xFF
};

View File

@ -47,6 +47,7 @@
#include "RimMainPlotCollection.h"
#include "RimOilField.h"
#include "RimProject.h"
#include "RimRoffCase.h"
#include "RimSummaryCase.h"
#include "RimSummaryCaseCollection.h"
#include "RimSummaryCaseMainCollection.h"
@ -508,3 +509,51 @@ bool RiaImportEclipseCaseTools::addEclipseCases( const QStringList& fil
return true;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
int RiaImportEclipseCaseTools::openRoffCaseFromFileNames( const QStringList& fileNames, bool createDefaultView )
{
CAF_ASSERT( !fileNames.empty() );
auto* roffCase = new RimRoffCase();
RiaApplication* app = RiaApplication::instance();
RimProject* project = app->project();
project->assignCaseIdToCase( roffCase );
roffCase->setGridFileName( fileNames[0] );
bool gridImportSuccess = roffCase->openEclipseGridFile();
if ( !gridImportSuccess )
{
RiaLogging::error( "Failed to import grid" );
return -1;
}
RimEclipseCaseCollection* analysisModels = project->activeOilField() ? project->activeOilField()->analysisModels()
: nullptr;
if ( !analysisModels ) return -1;
analysisModels->cases.push_back( roffCase );
RimEclipseView* eclipseView = nullptr;
if ( createDefaultView )
{
eclipseView = roffCase->createAndAddReservoirView();
eclipseView->cellResult()->setResultType( RiaDefines::ResultCatType::INPUT_PROPERTY );
if ( RiaGuiApplication::isRunning() )
{
if ( RiuMainWindow::instance() )
RiuMainWindow::instance()->selectAsCurrentItem( eclipseView->cellResult() );
}
eclipseView->loadDataAndUpdate();
}
analysisModels->updateConnectedEditors();
return roffCase->caseId();
}

View File

@ -52,6 +52,8 @@ public:
bool createView,
std::shared_ptr<RifReaderSettings> readerSettings = nullptr );
static int openRoffCaseFromFileNames( const QStringList& fileNames, bool createDefaultView );
private:
static int openEclipseCaseShowTimeStepFilterImpl( const QString& fileName,
bool showTimeStepFilter,

View File

@ -19,6 +19,7 @@ set(SOURCE_GROUP_HEADER_FILES
${CMAKE_CURRENT_LIST_DIR}/RicEclipseHideFaultFeature.h
${CMAKE_CURRENT_LIST_DIR}/RicEclipseShowOnlyFaultFeature.h
${CMAKE_CURRENT_LIST_DIR}/RicRenameCaseFeature.h
${CMAKE_CURRENT_LIST_DIR}/RicImportRoffCaseFeature.h
)
set(SOURCE_GROUP_SOURCE_FILES
@ -42,6 +43,7 @@ set(SOURCE_GROUP_SOURCE_FILES
${CMAKE_CURRENT_LIST_DIR}/RicEclipseHideFaultFeature.cpp
${CMAKE_CURRENT_LIST_DIR}/RicEclipseShowOnlyFaultFeature.cpp
${CMAKE_CURRENT_LIST_DIR}/RicRenameCaseFeature.cpp
${CMAKE_CURRENT_LIST_DIR}/RicImportRoffCaseFeature.cpp
)
list(APPEND COMMAND_CODE_HEADER_FILES ${SOURCE_GROUP_HEADER_FILES})

View File

@ -0,0 +1,48 @@
/////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2022- Equinor 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 "RicImportRoffCaseFeature.h"
#include <QAction>
CAF_CMD_SOURCE_INIT( RicImportRoffCaseFeature, "RicImportRoffCaseFeature" );
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
bool RicImportRoffCaseFeature::isCommandEnabled()
{
return true;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RicImportRoffCaseFeature::onActionTriggered( bool isChecked )
{
RicImportGeneralDataFeature::openFileDialog( RiaDefines::ImportFileType::ROFF_FILE );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RicImportRoffCaseFeature::setupActionLook( QAction* actionToSetup )
{
actionToSetup->setIcon( QIcon( ":/EclipseInput48x48.png" ) );
actionToSetup->setText( "Import Roff Case" );
}

View File

@ -0,0 +1,34 @@
/////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2022- Equinor 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.
//
/////////////////////////////////////////////////////////////////////////////////
#pragma once
#include "RicImportGeneralDataFeature.h"
//==================================================================================================
///
//==================================================================================================
class RicImportRoffCaseFeature : public RicImportGeneralDataFeature
{
CAF_CMD_HEADER_INIT;
protected:
bool isCommandEnabled() override;
void onActionTriggered( bool isChecked ) override;
void setupActionLook( QAction* actionToSetup ) override;
};

View File

@ -55,6 +55,7 @@ RicImportGeneralDataFeature::OpenCaseResults
QStringList eclipseCaseFiles;
QStringList eclipseInputFiles;
QStringList eclipseSummaryFiles;
QStringList roffFiles;
for ( const QString& fileName : fileNames )
{
@ -71,6 +72,10 @@ RicImportGeneralDataFeature::OpenCaseResults
{
eclipseSummaryFiles.push_back( fileName );
}
else if ( fileTypeAsInt & int( ImportFileType::ROFF_FILE ) )
{
roffFiles.push_back( fileName );
}
}
OpenCaseResults results;
@ -104,6 +109,17 @@ RicImportGeneralDataFeature::OpenCaseResults
RiaApplication::instance()->setLastUsedDialogDirectory( defaultDirectoryLabel( ImportFileType::ECLIPSE_SUMMARY_FILE ),
defaultDir );
}
if ( !roffFiles.empty() )
{
if ( !openRoffCaseFromFileNames( roffFiles, createDefaultView, results.createdCaseIds ) )
{
return OpenCaseResults();
}
results.roffFiles = roffFiles;
RiaApplication::instance()->setLastUsedDialogDirectory( defaultDirectoryLabel( ImportFileType::ROFF_FILE ),
defaultDir );
}
return results;
}
@ -167,6 +183,7 @@ QStringList RicImportGeneralDataFeature::getEclipseFileNamesWithDialog( RiaDefin
QString eclipseEGridFilePattern( "*.EGRID" );
QString eclipseInputFilePattern( "*.GRDECL" );
QString eclipseSummaryFilePattern( "*.SMSPEC" );
QString roffFilePattern( "*.ROFF *.ROFFASC" );
QStringList filePatternTexts;
if ( fileType == ImportFileType::ANY_ECLIPSE_FILE )
@ -196,6 +213,10 @@ QStringList RicImportGeneralDataFeature::getEclipseFileNamesWithDialog( RiaDefin
{
filePatternTexts += QString( "Eclipse Summary File (%1)" ).arg( eclipseSummaryFilePattern );
}
if ( fileTypeAsInt & int( ImportFileType::ROFF_FILE ) )
{
filePatternTexts += QString( "Roff File (%1)" ).arg( roffFilePattern );
}
QString fullPattern = filePatternTexts.join( ";;" );
@ -289,3 +310,22 @@ bool RicImportGeneralDataFeature::openSummaryCaseFromFileNames( const QStringLis
}
return false;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
bool RicImportGeneralDataFeature::openRoffCaseFromFileNames( const QStringList& fileNames,
bool createDefaultView,
std::vector<int>& createdCaseIds )
{
CAF_ASSERT( !fileNames.empty() );
auto generatedCaseId = RiaImportEclipseCaseTools::openRoffCaseFromFileNames( fileNames, createDefaultView );
if ( generatedCaseId >= 0 )
{
RiaApplication::instance()->addToRecentFiles( fileNames[0] );
createdCaseIds.push_back( generatedCaseId );
return true;
}
return false;
}

View File

@ -42,12 +42,14 @@ public:
QStringList eclipseCaseFiles;
QStringList eclipseInputFiles;
QStringList eclipseSummaryFiles;
QStringList roffFiles;
std::vector<int> createdCaseIds;
operator bool() const
{
return !( eclipseCaseFiles.empty() && eclipseInputFiles.empty() && eclipseSummaryFiles.empty() );
return !( eclipseCaseFiles.empty() && eclipseInputFiles.empty() && eclipseSummaryFiles.empty() &&
roffFiles.empty() );
}
};
@ -73,4 +75,7 @@ protected:
bool createDefaultView,
std::vector<int>& createdCaseIds );
static bool openSummaryCaseFromFileNames( const QStringList& fileNames, bool doCreateDefaultPlot = true );
static bool openRoffCaseFromFileNames( const QStringList& fileNames,
bool createDefaultView,
std::vector<int>& createdCaseIds );
};

View File

@ -1,6 +1,7 @@
set(SOURCE_GROUP_HEADER_FILES
${CMAKE_CURRENT_LIST_DIR}/RifTextDataTableFormatter.h
${CMAKE_CURRENT_LIST_DIR}/RifEclipseInputFileTools.h
${CMAKE_CURRENT_LIST_DIR}/RifRoffFileTools.h
${CMAKE_CURRENT_LIST_DIR}/RifEclipseOutputFileTools.h
${CMAKE_CURRENT_LIST_DIR}/RifEclipseRestartDataAccess.h
${CMAKE_CURRENT_LIST_DIR}/RifEclipseRestartFilesetAccess.h
@ -78,6 +79,7 @@ set(SOURCE_GROUP_HEADER_FILES
set(SOURCE_GROUP_SOURCE_FILES
${CMAKE_CURRENT_LIST_DIR}/RifTextDataTableFormatter.cpp
${CMAKE_CURRENT_LIST_DIR}/RifEclipseInputFileTools.cpp
${CMAKE_CURRENT_LIST_DIR}/RifRoffFileTools.cpp
${CMAKE_CURRENT_LIST_DIR}/RifEclipseOutputFileTools.cpp
${CMAKE_CURRENT_LIST_DIR}/RifEclipseRestartDataAccess.cpp
${CMAKE_CURRENT_LIST_DIR}/RifEclipseRestartFilesetAccess.cpp

View File

@ -0,0 +1,510 @@
/////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2022- Equinor 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 "RifRoffFileTools.h"
#include "RiaLogging.h"
#include "RigActiveCellInfo.h"
#include "RigCaseCellResultsData.h"
#include "RigEclipseCaseData.h"
#include "RigMainGrid.h"
#include "cafProgressInfo.h"
#include <chrono>
#include <cmath>
#include <fstream>
#include <iostream>
#include <vector>
#include "Reader.hpp"
#ifdef USE_OPENMP
#include <omp.h>
#endif
using namespace std::chrono;
//--------------------------------------------------------------------------------------------------
/// Constructor
//--------------------------------------------------------------------------------------------------
RifRoffFileTools::RifRoffFileTools()
{
}
//--------------------------------------------------------------------------------------------------
/// Destructor
//--------------------------------------------------------------------------------------------------
RifRoffFileTools::~RifRoffFileTools()
{
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
bool RifRoffFileTools::openGridFile( const QString& fileName, RigEclipseCaseData* eclipseCase, QString* errorMessages )
{
RiaLogging::info( QString( "Opening roff file: %1" ).arg( fileName ) );
std::string filename = fileName.toStdString();
std::ifstream stream( filename, std::ios::binary );
if ( !stream.good() )
{
if ( errorMessages ) *errorMessages = QString( "Unable to open roff file" );
return false;
}
auto getInt = []( auto values, const std::string& name ) {
auto v = std::find_if( values.begin(), values.end(), [&name]( const auto& arg ) { return arg.first == name; } );
if ( v != values.end() )
return std::get<int>( v->second );
else
throw std::runtime_error( "Missing parameter (integer): " + name );
};
auto getFloat = []( auto values, const std::string& name ) {
auto v = std::find_if( values.begin(), values.end(), [&name]( const auto& arg ) { return arg.first == name; } );
if ( v != values.end() )
return std::get<float>( v->second );
else
throw std::runtime_error( "Missing parameter (float): " + name );
};
try
{
const auto totalStart = high_resolution_clock::now();
Reader reader( stream );
reader.parse();
const auto tokenizeDone = high_resolution_clock::now();
std::vector<std::pair<std::string, RoffScalar>> values = reader.scalarNamedValues();
std::vector<std::pair<std::string, Token::Kind>> arrayTypes = reader.getNamedArrayTypes();
size_t nx = getInt( values, "dimensions.nX" );
size_t ny = getInt( values, "dimensions.nY" );
size_t nz = getInt( values, "dimensions.nZ" );
RiaLogging::info( QString( "Grid dimensions: %1 %2 %3" ).arg( nx ).arg( ny ).arg( nz ) );
float xOffset = getFloat( values, "translate.xoffset" );
float yOffset = getFloat( values, "translate.yoffset" );
float zOffset = getFloat( values, "translate.zoffset" );
RiaLogging::info( QString( "Offset: %1 %2 %3" ).arg( xOffset ).arg( yOffset ).arg( zOffset ) );
float xScale = getFloat( values, "scale.xscale" );
float yScale = getFloat( values, "scale.yscale" );
float zScale = getFloat( values, "scale.zscale" );
RiaLogging::info( QString( "Scale: %1 %2 %3" ).arg( xScale ).arg( yScale ).arg( zScale ) );
std::vector<int> layers = reader.getIntArray( "subgrids.nLayers" );
std::vector<float> cornerLines = reader.getFloatArray( "cornerLines.data" );
std::vector<float> zValues = reader.getFloatArray( "zvalues.data" );
std::vector<char> splitEnz = reader.getByteArray( "zvalues.splitEnz" );
std::vector<char> active = reader.getByteArray( "active.data" );
const auto parsingDone = high_resolution_clock::now();
RiaLogging::info( QString( "Layers: %1" ).arg( layers.size() ) );
RiaLogging::info( QString( "Corner lines: %1" ).arg( cornerLines.size() ) );
RiaLogging::info( QString( "Z values: %1" ).arg( zValues.size() ) );
RiaLogging::info( QString( "Splitenz: %1" ).arg( splitEnz.size() ) );
RiaLogging::info( QString( "Active: %1" ).arg( active.size() ) );
unsigned int zCornerSize = static_cast<unsigned int>( ( nx + 1 ) * ( ny + 1 ) * ( nz + 1 ) * 4u );
std::vector<float> zCorners( zCornerSize, 0.0 );
interpretSplitenzData( static_cast<int>( nz ) + 1, zOffset, zScale, splitEnz, zValues, zCorners );
RiaLogging::info( QString( "zCorners: %1" ).arg( zCorners.size() ) );
RigActiveCellInfo* activeCellInfo = eclipseCase->activeCellInfo( RiaDefines::PorosityModelType::MATRIX_MODEL );
CVF_ASSERT( activeCellInfo );
RigActiveCellInfo* fractureActiveCellInfo =
eclipseCase->activeCellInfo( RiaDefines::PorosityModelType::FRACTURE_MODEL );
CVF_ASSERT( fractureActiveCellInfo );
RigMainGrid* mainGrid = eclipseCase->mainGrid();
CVF_ASSERT( mainGrid );
cvf::Vec3st gridPointDim( nx + 1, ny + 1, nz + 1 );
mainGrid->setGridPointDimensions( gridPointDim );
mainGrid->setGridName( "Main grid" );
size_t totalCellCount = nx * ny * nz;
activeCellInfo->setGridCount( 1 );
fractureActiveCellInfo->setGridCount( 1 );
activeCellInfo->setReservoirCellCount( totalCellCount );
fractureActiveCellInfo->setReservoirCellCount( totalCellCount );
// Reserve room for the cells and nodes and fill them with data
mainGrid->globalCellArray().reserve( totalCellCount );
mainGrid->nodes().reserve( 8 * totalCellCount );
int progTicks = 100;
caf::ProgressInfo progInfo( progTicks, "" );
int cellCount = static_cast<int>( totalCellCount );
size_t cellStartIndex = mainGrid->globalCellArray().size();
size_t nodeStartIndex = mainGrid->nodes().size();
RigCell defaultCell;
defaultCell.setHostGrid( mainGrid );
mainGrid->globalCellArray().resize( cellStartIndex + cellCount, defaultCell );
mainGrid->nodes().resize( nodeStartIndex + static_cast<size_t>( cellCount ) * 8, cvf::Vec3d( 0, 0, 0 ) );
const size_t cellMappingECLRi[8] = { 0, 1, 3, 2, 4, 5, 7, 6 };
cvf::Vec3d offset( xOffset, yOffset, zOffset );
cvf::Vec3d scale( xScale, yScale, zScale );
std::vector<int> activeCells;
convertToReservoirIndexOrder( nx, ny, nz, active, activeCells );
// Precompute the active cell matrix index
size_t numActiveCells = computeActiveCellMatrixIndex( activeCells );
// Loop over cells and fill them with data
#pragma omp parallel
{
int cellCountPerThread = cellCount;
#ifdef USE_OPENMP
cellCountPerThread = std::max( 1, cellCount / omp_get_num_threads() );
#endif
int computedThreadCellCount = 0;
int cellsPrProgressTick = std::max( 1, cellCountPerThread / progTicks );
int maxProgressCell = cellsPrProgressTick * progTicks;
#pragma omp for
for ( int gridLocalCellIndex = 0; gridLocalCellIndex < cellCount; ++gridLocalCellIndex )
{
RigCell& cell = mainGrid->globalCellArray()[cellStartIndex + gridLocalCellIndex];
cell.setGridLocalCellIndex( gridLocalCellIndex );
// Active cell index
int matrixActiveIndex = activeCells[gridLocalCellIndex];
if ( matrixActiveIndex != -1 )
{
activeCellInfo->setCellResultIndex( cellStartIndex + gridLocalCellIndex, matrixActiveIndex );
}
cell.setParentCellIndex( cvf::UNDEFINED_SIZE_T );
// Corner coordinates
for ( int cIdx = 0; cIdx < 8; ++cIdx )
{
double* point =
mainGrid->nodes()[nodeStartIndex + (size_t)gridLocalCellIndex * 8 + cellMappingECLRi[cIdx]].ptr();
auto corner = getCorner( *mainGrid, cornerLines, zCorners, gridLocalCellIndex, cIdx, offset, scale );
point[0] = corner.x();
point[1] = corner.y();
point[2] = corner.z();
cell.cornerIndices()[cIdx] = nodeStartIndex + (size_t)gridLocalCellIndex * 8 + cIdx;
}
// Mark inactive long pyramid looking cells as invalid
cell.setInvalid( cell.isLongPyramidCell() );
#ifdef USE_OPENMP
if ( omp_get_thread_num() == 0 )
{
computedThreadCellCount++;
if ( computedThreadCellCount <= maxProgressCell && computedThreadCellCount % cellsPrProgressTick == 0 )
progInfo.incrementProgress();
}
#else
computedThreadCellCount++;
if ( computedThreadCellCount <= maxProgressCell && computedThreadCellCount % cellsPrProgressTick == 0 )
progInfo.incrementProgress();
#endif
}
}
activeCellInfo->setGridActiveCellCounts( 0, numActiveCells );
fractureActiveCellInfo->setGridActiveCellCounts( 0, 0 );
mainGrid->initAllSubGridsParentGridPointer();
activeCellInfo->computeDerivedData();
fractureActiveCellInfo->computeDerivedData();
auto gridConstructionDone = high_resolution_clock::now();
auto tokenizeDuration = duration_cast<milliseconds>( tokenizeDone - totalStart );
RiaLogging::info( QString( "Tokenizing: %1 ms" ).arg( tokenizeDuration.count() ) );
auto parsingDuration = duration_cast<milliseconds>( parsingDone - tokenizeDone );
RiaLogging::info( QString( "Parsing: %1 ms" ).arg( parsingDuration.count() ) );
auto gridConstructionDuration = duration_cast<milliseconds>( gridConstructionDone - parsingDone );
RiaLogging::info( QString( "Grid Construction: %1 ms" ).arg( gridConstructionDuration.count() ) );
auto totalDuration = duration_cast<milliseconds>( gridConstructionDone - totalStart );
RiaLogging::info( QString( "Total: %1 ms" ).arg( totalDuration.count() ) );
}
catch ( std::runtime_error& err )
{
RiaLogging::error( QString( "Roff file import failed: %1" ).arg( err.what() ) );
return false;
}
return true;
}
//--------------------------------------------------------------------------------------------------
// The indexing conventions for vertices in Roff file
//
// 1-------------0
// /| /|
// / | / | /j
// / | / | /
// 3-------------2 | *---i
// | | | | |
// | 5---------|---4 |
// | / | / |k
// | / | /
// |/ |/
// 7-------------6
// vertex indices
//
//
// The indexing conventions for vertices in ResInsight
//
// 7-------------6 |k
// /| /| | /j
// / | / | |/
// / | / | *---i
// 4-------------5 |
// | | | |
// | 3---------|---2
// | / | /
// | / | /
// |/ |/
// 0-------------1
// vertex indices
//
//--------------------------------------------------------------------------------------------------
cvf::Vec3d RifRoffFileTools::getCorner( const RigMainGrid& grid,
const std::vector<float>& cornerLines,
const std::vector<float>& zcorn,
size_t cellIdx,
int cornerIdx,
const cvf::Vec3d& offset,
const cvf::Vec3d& scale )
{
size_t iOffset = 0;
if ( !( cornerIdx == 1 || cornerIdx == 3 || cornerIdx == 5 || cornerIdx == 7 ) )
{
iOffset = 1;
}
size_t jOffset = 0;
if ( !( cornerIdx == 2 || cornerIdx == 3 || cornerIdx == 6 || cornerIdx == 7 ) )
{
jOffset = 1;
}
size_t i;
size_t j;
size_t k;
grid.ijkFromCellIndex( cellIdx, &i, &j, &k );
size_t ny = grid.cellCountJ();
size_t nz = grid.cellCountK();
size_t cOffset = ( ( i + iOffset ) * ( ny + 1 ) + ( j + jOffset ) ) * 6;
cvf::Vec3d top( cornerLines[cOffset], cornerLines[cOffset + 1], cornerLines[cOffset + 2] );
cvf::Vec3d bottom( cornerLines[cOffset + 3], cornerLines[cOffset + 4], cornerLines[cOffset + 5] );
int adjustedCornerIdx = cornerIdx;
if ( cornerIdx == 4 ) adjustedCornerIdx = 0;
if ( cornerIdx == 5 ) adjustedCornerIdx = 1;
if ( cornerIdx == 6 ) adjustedCornerIdx = 2;
if ( cornerIdx == 7 ) adjustedCornerIdx = 3;
// Find the correct offset k direction
size_t kOffset = 0;
if ( cornerIdx > 3 )
{
kOffset = 1;
}
size_t zOffset = ( ( i + iOffset ) * ( ny + 1 ) * ( nz + 1 ) + ( j + jOffset ) * ( nz + 1 ) + ( k + kOffset ) ) * 4 +
adjustedCornerIdx;
double z = -zcorn[zOffset];
double x = interpolate( top, bottom, z, 0 ) + offset.x();
double y = interpolate( top, bottom, z, 1 ) + offset.y();
cvf::Vec3d result = cvf::Vec3d( x, y, z );
return result;
}
//--------------------------------------------------------------------------------------------------
/// Adapted from xtgeo
//--------------------------------------------------------------------------------------------------
double RifRoffFileTools::interpolate( const cvf::Vec3d& top, const cvf::Vec3d& bottom, double z, int idx )
{
if ( fabs( bottom[idx] - top[idx] ) > 0.01 )
{
return top[idx] - ( z - top.z() ) * ( top[idx] - bottom[idx] ) / ( bottom.z() - top.z() );
}
else
{
return top[idx];
}
}
//--------------------------------------------------------------------------------------------------
/// Adapted from xtgeo: https://github.com/equinor/xtgeo/blob/master/src/clib/xtg/grd3d_roff2xtgeo_splitenz.c
//--------------------------------------------------------------------------------------------------
void RifRoffFileTools::interpretSplitenzData( int nz,
float zoffset,
float zscale,
const std::vector<char>& splitenz,
const std::vector<float>& zdata,
std::vector<float>& zcornsv )
{
// We Read one corner line (pillar) from zdata at a time (one for each
// i,j), transform it according to zoffset and zscale, and put it in to
// zcornsv in reverse order.
// As i and j order and size is the same for both zcornsv and zdata, we can
// ignore it here and place one pillar at the time regardless of how many
// pillars there are.
size_t nzcorn = zcornsv.size();
size_t nsplitenz = splitenz.size();
size_t nzdata = zdata.size();
size_t num_row = 4 * static_cast<size_t>( nz );
if ( nzcorn % num_row != 0 ) throw std::runtime_error( "Incorrect size of zcorn." );
if ( nsplitenz != nzcorn / 4 ) throw std::runtime_error( "Incorrect size of splitenz." );
std::vector<float> pillar( num_row, 0.0 );
size_t it_zdata = 0, it_splitenz = 0, it_zcorn = 0;
while ( it_zcorn < nzcorn )
{
for ( size_t it_pillar = 0; it_pillar < num_row; )
{
char split = splitenz[it_splitenz++];
if ( split == 1 )
{
// There is one value for this corner which
// we must duplicate 4 times in zcornsv
if ( it_zdata >= nzdata ) throw std::runtime_error( "Incorrect size of zdata" );
float val = ( zdata[it_zdata++] + zoffset ) * zscale;
for ( int n = 0; n < 4; n++ )
{
pillar[it_pillar++] = val;
}
}
else if ( split == 4 )
{
// There are four value for this corner which
// we must duplicate 4 times in zcornsv
if ( it_zdata + 3 >= nzdata ) throw std::runtime_error( "Incorrect size of zdata" );
// As we place the pillar in reverse order into zcornsv,
// we must put zdata in reverse order into pillar to
// preserve n,s,w,e directions.
pillar[it_pillar + 3] = ( zdata[it_zdata++] + zoffset ) * zscale;
pillar[it_pillar + 2] = ( zdata[it_zdata++] + zoffset ) * zscale;
pillar[it_pillar + 1] = ( zdata[it_zdata++] + zoffset ) * zscale;
pillar[it_pillar] = ( zdata[it_zdata++] + zoffset ) * zscale;
it_pillar += 4;
}
else
{
throw std::runtime_error( "Unsupported split type" );
}
}
// Put the pillar into zcornsv in reverse order
for ( size_t it_pillar = num_row; it_pillar >= 1; )
{
zcornsv[it_zcorn++] = pillar[--it_pillar];
}
}
if ( it_splitenz != nsplitenz ) throw std::runtime_error( "Incorrect size of splitenz." );
if ( it_zdata != nzdata ) throw std::runtime_error( "Incorrect size of zdata" );
if ( it_zcorn != nzcorn ) throw std::runtime_error( "Incorrect size of zcorn." );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RifRoffFileTools::convertToReservoirIndexOrder( int nx,
int ny,
int nz,
const std::vector<char>& activeIn,
std::vector<int>& activeOut )
{
CAF_ASSERT( static_cast<size_t>( nx ) * ny * nz == activeIn.size() );
activeOut.resize( activeIn.size(), -1 );
int outIdx = 0;
for ( int k = 0; k < nz; k++ )
{
for ( int j = 0; j < ny; j++ )
{
for ( int i = 0; i < nx; i++ )
{
int inIdx = i * ny * nz + j * nz + k;
activeOut[outIdx] = static_cast<int>( activeIn[inIdx] );
outIdx++;
}
}
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
size_t RifRoffFileTools::computeActiveCellMatrixIndex( std::vector<int>& activeCells )
{
size_t activeMatrixIndex = 0;
int cellCount = static_cast<int>( activeCells.size() );
for ( int gridLocalCellIndex = 0; gridLocalCellIndex < cellCount; gridLocalCellIndex++ )
{
if ( activeCells[gridLocalCellIndex] != 0 )
{
activeCells[gridLocalCellIndex] = activeMatrixIndex;
activeMatrixIndex++;
}
else
{
activeCells[gridLocalCellIndex] = -1;
}
}
return activeMatrixIndex;
}

View File

@ -0,0 +1,66 @@
/////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2022- Equinor 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.
//
/////////////////////////////////////////////////////////////////////////////////
#pragma once
#include "cvfObject.h"
#include "cvfVector3.h"
#include <QString>
#include <vector>
class RigEclipseCaseData;
class RigMainGrid;
//==================================================================================================
//
// Class for access to Roff grids.
//
//==================================================================================================
class RifRoffFileTools : public cvf::Object
{
public:
RifRoffFileTools();
~RifRoffFileTools() override;
static bool openGridFile( const QString& fileName, RigEclipseCaseData* eclipseCase, QString* errorMessages );
private:
static void interpretSplitenzData( int nz,
float zoffset,
float zscale,
const std::vector<char>& splitenz,
const std::vector<float>& zdata,
std::vector<float>& zcornsv );
static void
convertToReservoirIndexOrder( int nx, int ny, int nz, const std::vector<char>& activeIn, std::vector<int>& activeOut );
static size_t computeActiveCellMatrixIndex( std::vector<int>& activeCells );
static cvf::Vec3d getCorner( const RigMainGrid& grid,
const std::vector<float>& cornerLines,
const std::vector<float>& zcorn,
size_t cellIdx,
int cornerIdx,
const cvf::Vec3d& offset,
const cvf::Vec3d& scale );
static double interpolate( const cvf::Vec3d& top, const cvf::Vec3d& bottom, double z, int idx );
};

View File

@ -126,6 +126,7 @@ set(SOURCE_GROUP_HEADER_FILES
${CMAKE_CURRENT_LIST_DIR}/RimSurfaceIntersectionCollection.h
${CMAKE_CURRENT_LIST_DIR}/RimEclipseResultAddress.h
${CMAKE_CURRENT_LIST_DIR}/RimEclipseResultAddressCollection.h
${CMAKE_CURRENT_LIST_DIR}/RimRoffCase.h
${CMAKE_CURRENT_LIST_DIR}/RimEclipseCaseTools.h
${CMAKE_CURRENT_LIST_DIR}/RimMultipleEclipseResults.h
)
@ -253,6 +254,7 @@ set(SOURCE_GROUP_SOURCE_FILES
${CMAKE_CURRENT_LIST_DIR}/RimSurfaceIntersectionCollection.cpp
${CMAKE_CURRENT_LIST_DIR}/RimEclipseResultAddress.cpp
${CMAKE_CURRENT_LIST_DIR}/RimEclipseResultAddressCollection.cpp
${CMAKE_CURRENT_LIST_DIR}/RimRoffCase.cpp
${CMAKE_CURRENT_LIST_DIR}/RimEclipseCaseTools.cpp
${CMAKE_CURRENT_LIST_DIR}/RimMultipleEclipseResults.cpp
)

View File

@ -0,0 +1,140 @@
/////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2022- Equinor 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 "RimRoffCase.h"
#include "RiaDefines.h"
#include "RiaLogging.h"
#include "RiaPreferences.h"
#include "RifRoffFileTools.h"
#include "RigActiveCellInfo.h"
#include "RigCaseCellResultsData.h"
#include "RigEclipseCaseData.h"
#include "RigMainGrid.h"
#include "RimEclipseInputProperty.h"
#include "RimEclipseInputPropertyCollection.h"
#include "RimReservoirCellResultsStorage.h"
#include "cafProgressInfo.h"
#include <QDir>
#include <QFileInfo>
CAF_PDM_SOURCE_INIT( RimRoffCase, "RimRoffCase" );
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RimRoffCase::RimRoffCase()
: RimEclipseCase()
{
CAF_PDM_InitObject( "RimRoffCase", ":/EclipseInput48x48.png" );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RimRoffCase::~RimRoffCase()
{
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
bool RimRoffCase::openEclipseGridFile()
{
if ( eclipseCaseData() )
{
// Early exit if reservoir data is created
return true;
}
setReservoirData( new RigEclipseCaseData( this ) );
QString fileName = gridFileName();
// First find and read the grid data
if ( eclipseCaseData()->mainGrid()->gridPointDimensions() == cvf::Vec3st( 0, 0, 0 ) )
{
QString errorMessages;
if ( RifRoffFileTools::openGridFile( fileName, this->eclipseCaseData(), &errorMessages ) )
{
QFileInfo gridFileInfo( fileName );
QString caseName = gridFileInfo.completeBaseName();
setCaseUserDescription( caseName );
eclipseCaseData()->mainGrid()->setFlipAxis( m_flipXAxis, m_flipYAxis );
computeCachedData();
}
else
{
RiaLogging::error( errorMessages );
return false;
}
}
results( RiaDefines::PorosityModelType::MATRIX_MODEL )->createPlaceholderResultEntries();
if ( RiaPreferences::current()->autocomputeDepthRelatedProperties )
{
results( RiaDefines::PorosityModelType::MATRIX_MODEL )->computeDepthRelatedResults();
results( RiaDefines::PorosityModelType::FRACTURE_MODEL )->computeDepthRelatedResults();
}
results( RiaDefines::PorosityModelType::MATRIX_MODEL )->computeCellVolumes();
return true;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimRoffCase::reloadEclipseGridFile()
{
setReservoirData( nullptr );
openReserviorCase();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimRoffCase::defineUiOrdering( QString uiConfigName, caf::PdmUiOrdering& uiOrdering )
{
uiOrdering.add( &m_caseUserDescription );
uiOrdering.add( &m_displayNameOption );
uiOrdering.add( &m_caseId );
uiOrdering.add( &m_caseFileName );
auto group = uiOrdering.addNewGroup( "Case Options" );
group->add( &m_activeFormationNames );
group->add( &m_flipXAxis );
group->add( &m_flipYAxis );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
QString RimRoffCase::locationOnDisc() const
{
if ( gridFileName().isEmpty() ) return QString();
QFileInfo fi( gridFileName() );
return fi.absolutePath();
}

View File

@ -0,0 +1,49 @@
/////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2022- Equinor 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.
//
/////////////////////////////////////////////////////////////////////////////////
#pragma once
#include "RimEclipseCase.h"
#include "cafPdmChildField.h"
#include "cafPdmField.h"
#include "cafPdmObject.h"
//==================================================================================================
//
//
//
//==================================================================================================
class RimRoffCase : public RimEclipseCase
{
CAF_PDM_HEADER_INIT;
public:
RimRoffCase();
~RimRoffCase() override;
// RimCase overrides
bool openEclipseGridFile() override;
void reloadEclipseGridFile() override;
// Overrides from RimCase
QString locationOnDisc() const override;
protected:
void defineUiOrdering( QString uiConfigName, caf::PdmUiOrdering& uiOrdering ) override;
};

View File

@ -449,6 +449,7 @@ void RiuMainWindow::createMenus()
importEclipseMenu->addAction( cmdFeatureMgr->action( "RicImportEclipseCasesFeature" ) );
importEclipseMenu->addAction( cmdFeatureMgr->action( "RicImportEclipseCaseTimeStepFilterFeature" ) );
importEclipseMenu->addAction( cmdFeatureMgr->action( "RicImportInputEclipseCaseFeature" ) );
importEclipseMenu->addAction( cmdFeatureMgr->action( "RicImportRoffCaseFeature" ) );
importEclipseMenu->addAction( cmdFeatureMgr->action( "RicCreateGridCaseGroupFromFilesFeature" ) );
importMenu->addSeparator();

View File

@ -495,6 +495,13 @@ endif()
list(APPEND THIRD_PARTY_LIBRARIES clipper)
# ##############################################################################
# roffcpp
# ##############################################################################
add_subdirectory(ThirdParty/roffcpp)
list(APPEND THIRD_PARTY_LIBRARIES roffcpp)
# ##############################################################################
# Thirdparty libraries are put in ThirdParty solution folder
# ##############################################################################

1
ThirdParty/roffcpp vendored Submodule

@ -0,0 +1 @@
Subproject commit 2b5e846161a504afd1cbf1a1fa7e7388759cd300