mirror of
https://github.com/OPM/ResInsight.git
synced 2025-01-05 21:53:27 -06:00
1570 lines
58 KiB
C++
1570 lines
58 KiB
C++
/////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Copyright (C) 2011- Statoil ASA
|
|
// Copyright (C) 2013- Ceetron Solutions AS
|
|
// Copyright (C) 2011-2012 Ceetron AS
|
|
//
|
|
// 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 "RifEclipseInputFileTools.h"
|
|
|
|
#include "RiaCellDividingTools.h"
|
|
#include "RiaLogging.h"
|
|
#include "RiaStringEncodingTools.h"
|
|
#include "RiaTextStringTools.h"
|
|
|
|
#include "ExportCommands/RicEclipseCellResultToFileImpl.h"
|
|
|
|
#include "RifEclipseInputPropertyLoader.h"
|
|
#include "RifEclipseKeywordContent.h"
|
|
#include "RifEclipseTextFileReader.h"
|
|
#include "RifReaderEclipseOutput.h"
|
|
|
|
#include "RigActiveCellInfo.h"
|
|
#include "RigCaseCellResultsData.h"
|
|
#include "RigEclipseCaseData.h"
|
|
#include "RigEclipseResultAddress.h"
|
|
#include "RigFault.h"
|
|
#include "RigMainGrid.h"
|
|
|
|
#include "cafProgressInfo.h"
|
|
|
|
#include <cmath>
|
|
#include <fstream>
|
|
#include <iostream>
|
|
#include <vector>
|
|
|
|
#include <QDebug>
|
|
#include <QDir>
|
|
#include <QFile>
|
|
#include <QFileInfo>
|
|
#include <QTextStream>
|
|
|
|
#include "ert/ecl/ecl_box.hpp"
|
|
#include "ert/ecl/ecl_grid.hpp"
|
|
#include "ert/ecl/ecl_kw.h"
|
|
|
|
QString includeKeyword( "INCLUDE" );
|
|
QString faultsKeyword( "FAULTS" );
|
|
QString editKeyword( "EDIT" );
|
|
QString gridKeyword( "GRID" );
|
|
QString pathsKeyword( "PATHS" );
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
/// Constructor
|
|
//--------------------------------------------------------------------------------------------------
|
|
RifEclipseInputFileTools::RifEclipseInputFileTools()
|
|
{
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
/// Destructor
|
|
//--------------------------------------------------------------------------------------------------
|
|
RifEclipseInputFileTools::~RifEclipseInputFileTools()
|
|
{
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
bool RifEclipseInputFileTools::openGridFile( const QString& fileName, RigEclipseCaseData* eclipseCase, bool readFaultData, QString* errorMessages )
|
|
{
|
|
std::string filename = fileName.toStdString();
|
|
|
|
auto objects = RifEclipseTextFileReader::readKeywordAndValues( filename );
|
|
|
|
ecl_kw_type* specGridKw = nullptr;
|
|
ecl_kw_type* zCornKw = nullptr;
|
|
ecl_kw_type* coordKw = nullptr;
|
|
ecl_kw_type* actNumKw = nullptr;
|
|
ecl_kw_type* mapAxesKw = nullptr;
|
|
|
|
for ( const auto& obj : objects )
|
|
{
|
|
{
|
|
std::string keyword = "SPECGRID";
|
|
if ( obj.keyword.compare( keyword ) == 0 )
|
|
{
|
|
std::vector<int> intValues;
|
|
for ( const auto& val : obj.values )
|
|
{
|
|
intValues.push_back( val );
|
|
}
|
|
|
|
specGridKw = ecl_kw_alloc_new( keyword.data(), (int)obj.values.size(), ECL_INT, intValues.data() );
|
|
continue;
|
|
}
|
|
}
|
|
|
|
{
|
|
std::string keyword = "COORD";
|
|
if ( obj.keyword.compare( keyword ) == 0 )
|
|
{
|
|
coordKw = ecl_kw_alloc_new( keyword.data(), (int)obj.values.size(), ECL_FLOAT, obj.values.data() );
|
|
continue;
|
|
}
|
|
}
|
|
|
|
{
|
|
std::string keyword = "ZCORN";
|
|
if ( obj.keyword.compare( keyword ) == 0 )
|
|
{
|
|
zCornKw = ecl_kw_alloc_new( keyword.data(), (int)obj.values.size(), ECL_FLOAT, obj.values.data() );
|
|
continue;
|
|
}
|
|
}
|
|
|
|
{
|
|
std::string keyword = "ACTNUM";
|
|
if ( obj.keyword.compare( keyword ) == 0 )
|
|
{
|
|
std::vector<int> intValues;
|
|
for ( const auto& val : obj.values )
|
|
{
|
|
intValues.push_back( val );
|
|
}
|
|
|
|
actNumKw = ecl_kw_alloc_new( keyword.data(), (int)obj.values.size(), ECL_INT, intValues.data() );
|
|
continue;
|
|
}
|
|
}
|
|
|
|
{
|
|
std::string keyword = "MAPAXES";
|
|
if ( obj.keyword.compare( keyword ) == 0 )
|
|
{
|
|
mapAxesKw = ecl_kw_alloc_new( keyword.data(), (int)obj.values.size(), ECL_FLOAT, obj.values.data() );
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( specGridKw && zCornKw && coordKw )
|
|
{
|
|
int nx = ecl_kw_iget_int( specGridKw, 0 );
|
|
int ny = ecl_kw_iget_int( specGridKw, 1 );
|
|
int nz = ecl_kw_iget_int( specGridKw, 2 );
|
|
|
|
ecl_grid_type* inputGrid = ecl_grid_alloc_GRDECL_kw( nx, ny, nz, zCornKw, coordKw, actNumKw, mapAxesKw );
|
|
|
|
RifReaderEclipseOutput::transferGeometry( inputGrid, eclipseCase );
|
|
|
|
if ( readFaultData )
|
|
{
|
|
bool isFaultKeywordPresent = false;
|
|
for ( const auto& keywordObj : objects )
|
|
{
|
|
if ( keywordObj.keyword == "FAULTS" ) isFaultKeywordPresent = true;
|
|
}
|
|
|
|
if ( isFaultKeywordPresent )
|
|
{
|
|
importFaultsFromFile( eclipseCase, fileName );
|
|
}
|
|
}
|
|
|
|
bool useMapAxes = ecl_grid_use_mapaxes( inputGrid );
|
|
eclipseCase->mainGrid()->setUseMapAxes( useMapAxes );
|
|
|
|
if ( useMapAxes )
|
|
{
|
|
std::array<double, 6> mapAxesValues;
|
|
ecl_grid_init_mapaxes_data_double( inputGrid, mapAxesValues.data() );
|
|
eclipseCase->mainGrid()->setMapAxes( mapAxesValues );
|
|
}
|
|
|
|
ecl_kw_free( specGridKw );
|
|
ecl_kw_free( zCornKw );
|
|
ecl_kw_free( coordKw );
|
|
if ( actNumKw ) ecl_kw_free( actNumKw );
|
|
if ( mapAxesKw ) ecl_kw_free( mapAxesKw );
|
|
|
|
ecl_grid_free( inputGrid );
|
|
|
|
QString errorMessages;
|
|
|
|
// Import additional keywords as input properties
|
|
RifEclipseInputPropertyLoader::createInputPropertiesFromKeywords( eclipseCase, objects, &errorMessages );
|
|
|
|
if ( !errorMessages.isEmpty() ) RiaLogging::error( errorMessages );
|
|
|
|
return true;
|
|
}
|
|
|
|
QString txt = "Missing required keywords :";
|
|
if ( !specGridKw ) txt += " SPECGRID";
|
|
if ( !zCornKw ) txt += " ZCORN";
|
|
if ( !coordKw ) txt += " COORD";
|
|
|
|
RiaLogging::error( txt );
|
|
|
|
return false;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
bool RifEclipseInputFileTools::exportGrid( const QString& fileName,
|
|
RigEclipseCaseData* eclipseCase,
|
|
bool exportInLocalCoordinates,
|
|
const cvf::UByteArray* cellVisibilityOverrideForActnum,
|
|
const cvf::Vec3st& min,
|
|
const cvf::Vec3st& maxIn,
|
|
const cvf::Vec3st& refinement )
|
|
{
|
|
if ( !eclipseCase )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
const RigActiveCellInfo* activeCellInfo = eclipseCase->activeCellInfo( RiaDefines::PorosityModelType::MATRIX_MODEL );
|
|
|
|
CVF_ASSERT( activeCellInfo );
|
|
|
|
const RigMainGrid* mainGrid = eclipseCase->mainGrid();
|
|
|
|
cvf::Vec3st max = maxIn;
|
|
if ( max == cvf::Vec3st::UNDEFINED )
|
|
{
|
|
max = cvf::Vec3st( mainGrid->cellCountI() - 1, mainGrid->cellCountJ() - 1, mainGrid->cellCountK() - 1 );
|
|
}
|
|
|
|
int ecl_nx = static_cast<int>( ( max.x() - min.x() ) * refinement.x() + 1 );
|
|
int ecl_ny = static_cast<int>( ( max.y() - min.y() ) * refinement.y() + 1 );
|
|
int ecl_nz = static_cast<int>( ( max.z() - min.z() ) * refinement.z() + 1 );
|
|
|
|
CVF_ASSERT( ecl_nx > 0 && ecl_ny > 0 && ecl_nz > 0 );
|
|
|
|
size_t cellsPerOriginal = refinement.x() * refinement.y() * refinement.z();
|
|
|
|
caf::ProgressInfo progress( mainGrid->cellCount() * 2, "Save Eclipse Grid" );
|
|
int cellProgressInterval = 1000;
|
|
|
|
std::vector<float*> ecl_corners;
|
|
ecl_corners.reserve( mainGrid->cellCount() * cellsPerOriginal );
|
|
std::vector<int*> ecl_coords;
|
|
ecl_coords.reserve( mainGrid->cellCount() * cellsPerOriginal );
|
|
|
|
std::array<float, 6> mapAxes = mainGrid->mapAxesF();
|
|
cvf::Mat4d mapAxisTrans = mainGrid->mapAxisTransform();
|
|
if ( exportInLocalCoordinates )
|
|
{
|
|
cvf::Vec3d minPoint3d( mainGrid->boundingBox().min() );
|
|
cvf::Vec2f minPoint2f( minPoint3d.x(), minPoint3d.y() );
|
|
cvf::Vec2f origin( mapAxes[2] - minPoint2f.x(), mapAxes[3] - minPoint2f.y() );
|
|
cvf::Vec2f xPoint = cvf::Vec2f( mapAxes[4], mapAxes[5] ) - minPoint2f;
|
|
cvf::Vec2f yPoint = cvf::Vec2f( mapAxes[0], mapAxes[1] ) - minPoint2f;
|
|
mapAxes = { yPoint.x(), yPoint.y(), origin.x(), origin.y(), xPoint.x(), xPoint.y() };
|
|
|
|
mapAxisTrans.setTranslation( mapAxisTrans.translation() - minPoint3d );
|
|
}
|
|
|
|
const size_t* cellMappingECLRi = RifReaderEclipseOutput::eclipseCellIndexMapping();
|
|
|
|
int incrementalIndex = 0;
|
|
for ( size_t k = min.z() * refinement.z(); k <= max.z() * refinement.z(); ++k )
|
|
{
|
|
size_t mainK = k / refinement.z();
|
|
size_t k0 = k - min.z() * refinement.z();
|
|
for ( size_t j = min.y() * refinement.y(); j <= max.y() * refinement.y(); ++j )
|
|
{
|
|
size_t mainJ = j / refinement.y();
|
|
size_t j0 = j - min.y() * refinement.y();
|
|
for ( size_t i = min.x() * refinement.x(); i <= max.x() * refinement.x(); ++i )
|
|
{
|
|
size_t mainI = i / refinement.x();
|
|
size_t i0 = i - min.x() * refinement.x();
|
|
|
|
size_t mainIndex = mainGrid->cellIndexFromIJK( mainI, mainJ, mainK );
|
|
|
|
int active = activeCellInfo->isActive( mainIndex ) ? 1 : 0;
|
|
if ( active && cellVisibilityOverrideForActnum )
|
|
{
|
|
active = ( *cellVisibilityOverrideForActnum )[mainIndex];
|
|
}
|
|
|
|
int* ecl_cell_coords = new int[5];
|
|
ecl_cell_coords[0] = (int)( i0 + 1 );
|
|
ecl_cell_coords[1] = (int)( j0 + 1 );
|
|
ecl_cell_coords[2] = (int)( k0 + 1 );
|
|
ecl_cell_coords[3] = incrementalIndex++;
|
|
ecl_cell_coords[4] = active;
|
|
ecl_coords.push_back( ecl_cell_coords );
|
|
|
|
std::array<cvf::Vec3d, 8> cellCorners;
|
|
mainGrid->cellCornerVertices( mainIndex, cellCorners.data() );
|
|
|
|
if ( mainGrid->useMapAxes() )
|
|
{
|
|
for ( cvf::Vec3d& corner : cellCorners )
|
|
{
|
|
corner.transformPoint( mapAxisTrans );
|
|
}
|
|
}
|
|
|
|
auto refinedCoords = RiaCellDividingTools::createHexCornerCoords( cellCorners, refinement.x(), refinement.y(), refinement.z() );
|
|
|
|
size_t subI = i % refinement.x();
|
|
size_t subJ = j % refinement.y();
|
|
size_t subK = k % refinement.z();
|
|
size_t subIndex = subI + subJ * refinement.x() + subK * refinement.x() * refinement.y();
|
|
|
|
float* ecl_cell_corners = new float[24];
|
|
for ( size_t cIdx = 0; cIdx < 8; ++cIdx )
|
|
{
|
|
cvf::Vec3d cellCorner = refinedCoords[subIndex * 8 + cIdx];
|
|
ecl_cell_corners[cellMappingECLRi[cIdx] * 3] = cellCorner[0];
|
|
ecl_cell_corners[cellMappingECLRi[cIdx] * 3 + 1] = cellCorner[1];
|
|
ecl_cell_corners[cellMappingECLRi[cIdx] * 3 + 2] = -cellCorner[2];
|
|
}
|
|
ecl_corners.push_back( ecl_cell_corners );
|
|
}
|
|
}
|
|
if ( incrementalIndex % cellProgressInterval == 0 )
|
|
{
|
|
progress.setProgress( incrementalIndex / cellsPerOriginal );
|
|
}
|
|
}
|
|
|
|
// Do not perform the transformation (applyMapaxes == false):
|
|
// The coordinates have been transformed to the map axes coordinate system already.
|
|
// However, send the map axes data in to libecl so that the coordinate system description is saved.
|
|
bool applyMapaxes = false;
|
|
ecl_grid_type* mainEclGrid =
|
|
ecl_grid_alloc_GRID_data( (int)ecl_coords.size(), ecl_nx, ecl_ny, ecl_nz, 5, &ecl_coords[0], &ecl_corners[0], applyMapaxes, mapAxes.data() );
|
|
progress.setProgress( mainGrid->cellCount() );
|
|
|
|
for ( float* floatArray : ecl_corners )
|
|
{
|
|
delete floatArray;
|
|
}
|
|
|
|
for ( int* intArray : ecl_coords )
|
|
{
|
|
delete intArray;
|
|
}
|
|
|
|
FILE* filePtr = util_fopen( RiaStringEncodingTools::toNativeEncoded( fileName ).data(), "w" );
|
|
|
|
if ( !filePtr )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
ert_ecl_unit_enum ecl_units = ECL_METRIC_UNITS;
|
|
if ( eclipseCase->unitsType() == RiaDefines::EclipseUnitSystem::UNITS_FIELD )
|
|
ecl_units = ECL_FIELD_UNITS;
|
|
else if ( eclipseCase->unitsType() == RiaDefines::EclipseUnitSystem::UNITS_LAB )
|
|
ecl_units = ECL_LAB_UNITS;
|
|
|
|
ecl_grid_fprintf_grdecl2( mainEclGrid, filePtr, ecl_units );
|
|
ecl_grid_free( mainEclGrid );
|
|
fclose( filePtr );
|
|
|
|
return true;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
bool RifEclipseInputFileTools::exportKeywords( const QString& resultFileName,
|
|
RigEclipseCaseData* eclipseCase,
|
|
const std::vector<QString>& keywords,
|
|
bool writeEchoKeywords,
|
|
const cvf::Vec3st& min,
|
|
const cvf::Vec3st& maxIn,
|
|
const cvf::Vec3st& refinement )
|
|
{
|
|
QFile exportFile( resultFileName );
|
|
if ( !exportFile.open( QIODevice::WriteOnly | QIODevice::Text ) )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if ( writeEchoKeywords )
|
|
{
|
|
QTextStream stream( &exportFile );
|
|
stream << "NOECHO\n";
|
|
}
|
|
|
|
RigCaseCellResultsData* cellResultsData = eclipseCase->results( RiaDefines::PorosityModelType::MATRIX_MODEL );
|
|
RigActiveCellInfo* activeCells = cellResultsData->activeCellInfo();
|
|
RigMainGrid* mainGrid = eclipseCase->mainGrid();
|
|
|
|
cvf::Vec3st max = maxIn;
|
|
if ( max == cvf::Vec3st::UNDEFINED )
|
|
{
|
|
max = cvf::Vec3st( mainGrid->cellCountI() - 1, mainGrid->cellCountJ() - 1, mainGrid->cellCountK() - 1 );
|
|
}
|
|
|
|
caf::ProgressInfo progress( keywords.size(), "Saving Keywords" );
|
|
|
|
for ( const QString& keyword : keywords )
|
|
{
|
|
std::vector<double> resultValues;
|
|
|
|
RigEclipseResultAddress resAddr( RiaDefines::ResultCatType::STATIC_NATIVE, keyword );
|
|
if ( !cellResultsData->hasResultEntry( resAddr ) ) continue;
|
|
|
|
cellResultsData->ensureKnownResultLoaded( resAddr );
|
|
|
|
CVF_ASSERT( !cellResultsData->cellScalarResults( resAddr ).empty() );
|
|
|
|
resultValues = cellResultsData->cellScalarResults( resAddr )[0];
|
|
CVF_ASSERT( !resultValues.empty() );
|
|
if ( resultValues.empty() ) continue;
|
|
|
|
double defaultExportValue = 0.0;
|
|
if ( keyword.endsWith( "NUM" ) )
|
|
{
|
|
defaultExportValue = 1.0;
|
|
}
|
|
|
|
std::vector<double> filteredResults;
|
|
filteredResults.reserve( resultValues.size() );
|
|
|
|
for ( size_t k = min.z() * refinement.z(); k <= max.z() * refinement.z(); ++k )
|
|
{
|
|
size_t mainK = k / refinement.z();
|
|
for ( size_t j = min.y() * refinement.y(); j <= max.y() * refinement.y(); ++j )
|
|
{
|
|
size_t mainJ = j / refinement.y();
|
|
for ( size_t i = min.x() * refinement.x(); i <= max.x() * refinement.x(); ++i )
|
|
{
|
|
size_t mainI = i / refinement.x();
|
|
|
|
size_t mainIndex = mainGrid->cellIndexFromIJK( mainI, mainJ, mainK );
|
|
|
|
size_t resIndex = activeCells->cellResultIndex( mainIndex );
|
|
if ( resIndex != cvf::UNDEFINED_SIZE_T )
|
|
{
|
|
filteredResults.push_back( resultValues[resIndex] );
|
|
}
|
|
else
|
|
{
|
|
filteredResults.push_back( defaultExportValue );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Multiple keywords can be exported to same file, so write ECHO keywords outside the loop
|
|
bool writeEchoKeywordsInExporterObject = false;
|
|
RicEclipseCellResultToFileImpl::writeDataToTextFile( &exportFile, writeEchoKeywordsInExporterObject, keyword, filteredResults );
|
|
|
|
progress.incrementProgress();
|
|
}
|
|
|
|
if ( writeEchoKeywords )
|
|
{
|
|
QTextStream stream( &exportFile );
|
|
stream << "ECHO\n";
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RifEclipseInputFileTools::saveFault( QString completeFilename,
|
|
const RigMainGrid* mainGrid,
|
|
const std::vector<RigFault::FaultFace>& faultFaces,
|
|
QString faultName,
|
|
const cvf::Vec3st& min,
|
|
const cvf::Vec3st& maxIn,
|
|
const cvf::Vec3st& refinement )
|
|
{
|
|
QFile exportFile( completeFilename );
|
|
if ( !exportFile.open( QIODevice::WriteOnly | QIODevice::Text ) )
|
|
{
|
|
RiaLogging::error( "Could not open the file : " + completeFilename );
|
|
return;
|
|
}
|
|
|
|
QTextStream stream( &exportFile );
|
|
stream << "FAULTS" << '\n';
|
|
|
|
stream << "-- Name I1 I2 J1 J2 K1 K2 Face ( I/J/K )" << '\n';
|
|
|
|
saveFault( stream, mainGrid, faultFaces, faultName, min, maxIn, refinement );
|
|
stream << "/" << '\n';
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RifEclipseInputFileTools::saveFault( QTextStream& stream,
|
|
const RigMainGrid* mainGrid,
|
|
const std::vector<RigFault::FaultFace>& faultFaces,
|
|
QString faultName,
|
|
const cvf::Vec3st& min,
|
|
const cvf::Vec3st& maxIn,
|
|
const cvf::Vec3st& refinement )
|
|
{
|
|
// 'NAME' 1 1 1 1 1 2 J /
|
|
|
|
if ( faultName.contains( ' ' ) )
|
|
{
|
|
RiaLogging::error( QString( "Fault name '%1' contains spaces" ).arg( faultName ) );
|
|
return;
|
|
}
|
|
else if ( faultName.length() > 8 )
|
|
{
|
|
// Keep going anyway, eclipse files sometimes have longer than
|
|
// the specified 8 characters in the name without Eclipse complaining
|
|
RiaLogging::warning( QString( "Fault name '%1' is longer than 8 characters" ).arg( faultName ) );
|
|
}
|
|
|
|
std::vector<RigFault::CellAndFace> faultCellAndFaces;
|
|
|
|
cvf::Vec3st max = maxIn;
|
|
if ( max == cvf::Vec3st::UNDEFINED )
|
|
{
|
|
max = cvf::Vec3st( mainGrid->cellCountI() - 1, mainGrid->cellCountJ() - 1, mainGrid->cellCountK() - 1 );
|
|
}
|
|
|
|
for ( const RigFault::FaultFace& faultCellAndFace : faultFaces )
|
|
{
|
|
size_t i, j, k;
|
|
bool ok = mainGrid->ijkFromCellIndex( faultCellAndFace.m_nativeReservoirCellIndex, &i, &j, &k );
|
|
if ( !ok ) continue;
|
|
|
|
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();
|
|
|
|
// 2x2 Refinement of Original Cell 0, 0
|
|
// Y/J POS_J boundary
|
|
// ^ _______________
|
|
// | | | |
|
|
// | | 0,1 | 1,1 |
|
|
// | |_______|_______| POS_I boundary
|
|
// | | | |
|
|
// | | 0,0 | 1,0 |
|
|
// | |_______|_______|
|
|
// ---------------------> X/I
|
|
// NEG_J boundary
|
|
//
|
|
// POS_J gets shifted 1 index in J direction, NEG_J stays the same in J but spans two I.
|
|
// POS_I gets shifted 1 index in I direction, NEG_I stays the same in I but spans two J.
|
|
|
|
if ( refinement != cvf::Vec3st( 1, 1, 1 ) )
|
|
{
|
|
auto gridAxis = cvf::StructGridInterface::gridAxisFromFace( faultCellAndFace.m_nativeFace );
|
|
|
|
if ( gridAxis == cvf::StructGridInterface::GridAxisType::AXIS_I )
|
|
{
|
|
if ( faultCellAndFace.m_nativeFace == cvf::StructGridInterface::POS_I )
|
|
{
|
|
shifted_i += refinement.x() - 1;
|
|
}
|
|
for ( size_t refineK = 0; refineK < refinement.z(); ++refineK )
|
|
{
|
|
for ( size_t refineJ = 0; refineJ < refinement.y(); ++refineJ )
|
|
{
|
|
faultCellAndFaces.push_back(
|
|
std::make_tuple( shifted_i, shifted_j + refineJ, shifted_k + refineK, faultCellAndFace.m_nativeFace ) );
|
|
}
|
|
}
|
|
}
|
|
else if ( gridAxis == cvf::StructGridInterface::GridAxisType::AXIS_J )
|
|
{
|
|
if ( faultCellAndFace.m_nativeFace == cvf::StructGridInterface::POS_J )
|
|
{
|
|
shifted_j += refinement.y() - 1;
|
|
}
|
|
|
|
for ( size_t refineK = 0; refineK < refinement.z(); ++refineK )
|
|
{
|
|
for ( size_t refineI = 0; refineI < refinement.x(); ++refineI )
|
|
{
|
|
faultCellAndFaces.push_back(
|
|
std::make_tuple( shifted_i + refineI, shifted_j, shifted_k + refineK, faultCellAndFace.m_nativeFace ) );
|
|
}
|
|
}
|
|
}
|
|
else if ( gridAxis == cvf::StructGridInterface::GridAxisType::AXIS_K )
|
|
{
|
|
if ( faultCellAndFace.m_nativeFace == cvf::StructGridInterface::POS_K )
|
|
{
|
|
shifted_k += refinement.z() - 1;
|
|
}
|
|
|
|
for ( size_t refineJ = 0; refineJ < refinement.y(); ++refineJ )
|
|
{
|
|
for ( size_t refineI = 0; refineI < refinement.x(); ++refineI )
|
|
{
|
|
faultCellAndFaces.push_back(
|
|
std::make_tuple( shifted_i + refineI, shifted_j + refineJ, shifted_k, faultCellAndFace.m_nativeFace ) );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
faultCellAndFaces.push_back( std::make_tuple( shifted_i, shifted_j, shifted_k, faultCellAndFace.m_nativeFace ) );
|
|
}
|
|
}
|
|
|
|
// Sort order: i, j, face then k.
|
|
std::sort( faultCellAndFaces.begin(), faultCellAndFaces.end(), RigFault::ordering );
|
|
|
|
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;
|
|
|
|
if ( i != lastI || j != lastJ || lastFaceType != faceType || k != lastK + 1 )
|
|
{
|
|
// No fault should have no face
|
|
if ( lastFaceType != cvf::StructGridInterface::FaceType::NO_FACE )
|
|
{
|
|
writeFaultLine( stream, faultName, lastI, lastJ, startK, lastK, lastFaceType );
|
|
}
|
|
lastI = i;
|
|
lastJ = j;
|
|
lastK = k;
|
|
lastFaceType = faceType;
|
|
startK = k;
|
|
}
|
|
else
|
|
{
|
|
lastK = k;
|
|
}
|
|
}
|
|
|
|
// No fault should have no face
|
|
if ( lastFaceType != cvf::StructGridInterface::FaceType::NO_FACE )
|
|
{
|
|
writeFaultLine( stream, faultName, lastI, lastJ, startK, lastK, lastFaceType );
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RifEclipseInputFileTools::saveFaults( QTextStream& stream,
|
|
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)*/ )
|
|
{
|
|
stream << "FAULTS" << '\n';
|
|
|
|
stream << "-- Name I1 I2 J1 J2 K1 K2 Face ( I/J/K )" << '\n';
|
|
|
|
const cvf::Collection<RigFault>& faults = mainGrid->faults();
|
|
for ( const auto& fault : faults )
|
|
{
|
|
if ( fault->name() != RiaResultNames::undefinedGridFaultName() && fault->name() != RiaResultNames::undefinedGridFaultWithInactiveName() )
|
|
{
|
|
saveFault( stream, mainGrid, fault->faultFaces(), fault->name(), min, max, refinement );
|
|
}
|
|
}
|
|
stream << "/" << '\n';
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
bool RifEclipseInputFileTools::importFaultsFromFile( RigEclipseCaseData* eclipseCaseData, const QString& fileName )
|
|
{
|
|
cvf::Collection<RigFault> faultCollectionFromFile;
|
|
RifEclipseInputFileTools::parseAndReadFaults( fileName, &faultCollectionFromFile );
|
|
if ( faultCollectionFromFile.empty() )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
cvf::Collection<RigFault> faults;
|
|
{
|
|
cvf::Collection<RigFault> faultCollection = eclipseCaseData->mainGrid()->faults();
|
|
for ( size_t i = 0; i < faultCollection.size(); i++ )
|
|
{
|
|
RigFault* f = faultCollection.at( i );
|
|
if ( f->name() == RiaResultNames::undefinedGridFaultName() || f->name() == RiaResultNames::undefinedGridFaultName() )
|
|
{
|
|
// Do not include undefined grid faults, as these are recomputed based on the imported faults from
|
|
// files
|
|
continue;
|
|
}
|
|
|
|
faults.push_back( f );
|
|
}
|
|
}
|
|
|
|
for ( size_t i = 0; i < faultCollectionFromFile.size(); i++ )
|
|
{
|
|
RigFault* faultFromFile = faultCollectionFromFile.at( i );
|
|
|
|
bool existFaultWithSameName = false;
|
|
for ( size_t j = 0; j < faults.size(); j++ )
|
|
{
|
|
RigFault* existingFault = faults.at( j );
|
|
|
|
if ( existingFault->name() == faultFromFile->name() )
|
|
{
|
|
existFaultWithSameName = true;
|
|
}
|
|
}
|
|
|
|
if ( !existFaultWithSameName )
|
|
{
|
|
faults.push_back( faultFromFile );
|
|
}
|
|
}
|
|
|
|
eclipseCaseData->mainGrid()->setFaults( faults );
|
|
|
|
return true;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RifEclipseInputFileTools::parseAndReadPathAliasKeyword( const QString& fileName,
|
|
std::vector<std::pair<QString, QString>>* pathAliasDefinitions )
|
|
{
|
|
char buf[1024];
|
|
|
|
QFile data( fileName );
|
|
data.open( QFile::ReadOnly );
|
|
|
|
QString line;
|
|
|
|
bool foundPathsKeyword = false;
|
|
|
|
do
|
|
{
|
|
qint64 lineLength = data.readLine( buf, sizeof( buf ) );
|
|
if ( lineLength > 0 )
|
|
{
|
|
line = QString::fromLatin1( buf );
|
|
if ( line.size() && ( line[0].isLetter() || foundPathsKeyword ) )
|
|
{
|
|
line = line.trimmed();
|
|
|
|
if ( line == gridKeyword )
|
|
{
|
|
return;
|
|
}
|
|
else if ( line == pathsKeyword )
|
|
{
|
|
foundPathsKeyword = true;
|
|
}
|
|
else if ( foundPathsKeyword )
|
|
{
|
|
if ( line.startsWith( "/", Qt::CaseInsensitive ) )
|
|
{
|
|
// Detected end of keyword data section
|
|
return;
|
|
}
|
|
else if ( line.startsWith( "--", Qt::CaseInsensitive ) )
|
|
{
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
// Replace tab with space to be able to split the string using space as splitter
|
|
line.replace( "\t", " " );
|
|
|
|
// Remove character ' used to mark start and end of fault name, possibly also around face
|
|
// definition; 'I+'
|
|
line.remove( "'" );
|
|
|
|
QStringList entries = RiaTextStringTools::splitSkipEmptyParts( line );
|
|
if ( entries.size() < 2 )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
pathAliasDefinitions->push_back( std::make_pair( entries[0], entries[1] ) );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} while ( !data.atEnd() );
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RifEclipseInputFileTools::findGridKeywordPositions( const std::vector<RifKeywordAndFilePos>& keywords,
|
|
qint64* coordPos,
|
|
qint64* zcornPos,
|
|
qint64* specgridPos,
|
|
qint64* actnumPos,
|
|
qint64* mapaxesPos,
|
|
qint64* gridunitPos )
|
|
{
|
|
CVF_ASSERT( coordPos && zcornPos && specgridPos && actnumPos && mapaxesPos && gridunitPos );
|
|
|
|
size_t i;
|
|
for ( i = 0; i < keywords.size(); i++ )
|
|
{
|
|
if ( keywords[i].keyword == "COORD" )
|
|
{
|
|
*coordPos = keywords[i].filePos;
|
|
}
|
|
else if ( keywords[i].keyword == "ZCORN" )
|
|
{
|
|
*zcornPos = keywords[i].filePos;
|
|
}
|
|
else if ( keywords[i].keyword == "SPECGRID" )
|
|
{
|
|
*specgridPos = keywords[i].filePos;
|
|
}
|
|
else if ( keywords[i].keyword == "ACTNUM" )
|
|
{
|
|
*actnumPos = keywords[i].filePos;
|
|
}
|
|
else if ( keywords[i].keyword == "MAPAXES" )
|
|
{
|
|
*mapaxesPos = keywords[i].filePos;
|
|
}
|
|
else if ( keywords[i].keyword == "GRIDUNIT" )
|
|
{
|
|
*gridunitPos = keywords[i].filePos;
|
|
}
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RifEclipseInputFileTools::readFaults( const QString& fileName,
|
|
const std::vector<RifKeywordAndFilePos>& fileKeywords,
|
|
cvf::Collection<RigFault>* faults )
|
|
{
|
|
QFile data( fileName );
|
|
if ( !data.open( QFile::ReadOnly ) )
|
|
{
|
|
return;
|
|
}
|
|
|
|
for ( size_t i = 0; i < fileKeywords.size(); i++ )
|
|
{
|
|
if ( fileKeywords[i].keyword.compare( editKeyword, Qt::CaseInsensitive ) == 0 )
|
|
{
|
|
return;
|
|
}
|
|
else if ( fileKeywords[i].keyword.compare( faultsKeyword, Qt::CaseInsensitive ) != 0 )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
qint64 filePos = fileKeywords[i].filePos;
|
|
|
|
bool isEditKeywordDetected = false;
|
|
readFaults( data, filePos, faults, &isEditKeywordDetected );
|
|
|
|
if ( isEditKeywordDetected )
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RifEclipseInputFileTools::parseAndReadFaults( const QString& fileName, cvf::Collection<RigFault>* faults )
|
|
{
|
|
QFile data( fileName );
|
|
if ( !data.open( QFile::ReadOnly ) )
|
|
{
|
|
return;
|
|
}
|
|
|
|
qint64 filePos = findKeyword( faultsKeyword, data, 0 );
|
|
|
|
while ( filePos != -1 )
|
|
{
|
|
readFaults( data, filePos, faults, nullptr );
|
|
filePos = findKeyword( faultsKeyword, data, filePos );
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RifEclipseInputFileTools::readFaultsInGridSection( const QString& fileName,
|
|
cvf::Collection<RigFault>* faults,
|
|
std::vector<QString>* filenamesWithFaults,
|
|
const QString& faultIncludeFileAbsolutePathPrefix )
|
|
{
|
|
QFile data( fileName );
|
|
if ( !data.open( QFile::ReadOnly ) )
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Search for keyword grid
|
|
qint64 gridPos = findKeyword( gridKeyword, data, 0 );
|
|
if ( gridPos < 0 )
|
|
{
|
|
return;
|
|
}
|
|
|
|
bool isEditKeywordDetected = false;
|
|
|
|
std::vector<std::pair<QString, QString>> pathAliasDefinitions;
|
|
parseAndReadPathAliasKeyword( fileName, &pathAliasDefinitions );
|
|
|
|
readFaultsAndParseIncludeStatementsRecursively( data,
|
|
gridPos,
|
|
pathAliasDefinitions,
|
|
faults,
|
|
filenamesWithFaults,
|
|
&isEditKeywordDetected,
|
|
faultIncludeFileAbsolutePathPrefix );
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
size_t RifEclipseInputFileTools::findFaultByName( const cvf::Collection<RigFault>& faults, const QString& name )
|
|
{
|
|
for ( size_t i = 0; i < faults.size(); i++ )
|
|
{
|
|
if ( faults.at( i )->name() == name )
|
|
{
|
|
return i;
|
|
}
|
|
}
|
|
|
|
return cvf::UNDEFINED_SIZE_T;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
qint64 RifEclipseInputFileTools::findKeyword( const QString& keyword, QFile& file, qint64 startPos )
|
|
{
|
|
QString line;
|
|
|
|
file.seek( startPos );
|
|
|
|
do
|
|
{
|
|
line = file.readLine();
|
|
line = line.trimmed();
|
|
|
|
if ( line.startsWith( "--", Qt::CaseInsensitive ) )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if ( line.startsWith( keyword, Qt::CaseInsensitive ) )
|
|
{
|
|
return file.pos();
|
|
}
|
|
|
|
} while ( !file.atEnd() );
|
|
|
|
return -1;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RifEclipseInputFileTools::writeFaultLine( QTextStream& stream,
|
|
QString faultName,
|
|
size_t i,
|
|
size_t j,
|
|
size_t startK,
|
|
size_t endK,
|
|
cvf::StructGridInterface::FaceType faceType )
|
|
{
|
|
// Convert indices to eclipse format
|
|
i++;
|
|
j++;
|
|
startK++;
|
|
endK++;
|
|
|
|
stream << "'" << faultName << "'"
|
|
<< " " << i << " " << i << " " << j << " " << j << " " << startK << " " << endK << " "
|
|
<< faultFaceText( faceType ) << " / ";
|
|
stream << '\n';
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
QString RifEclipseInputFileTools::faultFaceText( cvf::StructGridInterface::FaceType faceType )
|
|
{
|
|
switch ( faceType )
|
|
{
|
|
case cvf::StructGridInterface::POS_I:
|
|
return QString( "I " );
|
|
case cvf::StructGridInterface::NEG_I:
|
|
return QString( "I-" );
|
|
case cvf::StructGridInterface::POS_J:
|
|
return QString( "J " );
|
|
case cvf::StructGridInterface::NEG_J:
|
|
return QString( "J-" );
|
|
case cvf::StructGridInterface::POS_K:
|
|
return QString( "K " );
|
|
case cvf::StructGridInterface::NEG_K:
|
|
return QString( "K-" );
|
|
default:
|
|
CVF_ASSERT( false );
|
|
}
|
|
return "";
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
bool RifEclipseInputFileTools::readFaultsAndParseIncludeStatementsRecursively( QFile& file,
|
|
qint64 startPos,
|
|
const std::vector<std::pair<QString, QString>>& pathAliasDefinitions,
|
|
cvf::Collection<RigFault>* faults,
|
|
std::vector<QString>* filenamesWithFaults,
|
|
bool* isEditKeywordDetected,
|
|
const QString& faultIncludeFileAbsolutePathPrefix )
|
|
{
|
|
QString line;
|
|
|
|
if ( !file.seek( startPos ) )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
bool continueParsing = true;
|
|
|
|
do
|
|
{
|
|
line = file.readLine();
|
|
line = line.trimmed();
|
|
|
|
if ( line.startsWith( "--", Qt::CaseInsensitive ) )
|
|
{
|
|
continue;
|
|
}
|
|
else if ( line.startsWith( editKeyword, Qt::CaseInsensitive ) )
|
|
{
|
|
if ( isEditKeywordDetected )
|
|
{
|
|
*isEditKeywordDetected = true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
if ( line.startsWith( includeKeyword, Qt::CaseInsensitive ) )
|
|
{
|
|
line = file.readLine();
|
|
line = line.trimmed();
|
|
|
|
while ( line.startsWith( "--", Qt::CaseInsensitive ) )
|
|
{
|
|
line = file.readLine();
|
|
line = line.trimmed();
|
|
}
|
|
|
|
int firstQuote = line.indexOf( "'" );
|
|
int lastQuote = line.lastIndexOf( "'" );
|
|
|
|
if ( !( firstQuote < 0 || lastQuote < 0 || firstQuote == lastQuote ) )
|
|
{
|
|
QDir currentFileFolder;
|
|
{
|
|
QFileInfo fi( file.fileName() );
|
|
currentFileFolder = fi.absoluteDir();
|
|
}
|
|
|
|
// Read include file name, and both relative and absolute path is supported
|
|
QString includeFilename = line.mid( firstQuote + 1, lastQuote - firstQuote - 1 );
|
|
|
|
for ( auto entry : pathAliasDefinitions )
|
|
{
|
|
QString textToReplace = "$" + entry.first;
|
|
includeFilename.replace( textToReplace, entry.second );
|
|
}
|
|
|
|
#ifdef WIN32
|
|
if ( includeFilename.startsWith( '/' ) )
|
|
{
|
|
// Absolute UNIX path, prefix on Windows
|
|
includeFilename = faultIncludeFileAbsolutePathPrefix + includeFilename;
|
|
}
|
|
#endif
|
|
|
|
QFileInfo fi( currentFileFolder, includeFilename );
|
|
if ( fi.exists() )
|
|
{
|
|
QString absoluteFilename = fi.canonicalFilePath();
|
|
QFile includeFile( absoluteFilename );
|
|
if ( includeFile.open( QFile::ReadOnly ) )
|
|
{
|
|
// qDebug() << "Found include statement, and start parsing of\n " << absoluteFilename;
|
|
|
|
if ( !readFaultsAndParseIncludeStatementsRecursively( includeFile,
|
|
0,
|
|
pathAliasDefinitions,
|
|
faults,
|
|
filenamesWithFaults,
|
|
isEditKeywordDetected,
|
|
faultIncludeFileAbsolutePathPrefix ) )
|
|
{
|
|
qDebug() << "Error when parsing include file : " << absoluteFilename;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if ( line.startsWith( faultsKeyword, Qt::CaseInsensitive ) )
|
|
{
|
|
if ( !line.contains( "/" ) )
|
|
{
|
|
readFaults( file, file.pos(), faults, isEditKeywordDetected );
|
|
filenamesWithFaults->push_back( file.fileName() );
|
|
}
|
|
}
|
|
|
|
if ( isEditKeywordDetected && *isEditKeywordDetected )
|
|
{
|
|
continueParsing = false;
|
|
}
|
|
|
|
if ( file.atEnd() )
|
|
{
|
|
continueParsing = false;
|
|
}
|
|
|
|
} while ( continueParsing );
|
|
|
|
return true;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
bool RifEclipseInputFileTools::readKeywordAndParseIncludeStatementsRecursively(
|
|
const QString& keyword,
|
|
const QString& keywordToStopParsing,
|
|
QFile& file,
|
|
qint64 startPos,
|
|
const std::vector<std::pair<QString, QString>>& pathAliasDefinitions,
|
|
QStringList* keywordDataContent,
|
|
std::vector<QString>* filenamesContainingKeyword,
|
|
bool* isStopParsingKeywordDetected,
|
|
const QString& faultIncludeFileAbsolutePathPrefix /* rename to includeStatementAbsolutePathPrefix */ )
|
|
{
|
|
QString line;
|
|
|
|
if ( !file.seek( startPos ) )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
bool continueParsing = true;
|
|
|
|
do
|
|
{
|
|
line = file.readLine();
|
|
line = line.trimmed();
|
|
|
|
if ( line.startsWith( "--", Qt::CaseInsensitive ) )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if ( !keywordToStopParsing.isEmpty() && line.startsWith( keywordToStopParsing, Qt::CaseInsensitive ) )
|
|
{
|
|
if ( isStopParsingKeywordDetected )
|
|
{
|
|
*isStopParsingKeywordDetected = true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
if ( line.startsWith( includeKeyword, Qt::CaseInsensitive ) )
|
|
{
|
|
line = file.readLine();
|
|
line = line.trimmed();
|
|
|
|
while ( line.startsWith( "--", Qt::CaseInsensitive ) )
|
|
{
|
|
line = file.readLine();
|
|
line = line.trimmed();
|
|
}
|
|
|
|
int firstQuote = line.indexOf( "'" );
|
|
int lastQuote = line.lastIndexOf( "'" );
|
|
|
|
if ( !( firstQuote < 0 || lastQuote < 0 || firstQuote == lastQuote ) )
|
|
{
|
|
QDir currentFileFolder;
|
|
{
|
|
QFileInfo fi( file.fileName() );
|
|
currentFileFolder = fi.absoluteDir();
|
|
}
|
|
|
|
// Read include file name, and both relative and absolute path is supported
|
|
QString includeFilename = line.mid( firstQuote + 1, lastQuote - firstQuote - 1 );
|
|
|
|
for ( auto entry : pathAliasDefinitions )
|
|
{
|
|
QString textToReplace = "$" + entry.first;
|
|
includeFilename.replace( textToReplace, entry.second );
|
|
}
|
|
|
|
#ifdef WIN32
|
|
if ( includeFilename.startsWith( '/' ) )
|
|
{
|
|
// Absolute UNIX path, prefix on Windows
|
|
includeFilename = faultIncludeFileAbsolutePathPrefix + includeFilename;
|
|
}
|
|
#endif
|
|
|
|
QFileInfo fi( currentFileFolder, includeFilename );
|
|
if ( fi.exists() )
|
|
{
|
|
QString absoluteFilename = fi.canonicalFilePath();
|
|
QFile includeFile( absoluteFilename );
|
|
if ( includeFile.open( QFile::ReadOnly ) )
|
|
{
|
|
// qDebug() << "Found include statement, and start parsing of\n " << absoluteFilename;
|
|
|
|
if ( !readKeywordAndParseIncludeStatementsRecursively( keyword,
|
|
keywordToStopParsing,
|
|
includeFile,
|
|
0,
|
|
pathAliasDefinitions,
|
|
keywordDataContent,
|
|
filenamesContainingKeyword,
|
|
isStopParsingKeywordDetected,
|
|
faultIncludeFileAbsolutePathPrefix ) )
|
|
{
|
|
qDebug() << "Error when parsing include file : " << absoluteFilename;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if ( line.startsWith( keyword, Qt::CaseInsensitive ) )
|
|
{
|
|
if ( !line.contains( "/" ) )
|
|
{
|
|
readKeywordDataContent( file, file.pos(), keywordDataContent, isStopParsingKeywordDetected );
|
|
filenamesContainingKeyword->push_back( file.fileName() );
|
|
}
|
|
}
|
|
|
|
if ( isStopParsingKeywordDetected && *isStopParsingKeywordDetected )
|
|
{
|
|
continueParsing = false;
|
|
}
|
|
|
|
if ( file.atEnd() )
|
|
{
|
|
continueParsing = false;
|
|
}
|
|
|
|
} while ( continueParsing );
|
|
|
|
return true;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RifEclipseInputFileTools::readKeywordDataContent( QFile& data, qint64 filePos, QStringList* textContent, bool* isEditKeywordDetected )
|
|
{
|
|
if ( !data.seek( filePos ) )
|
|
{
|
|
return;
|
|
}
|
|
|
|
// This function assumes the keyword is read from file, and the file pointer is pointing to the first line
|
|
// containing data for the keyword
|
|
|
|
do
|
|
{
|
|
QString line = data.readLine();
|
|
line = line.trimmed();
|
|
|
|
if ( line.startsWith( "--", Qt::CaseInsensitive ) )
|
|
{
|
|
// Skip comment lines
|
|
continue;
|
|
}
|
|
else if ( line.startsWith( "/", Qt::CaseInsensitive ) )
|
|
{
|
|
// Detected end of keyword data section
|
|
return;
|
|
}
|
|
else if ( line.startsWith( editKeyword, Qt::CaseInsensitive ) )
|
|
{
|
|
// End parsing when edit keyword is detected
|
|
|
|
if ( isEditKeywordDetected )
|
|
{
|
|
*isEditKeywordDetected = true;
|
|
}
|
|
|
|
return;
|
|
}
|
|
else if ( line[0].isLetter() )
|
|
{
|
|
// If a letter is starting the line, this is a new keyword
|
|
return;
|
|
}
|
|
|
|
if ( !line.isEmpty() )
|
|
{
|
|
textContent->push_back( line );
|
|
}
|
|
|
|
} while ( !data.atEnd() );
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
RiaDefines::EclipseUnitSystem RifEclipseInputFileTools::readUnitSystem( QFile& file, qint64 gridunitPos )
|
|
{
|
|
bool stopParsing = false;
|
|
QStringList unitText;
|
|
readKeywordDataContent( file, gridunitPos, &unitText, &stopParsing );
|
|
for ( QString unitString : unitText )
|
|
{
|
|
if ( unitString.contains( "FEET", Qt::CaseInsensitive ) )
|
|
{
|
|
return RiaDefines::EclipseUnitSystem::UNITS_FIELD;
|
|
}
|
|
else if ( unitString.contains( "CM", Qt::CaseInsensitive ) )
|
|
{
|
|
return RiaDefines::EclipseUnitSystem::UNITS_LAB;
|
|
}
|
|
else if ( unitString.contains( "MET", Qt::CaseInsensitive ) )
|
|
{
|
|
return RiaDefines::EclipseUnitSystem::UNITS_METRIC;
|
|
}
|
|
}
|
|
return RiaDefines::EclipseUnitSystem::UNITS_UNKNOWN;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
cvf::StructGridInterface::FaceEnum RifEclipseInputFileTools::faceEnumFromText( const QString& faceString )
|
|
{
|
|
QString upperCaseText = faceString.toUpper().trimmed();
|
|
|
|
if ( upperCaseText.size() > 1 )
|
|
{
|
|
QString firstTwoChars = upperCaseText.mid( 0, 2 );
|
|
|
|
if ( firstTwoChars == "X+" || firstTwoChars == "I+" ) return cvf::StructGridInterface::POS_I;
|
|
if ( firstTwoChars == "Y+" || firstTwoChars == "J+" ) return cvf::StructGridInterface::POS_J;
|
|
if ( firstTwoChars == "Z+" || firstTwoChars == "K+" ) return cvf::StructGridInterface::POS_K;
|
|
|
|
if ( firstTwoChars == "X-" || firstTwoChars == "I-" ) return cvf::StructGridInterface::NEG_I;
|
|
if ( firstTwoChars == "Y-" || firstTwoChars == "J-" ) return cvf::StructGridInterface::NEG_J;
|
|
if ( firstTwoChars == "Z-" || firstTwoChars == "K-" ) return cvf::StructGridInterface::NEG_K;
|
|
}
|
|
|
|
if ( upperCaseText.size() > 0 )
|
|
{
|
|
QString firstChar = upperCaseText.mid( 0, 1 );
|
|
|
|
if ( firstChar == "X" || firstChar == "I" ) return cvf::StructGridInterface::POS_I;
|
|
if ( firstChar == "Y" || firstChar == "J" ) return cvf::StructGridInterface::POS_J;
|
|
if ( firstChar == "Z" || firstChar == "K" ) return cvf::StructGridInterface::POS_K;
|
|
}
|
|
|
|
return cvf::StructGridInterface::NO_FACE;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
bool RifEclipseInputFileTools::hasGridData( const QString& fileName )
|
|
{
|
|
// Look for keyword "COORD" in file
|
|
// NOTE: If readKeywordAndValues is slow for large .GRDECL files, consider function in RifEclipseTextFileReader
|
|
// reading line for line and returns true on first hit of keyword. This prevents reading entire file on large cases
|
|
|
|
const auto keywordAndValues = RifEclipseTextFileReader::readKeywordAndValues( fileName.toStdString() );
|
|
auto coordKeywordItr = std::find_if( keywordAndValues.begin(),
|
|
keywordAndValues.end(),
|
|
[]( const auto& keywordAndValue ) { return keywordAndValue.keyword == "COORD"; } );
|
|
|
|
return coordKeywordItr != keywordAndValues.end();
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
/// The file pointer is pointing at the line following the FAULTS keyword.
|
|
/// Parse content of this keyword until end of file or
|
|
/// end of keyword when a single line with '/' is found
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RifEclipseInputFileTools::readFaults( QFile& data, qint64 filePos, cvf::Collection<RigFault>* faults, bool* isEditKeywordDetected )
|
|
{
|
|
if ( !data.seek( filePos ) )
|
|
{
|
|
return;
|
|
}
|
|
|
|
// qDebug() << "Reading faults from\n " << data.fileName();
|
|
|
|
std::set<QString> errorMessages;
|
|
std::set<QString> warningMessages;
|
|
|
|
RigFault* fault = nullptr;
|
|
|
|
do
|
|
{
|
|
QString line = data.readLine();
|
|
line = line.trimmed();
|
|
|
|
if ( line.startsWith( "--", Qt::CaseInsensitive ) )
|
|
{
|
|
// Skip comment lines
|
|
continue;
|
|
}
|
|
else if ( line.startsWith( "/", Qt::CaseInsensitive ) )
|
|
{
|
|
// Detected end of keyword data section
|
|
break;
|
|
}
|
|
else if ( line.startsWith( editKeyword, Qt::CaseInsensitive ) )
|
|
{
|
|
// End parsing when edit keyword is detected
|
|
|
|
if ( isEditKeywordDetected )
|
|
{
|
|
*isEditKeywordDetected = true;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
// Replace tab with space to be able to split the string using space as splitter
|
|
line.replace( "\t", " " );
|
|
|
|
QStringList entries;
|
|
bool insideQuotes = false;
|
|
QString column;
|
|
for ( int i = 0; i < line.length(); ++i )
|
|
{
|
|
if ( line[i] == '\'' )
|
|
{
|
|
insideQuotes = !insideQuotes;
|
|
}
|
|
else if ( line[i] == ' ' && !insideQuotes )
|
|
{
|
|
if ( column.length() > 0 )
|
|
{
|
|
entries.push_back( column );
|
|
}
|
|
column.clear();
|
|
}
|
|
else
|
|
{
|
|
column += line[i];
|
|
}
|
|
}
|
|
|
|
if ( entries.size() < 8 )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
QString faultName = entries[0];
|
|
|
|
if ( faultName.contains( ' ' ) )
|
|
{
|
|
errorMessages.insert( QString( "Fault name '%1' contains spaces" ).arg( faultName ) );
|
|
continue;
|
|
}
|
|
else if ( faultName.length() > 8 )
|
|
{
|
|
// Keep going anyway, eclipse files sometimes have longer than
|
|
// the specified 8 characters in the name without Eclipse complaining
|
|
warningMessages.insert( QString( "Fault name '%1' is longer than 8 characters" ).arg( faultName ) );
|
|
}
|
|
|
|
int i1, i2, j1, j2, k1, k2;
|
|
i1 = entries[1].toInt();
|
|
i2 = entries[2].toInt();
|
|
j1 = entries[3].toInt();
|
|
j2 = entries[4].toInt();
|
|
k1 = entries[5].toInt();
|
|
k2 = entries[6].toInt();
|
|
|
|
QString faceString = entries[7];
|
|
|
|
cvf::StructGridInterface::FaceEnum cellFaceEnum = RifEclipseInputFileTools::faceEnumFromText( faceString );
|
|
|
|
// Adjust from 1-based to 0-based cell indices
|
|
// Guard against invalid cell ranges by limiting lowest possible range value to zero
|
|
cvf::CellRange cellrange( std::max( i1 - 1, 0 ),
|
|
std::max( j1 - 1, 0 ),
|
|
std::max( k1 - 1, 0 ),
|
|
std::max( i2 - 1, 0 ),
|
|
std::max( j2 - 1, 0 ),
|
|
std::max( k2 - 1, 0 ) );
|
|
|
|
if ( !( fault && fault->name() == faultName ) )
|
|
{
|
|
if ( findFaultByName( *faults, faultName ) == cvf::UNDEFINED_SIZE_T )
|
|
{
|
|
RigFault* newFault = new RigFault;
|
|
newFault->setName( faultName );
|
|
|
|
faults->push_back( newFault );
|
|
}
|
|
|
|
size_t faultIndex = findFaultByName( *faults, faultName );
|
|
if ( faultIndex == cvf::UNDEFINED_SIZE_T )
|
|
{
|
|
CVF_ASSERT( faultIndex != cvf::UNDEFINED_SIZE_T );
|
|
continue;
|
|
}
|
|
|
|
fault = faults->at( faultIndex );
|
|
}
|
|
|
|
CVF_ASSERT( fault );
|
|
|
|
fault->addCellRangeForFace( cellFaceEnum, cellrange );
|
|
|
|
} while ( !data.atEnd() );
|
|
|
|
for ( QString errorMessage : errorMessages )
|
|
{
|
|
RiaLogging::error( errorMessage );
|
|
}
|
|
|
|
for ( QString warningMessage : warningMessages )
|
|
{
|
|
RiaLogging::warning( warningMessage );
|
|
}
|
|
}
|