///////////////////////////////////////////////////////////////////////////////// // // Copyright (C) 2025 Equinor ASA // // ResInsight is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // ResInsight is distributed in the hope that it will be useful, but WITHOUT ANY // WARRANTY; without even the implied warranty of MERCHANTABILITY or // FITNESS FOR A PARTICULAR PURPOSE. // // See the GNU General Public License at // for more details. // ///////////////////////////////////////////////////////////////////////////////// #include "RigEclipseResultTools.h" #include "RiaDefines.h" #include "RiaPorosityModel.h" #include "RiaResultNames.h" #include "RigActiveCellInfo.h" #include "RigCaseCellResultsData.h" #include "RigEclipseCaseData.h" #include "RigEclipseResultAddress.h" #include "RigMainGrid.h" #include "RigTypeSafeIndex.h" #include "RimEclipseCase.h" #include "RimEclipseResultCase.h" #include "RimEclipseView.h" namespace RigEclipseResultTools { //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void createResultVector( RimEclipseCase& eclipseCase, const QString& resultName, const std::vector& intValues ) { RigEclipseResultAddress resultAddress( RiaDefines::ResultCatType::GENERATED, RiaDefines::ResultDataType::INTEGER, resultName ); auto resultsData = eclipseCase.results( RiaDefines::PorosityModelType::MATRIX_MODEL ); resultsData->addStaticScalarResult( RiaDefines::ResultCatType::GENERATED, resultName, false, intValues.size() ); std::vector* resultVector = resultsData->modifiableCellScalarResult( resultAddress, 0 ); resultVector->resize( intValues.size() ); for ( size_t idx = 0; idx < intValues.size(); idx++ ) { resultVector->at( idx ) = 1.0 * intValues[idx]; } resultsData->recalculateStatistics( resultAddress ); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void generateBorderResult( RimEclipseCase* eclipseCase, cvf::ref customVisibility, const QString& resultName ) { if ( eclipseCase == nullptr || customVisibility.isNull() ) return; auto activeReservoirCellIdxs = eclipseCase->eclipseCaseData()->activeCellInfo( RiaDefines::PorosityModelType::MATRIX_MODEL )->activeReservoirCellIndices(); int numActiveCells = static_cast( activeReservoirCellIdxs.size() ); size_t reservoirCellCount = eclipseCase->eclipseCaseData()->activeCellInfo( RiaDefines::PorosityModelType::MATRIX_MODEL )->reservoirCellCount(); std::vector result( reservoirCellCount, BorderType::INVISIBLE_CELL ); auto grid = eclipseCase->eclipseCaseData()->mainGrid(); // go through all cells, only check those visible #pragma omp parallel for for ( int i = 0; i < numActiveCells; i++ ) { auto cellIdx = activeReservoirCellIdxs[i]; if ( customVisibility->val( cellIdx.value() ) ) { auto neighbors = grid->neighborCells( cellIdx.value(), true /*ignore invalid k layers*/ ); int nVisibleNeighbors = 0; for ( auto nIdx : neighbors ) { if ( customVisibility->val( nIdx ) ) nVisibleNeighbors++; } if ( nVisibleNeighbors == 6 ) { result[cellIdx.value()] = BorderType::INTERIOR_CELL; } else { result[cellIdx.value()] = BorderType::BORDER_CELL; } } } RigEclipseResultTools::createResultVector( *eclipseCase, resultName, result ); eclipseCase->updateConnectedEditors(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void generateOperNumResult( RimEclipseCase* eclipseCase, int borderCellValue ) { if ( eclipseCase == nullptr ) return; // Auto-determine border cell value if not specified if ( borderCellValue == -1 ) { int maxOperNum = findMaxOperNumValue( eclipseCase ); borderCellValue = maxOperNum + 1; } auto activeReservoirCellIdxs = eclipseCase->eclipseCaseData()->activeCellInfo( RiaDefines::PorosityModelType::MATRIX_MODEL )->activeReservoirCellIndices(); // Check if OPERNUM already exists - if so, keep existing values. RigEclipseResultAddress operNumAddrNative( RiaDefines::ResultCatType::STATIC_NATIVE, RiaDefines::ResultDataType::INTEGER, RiaResultNames::opernum() ); RigEclipseResultAddress operNumAddrGenerated( RiaDefines::ResultCatType::GENERATED, RiaDefines::ResultDataType::INTEGER, RiaResultNames::opernum() ); auto resultsData = eclipseCase->results( RiaDefines::PorosityModelType::MATRIX_MODEL ); RigEclipseResultAddress operNumAddr; bool hasExistingOperNum = false; if ( resultsData->hasResultEntry( operNumAddrNative ) ) { operNumAddr = operNumAddrNative; hasExistingOperNum = true; } else if ( resultsData->hasResultEntry( operNumAddrGenerated ) ) { operNumAddr = operNumAddrGenerated; hasExistingOperNum = true; } std::vector result; if ( hasExistingOperNum ) { resultsData->ensureKnownResultLoaded( operNumAddr ); auto existingValues = resultsData->cellScalarResults( operNumAddr, 0 ); result.resize( existingValues.size() ); for ( size_t i = 0; i < existingValues.size(); i++ ) { result[i] = static_cast( existingValues[i] ); } } // Check if BORDNUM exists to modify border cells RigEclipseResultAddress bordNumAddr( RiaDefines::ResultCatType::GENERATED, RiaDefines::ResultDataType::INTEGER, RiaResultNames::bordnum() ); if ( resultsData->hasResultEntry( bordNumAddr ) ) { resultsData->ensureKnownResultLoaded( bordNumAddr ); auto bordNumValues = resultsData->cellScalarResults( bordNumAddr, 0 ); if ( !bordNumValues.empty() ) { if ( result.empty() ) result.resize( bordNumValues.size(), 0 ); for ( auto activeCellIdx : activeReservoirCellIdxs ) { // If BORDNUM = 1 (BORDER_CELL), assign the border cell value if ( static_cast( bordNumValues[activeCellIdx.value()] ) == BorderType::BORDER_CELL ) { result[activeCellIdx.value()] = borderCellValue; } } } } RigEclipseResultTools::createResultVector( *eclipseCase, RiaResultNames::opernum(), result ); eclipseCase->updateConnectedEditors(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- int findMaxOperNumValue( RimEclipseCase* eclipseCase ) { if ( eclipseCase == nullptr ) return 0; auto resultsData = eclipseCase->results( RiaDefines::PorosityModelType::MATRIX_MODEL ); if ( !resultsData ) return 0; // Try to find OPERNUM in both STATIC_NATIVE (from file) and GENERATED (created by us) categories RigEclipseResultAddress operNumAddrNative( RiaDefines::ResultCatType::STATIC_NATIVE, RiaDefines::ResultDataType::INTEGER, RiaResultNames::opernum() ); RigEclipseResultAddress operNumAddrGenerated( RiaDefines::ResultCatType::GENERATED, RiaDefines::ResultDataType::INTEGER, RiaResultNames::opernum() ); RigEclipseResultAddress operNumAddr; bool hasOperNum = false; if ( resultsData->hasResultEntry( operNumAddrNative ) ) { operNumAddr = operNumAddrNative; hasOperNum = true; } else if ( resultsData->hasResultEntry( operNumAddrGenerated ) ) { operNumAddr = operNumAddrGenerated; hasOperNum = true; } if ( !hasOperNum ) return 0; resultsData->ensureKnownResultLoaded( operNumAddr ); auto operNumValues = resultsData->cellScalarResults( operNumAddr, 0 ); if ( operNumValues.empty() ) return 0; // Find maximum value int maxValue = 0; for ( double value : operNumValues ) { maxValue = std::max( maxValue, static_cast( value ) ); } return maxValue; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- std::vector generateBorderCellFaces( RimEclipseCase* eclipseCase ) { if ( eclipseCase == nullptr ) return {}; auto resultsData = eclipseCase->results( RiaDefines::PorosityModelType::MATRIX_MODEL ); if ( !resultsData ) return {}; auto grid = eclipseCase->eclipseCaseData()->mainGrid(); if ( !grid ) return {}; // Check if BORDNUM result exists RigEclipseResultAddress bordNumAddr( RiaDefines::ResultCatType::GENERATED, RiaDefines::ResultDataType::INTEGER, RiaResultNames::bordnum() ); if ( !resultsData->hasResultEntry( bordNumAddr ) ) return {}; // BORDNUM not generated yet resultsData->ensureKnownResultLoaded( bordNumAddr ); auto bordNumValues = resultsData->cellScalarResults( bordNumAddr, 0 ); if ( bordNumValues.empty() ) return {}; // Check if BCCON result exists to get boundary condition values RigEclipseResultAddress bcconAddr( RiaDefines::ResultCatType::GENERATED, RiaDefines::ResultDataType::INTEGER, "BCCON" ); if ( !resultsData->hasResultEntry( bcconAddr ) ) return {}; resultsData->ensureKnownResultLoaded( bcconAddr ); std::vector bcconValues = resultsData->cellScalarResults( bcconAddr, 0 ); auto activeReservoirCellIdxs = eclipseCase->eclipseCaseData()->activeCellInfo( RiaDefines::PorosityModelType::MATRIX_MODEL )->activeReservoirCellIndices(); std::vector borderCellFaces; // Iterate through all active cells for ( auto activeCellIdx : activeReservoirCellIdxs ) { // Check if this cell is a border cell int borderValue = static_cast( bordNumValues[activeCellIdx.value()] ); if ( borderValue != BorderType::BORDER_CELL ) continue; // Get IJK indices for this cell size_t i, j, k; if ( !grid->ijkFromCellIndex( activeCellIdx.value(), &i, &j, &k ) ) continue; // Check all 6 faces std::vector faces = cvf::StructGridInterface::validFaceTypes(); for ( auto faceType : faces ) { // Get neighbor cell IJK size_t ni, nj, nk; cvf::StructGridInterface::neighborIJKAtCellFace( i, j, k, faceType, &ni, &nj, &nk ); // Check if neighbor is within bounds if ( ni >= grid->cellCountI() || nj >= grid->cellCountJ() || nk >= grid->cellCountK() ) continue; // Get neighbor reservoir cell index size_t neighborReservoirIdx = grid->cellIndexFromIJK( ni, nj, nk ); // Find active cell index for neighbor auto it = std::find( activeReservoirCellIdxs.begin(), activeReservoirCellIdxs.end(), ReservoirCellIndex( neighborReservoirIdx ) ); if ( it == activeReservoirCellIdxs.end() ) continue; // Neighbor not active // Check if neighbor is an interior cell int neighborBorderValue = static_cast( bordNumValues[neighborReservoirIdx] ); if ( neighborBorderValue == BorderType::INTERIOR_CELL ) { // Get boundary condition value from BCCON grid property int boundaryCondition = static_cast( bcconValues[activeCellIdx.value()] ); if ( boundaryCondition > 0 ) { // Add this face to the result borderCellFaces.push_back( { cvf::Vec3st( i, j, k ), faceType, boundaryCondition } ); } } } } return borderCellFaces; } } // namespace RigEclipseResultTools