///////////////////////////////////////////////////////////////////////////////// // // 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 // for more details. // ///////////////////////////////////////////////////////////////////////////////// #include "RifEclipseInputFileTools.h" #include "RiaCellDividingTools.h" #include "RiaLogging.h" #include "RiaStringEncodingTools.h" #include "RifReaderEclipseOutput.h" #include "RigActiveCellInfo.h" #include "RigCaseCellResultsData.h" #include "RigEclipseCaseData.h" #include "RigFault.h" #include "RigMainGrid.h" #include "cafProgressInfo.h" #include #include #include #include #include #include #include #include #include #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 ) { CVF_ASSERT( eclipseCase && errorMessages ); std::vector keywordsAndFilePos; findKeywordsOnFile( fileName, &keywordsAndFilePos ); qint64 coordPos = -1; qint64 zcornPos = -1; qint64 specgridPos = -1; qint64 actnumPos = -1; qint64 mapaxesPos = -1; qint64 gridunitPos = -1; findGridKeywordPositions( keywordsAndFilePos, &coordPos, &zcornPos, &specgridPos, &actnumPos, &mapaxesPos, &gridunitPos ); if ( coordPos < 0 || zcornPos < 0 || specgridPos < 0 ) { QString errorText = QString( "Failed to import grid file '%1'\n" ).arg( fileName ); if ( coordPos < 0 ) { errorText += " Missing required keyword COORD"; } if ( zcornPos < 0 ) { errorText += " Missing required keyword ZCORN"; } if ( specgridPos < 0 ) { errorText += " Missing required keyword SPECGRID"; } *errorMessages += errorText; return false; } if ( gridunitPos >= 0 ) { QFile gridFile( fileName ); if ( gridFile.open( QFile::ReadOnly ) ) { RiaDefines::EclipseUnitSystem units = readUnitSystem( gridFile, gridunitPos ); if ( units != RiaDefines::EclipseUnitSystem::UNITS_UNKNOWN ) { eclipseCase->setUnitsType( units ); } } } FILE* gridFilePointer = util_fopen( fileName.toLatin1().data(), "r" ); if ( !gridFilePointer ) return false; // Main grid dimensions // SPECGRID - This is whats normally available, but not really the input to Eclipse. // DIMENS - Is what Eclipse expects and uses, but is not defined in the GRID section and is not (?) available // normally ZCORN, COORD, ACTNUM, MAPAXES // ecl_kw_type * ecl_kw_fscanf_alloc_grdecl_dynamic__( FILE * stream , const char * kw , bool strict , // ecl_type_enum ecl_type); ecl_grid_type * ecl_grid_alloc_GRDECL_kw( int nx, int ny , int nz , const ecl_kw_type * // zcorn_kw , const ecl_kw_type * coord_kw , const ecl_kw_type * actnum_kw , const ecl_kw_type * mapaxes_kw ); 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; // Try to read all the needed keywords. Early exit if some are not found caf::ProgressInfo progress( 8, "Read Grid from Eclipse Input file" ); bool allKwReadOk = true; fseek( gridFilePointer, specgridPos, SEEK_SET ); allKwReadOk = allKwReadOk && nullptr != ( specGridKw = ecl_kw_fscanf_alloc_current_grdecl__( gridFilePointer, false, ecl_type_create_from_type( ECL_INT_TYPE ) ) ); progress.setProgress( 1 ); fseek( gridFilePointer, zcornPos, SEEK_SET ); allKwReadOk = allKwReadOk && nullptr != ( zCornKw = ecl_kw_fscanf_alloc_current_grdecl__( gridFilePointer, false, ecl_type_create_from_type( ECL_FLOAT_TYPE ) ) ); progress.setProgress( 2 ); fseek( gridFilePointer, coordPos, SEEK_SET ); allKwReadOk = allKwReadOk && nullptr != ( coordKw = ecl_kw_fscanf_alloc_current_grdecl__( gridFilePointer, false, ecl_type_create_from_type( ECL_FLOAT_TYPE ) ) ); progress.setProgress( 3 ); // If ACTNUM is not defined, this pointer will be nullptr, which is a valid condition if ( actnumPos >= 0 ) { fseek( gridFilePointer, actnumPos, SEEK_SET ); allKwReadOk = allKwReadOk && nullptr != ( actNumKw = ecl_kw_fscanf_alloc_current_grdecl__( gridFilePointer, false, ecl_type_create_from_type( ECL_INT_TYPE ) ) ); progress.setProgress( 4 ); } // If MAPAXES is not defined, this pointer will be nullptr, which is a valid condition if ( mapaxesPos >= 0 ) { fseek( gridFilePointer, mapaxesPos, SEEK_SET ); mapAxesKw = ecl_kw_fscanf_alloc_current_grdecl__( gridFilePointer, false, ecl_type_create_from_type( ECL_FLOAT_TYPE ) ); } if ( !allKwReadOk ) { if ( specGridKw ) ecl_kw_free( specGridKw ); if ( zCornKw ) ecl_kw_free( zCornKw ); if ( coordKw ) ecl_kw_free( coordKw ); if ( actNumKw ) ecl_kw_free( actNumKw ); if ( mapAxesKw ) ecl_kw_free( mapAxesKw ); return false; } progress.setProgress( 5 ); 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 ); progress.setProgress( 6 ); RifReaderEclipseOutput::transferGeometry( inputGrid, eclipseCase ); progress.setProgress( 7 ); progress.setProgressDescription( "Read faults ..." ); if ( readFaultData ) { cvf::Collection faults; RifEclipseInputFileTools::readFaults( fileName, keywordsAndFilePos, &faults ); RigMainGrid* mainGrid = eclipseCase->mainGrid(); mainGrid->setFaults( faults ); } bool useMapAxes = ecl_grid_use_mapaxes( inputGrid ); eclipseCase->mainGrid()->setUseMapAxes( useMapAxes ); if ( useMapAxes ) { std::array mapAxesValues; ecl_grid_init_mapaxes_data_double( inputGrid, mapAxesValues.data() ); eclipseCase->mainGrid()->setMapAxes( mapAxesValues ); } progress.setProgress( 8 ); progress.setProgressDescription( "Cleaning up ..." ); 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 ); fclose( gridFilePointer ); return true; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- 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( ( max.x() - min.x() ) * refinement.x() + 1 ); int ecl_ny = static_cast( ( max.y() - min.y() ) * refinement.y() + 1 ); int ecl_nz = static_cast( ( 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 ecl_corners; ecl_corners.reserve( mainGrid->cellCount() * cellsPerOriginal ); std::vector ecl_coords; ecl_coords.reserve( mainGrid->cellCount() * cellsPerOriginal ); std::array 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 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& keywords, const QString& fileWriteMode, const cvf::Vec3st& min, const cvf::Vec3st& maxIn, const cvf::Vec3st& refinement ) { FILE* filePtr = util_fopen( RiaStringEncodingTools::toNativeEncoded( resultFileName ).data(), RiaStringEncodingTools::toNativeEncoded( fileWriteMode ).data() ); if ( !filePtr ) { return false; } 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 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 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 ); } } } } ecl_kw_type* ecl_kw = nullptr; if ( keyword.endsWith( "NUM" ) ) { std::vector resultValuesInt; resultValuesInt.reserve( filteredResults.size() ); for ( double val : filteredResults ) { resultValuesInt.push_back( static_cast( val ) ); } ecl_kw = ecl_kw_alloc_new( keyword.toLatin1().data(), (int)resultValuesInt.size(), ECL_INT, resultValuesInt.data() ); } else { std::vector resultValuesFloat; resultValuesFloat.reserve( filteredResults.size() ); for ( double val : filteredResults ) { resultValuesFloat.push_back( static_cast( val ) ); } ecl_kw = ecl_kw_alloc_new( keyword.toLatin1().data(), (int)resultValuesFloat.size(), ECL_FLOAT, resultValuesFloat.data() ); } ecl_kw_fprintf_grdecl( ecl_kw, filePtr ); ecl_kw_free( ecl_kw ); progress.incrementProgress(); } fclose( filePtr ); return true; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RifEclipseInputFileTools::saveFault( QString completeFilename, const RigMainGrid* mainGrid, const std::vector& 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 ); } QTextStream stream( &exportFile ); stream << "FAULTS" << endl; stream << "-- Name I1 I2 J1 J2 K1 K2 Face ( I/J/K )" << endl; saveFault( stream, mainGrid, faultFaces, faultName, min, maxIn, refinement ); stream << "/" << endl; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RifEclipseInputFileTools::saveFault( QTextStream& stream, const RigMainGrid* mainGrid, const std::vector& 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 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::max(); size_t lastJ = std::numeric_limits::max(); size_t lastK = std::numeric_limits::max(); size_t startK = std::numeric_limits::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" << endl; stream << "-- Name I1 I2 J1 J2 K1 K2 Face ( I/J/K )" << endl; const cvf::Collection& 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 << "/" << endl; } //-------------------------------------------------------------------------------------------------- /// Read known properties from the input file //-------------------------------------------------------------------------------------------------- std::map RifEclipseInputFileTools::readProperties( const QString& fileName, RigEclipseCaseData* caseData ) { CVF_ASSERT( caseData ); caf::ProgressInfo mainProgress( 2, "Reading Eclipse Input properties" ); std::vector fileKeywords; RifEclipseInputFileTools::findKeywordsOnFile( fileName, &fileKeywords ); if ( !fileKeywords.size() ) { RiaLogging::warning( QString( "No keywords found in file: %1" ).arg( fileName ) ); } mainProgress.setProgress( 1 ); caf::ProgressInfo progress( fileKeywords.size(), "Reading properties" ); FILE* gridFilePointer = util_fopen( fileName.toLatin1().data(), "r" ); if ( !gridFilePointer ) { RiaLogging::warning( QString( "Could not open file: %1" ).arg( fileName ) ); return std::map(); } std::map newResults; for ( size_t i = 0; i < fileKeywords.size(); ++i ) { if ( !isValidDataKeyword( fileKeywords[i].keyword ) ) { continue; } fseek( gridFilePointer, fileKeywords[i].filePos, SEEK_SET ); ecl_kw_type* eclipseKeywordData = ecl_kw_fscanf_alloc_current_grdecl__( gridFilePointer, false, ecl_type_create_from_type( ECL_FLOAT_TYPE ) ); if ( eclipseKeywordData ) { QString newResultName = caseData->results( RiaDefines::PorosityModelType::MATRIX_MODEL )->makeResultNameUnique( fileKeywords[i].keyword ); QString errMsg; if ( readDataFromKeyword( eclipseKeywordData, caseData, newResultName, &errMsg ) ) { newResults[newResultName] = fileKeywords[i].keyword; } else { RiaLogging::error( QString( "Failed to read keyword: %1" ).arg( errMsg ) ); } ecl_kw_free( eclipseKeywordData ); } else { RiaLogging::error( QString( "Failed to allocate keyword: %1" ).arg( fileKeywords[i].keyword ) ); } progress.setProgress( i ); } fclose( gridFilePointer ); return newResults; } //-------------------------------------------------------------------------------------------------- /// Reads the property data requested into the \a reservoir, overwriting any previous /// properties with the same name. //-------------------------------------------------------------------------------------------------- bool RifEclipseInputFileTools::readProperty( const QString& fileName, RigEclipseCaseData* caseData, const QString& eclipseKeyWord, const QString& resultName ) { CVF_ASSERT( caseData ); if ( !isValidDataKeyword( eclipseKeyWord ) ) return false; FILE* filePointer = util_fopen( fileName.toLatin1().data(), "r" ); if ( !filePointer ) { RiaLogging::error( QString( "Could not open property file: %1" ).arg( fileName ) ); return false; } qint64 filePos = -1; { std::vector keywordsAndFilePos; findKeywordsOnFile( fileName, &keywordsAndFilePos ); for ( auto kwAndPos : keywordsAndFilePos ) { if ( kwAndPos.keyword == eclipseKeyWord ) { filePos = kwAndPos.filePos; } } } bool isOk = false; if ( filePos == -1 ) { RiaLogging::error( QString( "Failed to load keyword %1 from file: %2" ).arg( eclipseKeyWord ).arg( fileName ) ); } else { fseek( filePointer, filePos, SEEK_SET ); ecl_kw_type* eclipseKeywordData = ecl_kw_fscanf_alloc_grdecl_dynamic__( filePointer, eclipseKeyWord.toLatin1().data(), false, ecl_type_create_from_type( ECL_FLOAT_TYPE ) ); if ( eclipseKeywordData ) { QString errMsg; isOk = readDataFromKeyword( eclipseKeywordData, caseData, resultName, &errMsg ); if ( !isOk ) { RiaLogging::error( QString( "Failed to read property: %1" ).arg( errMsg ) ); } ecl_kw_free( eclipseKeywordData ); } else { RiaLogging::error( QString( "Failed to load keyword %1 from file: %2" ).arg( eclipseKeyWord ).arg( fileName ) ); } } fclose( filePointer ); return isOk; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- bool RifEclipseInputFileTools::readDataFromKeyword( ecl_kw_type* eclipseKeywordData, RigEclipseCaseData* caseData, const QString& resultName, QString* errMsg ) { CVF_ASSERT( caseData ); CVF_ASSERT( eclipseKeywordData ); CVF_ASSERT( errMsg ); // Number of values to allocate in the result data structure. Must either be number of active cells or // number of total cells in case to match the criteria in RigCaseCellResultsData::isUsingGlobalActiveIndex size_t scalarValueCount = 0u; { bool mathingItemCount = false; size_t keywordItemCount = static_cast( ecl_kw_get_size( eclipseKeywordData ) ); if ( keywordItemCount == caseData->mainGrid()->cellCount() ) { mathingItemCount = true; scalarValueCount = caseData->mainGrid()->globalCellArray().size(); } else if ( keywordItemCount == caseData->activeCellInfo( RiaDefines::PorosityModelType::MATRIX_MODEL )->reservoirActiveCellCount() ) { mathingItemCount = true; scalarValueCount = caseData->activeCellInfo( RiaDefines::PorosityModelType::MATRIX_MODEL )->reservoirActiveCellCount(); } if ( !mathingItemCount ) { QString errFormat( "Size mismatch: Main Grid has %1 cells, keyword %2 has %3 cells" ); *errMsg = errFormat.arg( caseData->mainGrid()->cellCount() ).arg( resultName ).arg( keywordItemCount ); return false; } } RigEclipseResultAddress resAddr( RiaDefines::ResultCatType::INPUT_PROPERTY, resultName ); caseData->results( RiaDefines::PorosityModelType::MATRIX_MODEL )->createResultEntry( resAddr, false ); auto newPropertyData = caseData->results( RiaDefines::PorosityModelType::MATRIX_MODEL )->modifiableCellScalarResultTimesteps( resAddr ); newPropertyData->push_back( std::vector() ); newPropertyData->at( 0 ).resize( scalarValueCount, HUGE_VAL ); ecl_kw_get_data_as_double( eclipseKeywordData, newPropertyData->at( 0 ).data() ); return true; } //-------------------------------------------------------------------------------------------------- /// Read all the keywords from a file // // This code was originally written using QTextStream, but due to a bug in Qt version up to 4.8.0 // we had to implement the reading using QFile and QFile::readLine // // See: // https://bugreports.qt-project.org/browse/QTBUG-9814 // //-------------------------------------------------------------------------------------------------- void RifEclipseInputFileTools::findKeywordsOnFile( const QString& fileName, std::vector* keywords ) { char buf[1024]; QFile data( fileName ); data.open( QFile::ReadOnly ); QString line; qint64 filepos = -1; qint64 lineLength = -1; do { lineLength = data.readLine( buf, sizeof( buf ) ); if ( lineLength > 0 ) { line = QString::fromLatin1( buf ); RifKeywordAndFilePos keyPos; filepos = data.pos() - lineLength; keyPos.filePos = filepos; QString trimmedLine = line; int commentStart = trimmedLine.indexOf( "--" ); if ( commentStart > 0 ) { trimmedLine = trimmedLine.left( commentStart ); } trimmedLine = trimmedLine.trimmed(); if ( !trimmedLine.isEmpty() && trimmedLine[0].isLetter() ) { // Ensure we don't attempt to find keywords with a space in it. QStringList keywordCandidates = trimmedLine.split( " " ); if ( !keywordCandidates.isEmpty() ) { QString keywordCandidate = keywordCandidates.front(); keyPos.keyword = keywordCandidate; keywords->push_back( keyPos ); // qDebug() << keyPos.keyword << " - " << keyPos.filePos; } } } } while ( lineLength != -1 ); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RifEclipseInputFileTools::parseAndReadPathAliasKeyword( const QString& fileName, std::vector>* 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 = line.split( " ", QString::SkipEmptyParts ); if ( entries.size() < 2 ) { continue; } pathAliasDefinitions->push_back( std::make_pair( entries[0], entries[1] ) ); } } } } } while ( !data.atEnd() ); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- const std::vector& RifEclipseInputFileTools::invalidPropertyDataKeywords() { static std::vector keywords; static bool isInitialized = false; if ( !isInitialized ) { // Related to geometry keywords.push_back( "COORD" ); keywords.push_back( "ZCORN" ); keywords.push_back( "SPECGRID" ); keywords.push_back( "MAPAXES" ); keywords.push_back( "NOECHO" ); keywords.push_back( "ECHO" ); keywords.push_back( "MAPUNITS" ); keywords.push_back( "GRIDUNIT" ); keywords.push_back( faultsKeyword ); isInitialized = true; } return keywords; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RifEclipseInputFileTools::findGridKeywordPositions( const std::vector& 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& fileKeywords, cvf::Collection* 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* 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* faults, std::vector* 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> pathAliasDefinitions; parseAndReadPathAliasKeyword( fileName, &pathAliasDefinitions ); readFaultsAndParseIncludeStatementsRecursively( data, gridPos, pathAliasDefinitions, faults, filenamesWithFaults, &isEditKeywordDetected, faultIncludeFileAbsolutePathPrefix ); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- size_t RifEclipseInputFileTools::findFaultByName( const cvf::Collection& 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; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- bool RifEclipseInputFileTools::isValidDataKeyword( const QString& keyword ) { const std::vector& keywordsToSkip = RifEclipseInputFileTools::invalidPropertyDataKeywords(); for ( const QString& keywordToSkip : keywordsToSkip ) { if ( keywordToSkip == keyword.toUpper() ) { return false; } } return true; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- 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 << endl; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- 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>& pathAliasDefinitions, cvf::Collection* faults, std::vector* 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>& pathAliasDefinitions, QStringList* keywordDataContent, std::vector* 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; } //-------------------------------------------------------------------------------------------------- /// 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* faults, bool* isEditKeywordDetected ) { if ( !data.seek( filePos ) ) { return; } // qDebug() << "Reading faults from\n " << data.fileName(); std::set errorMessages; std::set 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 ); } }