diff --git a/opm/grid/cpgrid/CpGridUtilities.cpp b/opm/grid/cpgrid/CpGridUtilities.cpp index db7b145fd..809cfb0b3 100644 --- a/opm/grid/cpgrid/CpGridUtilities.cpp +++ b/opm/grid/cpgrid/CpGridUtilities.cpp @@ -22,52 +22,47 @@ #include #include +#include +#include #include -namespace Opm { +namespace Opm +{ -std::vector> lgrIJK(const Dune::CpGrid& grid, const std::string& lgr_name) +std::tuple, std::unordered_map, std::vector>> +lgrIJK(const Dune::CpGrid& grid, const std::string& lgr_name) { - // Check lgr_name exists in lgr_names_ + // Check if lgr_name exists in lgr_names_ const auto& lgr_names = grid.getLgrNameToLevel(); auto it = lgr_names.find(lgr_name); if (it == lgr_names.end()) { OPM_THROW(std::runtime_error, "LGR name not found: " + lgr_name); } + const auto level = it->second; const Opm::LevelCartesianIndexMapper levelCartMapper(grid); - const auto& level = it->second; - - // Determine the logical Cartesian size of the LGR and total cells (including inactive ones) - const auto lgr_dim = grid.currentData()[level]->logicalCartesianSize(); - const int lgr_cells = lgr_dim[0] * lgr_dim[1] * lgr_dim[2]; // (including inactive ones) - // Actual size is given by grid.levelGridView(level).size(0) - - // Initialize level IJKs with inactive values - static constexpr std::array inactiveIJK = { -1, -1, -1 }; - std::vector> lgrIJK(lgr_cells); - lgrIJK.assign(lgr_cells, inactiveIJK); // Ensures all elements are set to inactive + const auto& levelView = grid.levelGridView(level); + const auto numCells = levelView.size(0); - // Iterate over active elements in the grid. Rewrite ijks at the specified refined level active cells. - // - // Note: to avoid go over all the leaf elements and replace this approach with the - // corresponding grid.levelGridView(level), a map relating level_to_lgr_idx is needed. - for (const auto& element : Dune::elements(grid.leafGridView())) { - if (element.level() != level) { - continue; - } + std::vector> lgrIJK(numCells); + std::vector cellIdxToLgrCartesianIdx(numCells); + std::unordered_map lgrCartesianIdxToCellIdx; + lgrCartesianIdxToCellIdx.reserve(numCells); + // Iterate over (active) elements in the grid and populate the structures + for (const auto& element : Dune::elements(grid.levelGridView(level))) { std::array ijk; - levelCartMapper.cartesianCoordinate(element.getLevelElem().index(), ijk, level); - // In general, element.getLevelElem().index() != element.getLevelCartesianIdx(). - // - element.getLevelElem().index() : integer between 0 and "total active cells - 1" on the level grid. - // - element.getLevelCartesianIdx() : integer between 0 and "lgr_cells - 1", representing an index of - // the underlying Cartesian grid for the level (with/without INACTIVE cells). + levelCartMapper.cartesianCoordinate(element.index(), ijk, level); - const int lgr_cart_index = element.getLevelCartesianIdx(); - lgrIJK[lgr_cart_index] = ijk; + const int cellIndex = element.index(); + const int cartesianIdx = element.getLevelCartesianIdx(); + + lgrIJK[cellIndex] = ijk; + cellIdxToLgrCartesianIdx[cellIndex] = cartesianIdx; + lgrCartesianIdxToCellIdx[cartesianIdx] = cellIndex; } - return lgrIJK; -} + return std::make_tuple(cellIdxToLgrCartesianIdx, lgrCartesianIdxToCellIdx, lgrIJK); } + +} // namespace Opm diff --git a/opm/grid/cpgrid/CpGridUtilities.hpp b/opm/grid/cpgrid/CpGridUtilities.hpp index a00d6e5d2..3ac60643e 100644 --- a/opm/grid/cpgrid/CpGridUtilities.hpp +++ b/opm/grid/cpgrid/CpGridUtilities.hpp @@ -25,17 +25,20 @@ namespace Opm { -/// @brief Retrieves the local grid refinement (LGR) IJKs of cells. +/// @brief Retrieves Cartesian indices for a specified Local Grid Refinement (LGR) level in a Dune::CpGrid. /// -/// This method returns a vector of IJK indices corresponding to underlying Cartesian grid of the local -/// grid refinement (LGR) specified by its name. If the specified name is not found, an exception is thrown. -/// For inactive cells—i.e., non-existing child cells of inactive coarse cells defined by the CARFIN keyword— -/// the corresponding IJK index is set to {-1, -1, -1}. +/// This function extracts the mapping between active cell indices and Cartesian indices for a given LGR name. +/// If the specified name is not found, an exception is thrown. /// -/// @param lgr_name The name of the LGR whose local IJK coordinates are requested. -/// @return std::vector> A list of (i, j, k)'s for each cell in the LGR. -std::vector> lgrIJK(const Dune::CpGrid& grid, const std::string& lgr_name); - -} +/// @param grid The Dune::CpGrid +/// @param lgr_name The name of the LGR whose indices are to be retrieved. +/// @return A tuple containing: +/// - A std::vector mapping cell indices to their corresponding Cartesian indices in the LGR. +/// - A std::unordered_map mapping Cartesian indices back to cell indices (handles inactive parent cells). +/// - A std::vector> storing the (i, j, k) Cartesian coordinates for active cells. +std::tuple, std::unordered_map, std::vector>> +lgrIJK(const Dune::CpGrid& grid, const std::string& lgr_name); + +} // namespace Opm #endif // OPM_CPGRIDUTILITIES_HEADER_INCLUDED diff --git a/opm/grid/cpgrid/Entity.hpp b/opm/grid/cpgrid/Entity.hpp index 258a6a3d6..1f614376f 100644 --- a/opm/grid/cpgrid/Entity.hpp +++ b/opm/grid/cpgrid/Entity.hpp @@ -607,8 +607,7 @@ template int Dune::cpgrid::Entity::getLevelCartesianIdx() const { const auto& level_data = (*(pgrid_ -> level_data_ptr_))[level()].get(); - // getLevelElem() throws when the entity does not belong to the leaf grid view. - return level_data -> global_cell_[getLevelElem().index()]; + return level_data -> global_cell_[getEquivLevelElem().index()]; } } // namespace cpgrid diff --git a/tests/cpgrid/lgrIJK_test.cpp b/tests/cpgrid/lgrIJK_test.cpp index 3440567b6..4e059b795 100644 --- a/tests/cpgrid/lgrIJK_test.cpp +++ b/tests/cpgrid/lgrIJK_test.cpp @@ -54,8 +54,7 @@ #include #include -struct Fixture -{ +struct Fixture { Fixture() { int m_argc = boost::unit_test::framework::master_test_suite().argc; @@ -63,46 +62,52 @@ struct Fixture Dune::MPIHelper::instance(m_argc, m_argv); Opm::OpmLog::setupSimpleDefaultLogging(); } - }; - BOOST_GLOBAL_FIXTURE(Fixture); -Dune::CpGrid createGridAndAddLgrs(const std::vector>& cells_per_dim_vec, - const std::vector>& startIJK_vec , - const std::vector>& endIJK_vec, - const std::vector& lgr_name_vec, - const std::string& deck_string = std::string{}, - const std::array& cell_sizes = std::array{}, - const std::array& grid_dim = std::array{}) +// Create a grid and add one test LGR with dimension 6x6x3. +Dune::CpGrid +createGridAndAddTestLgr(const std::string& deck_string) { Dune::CpGrid grid; // Create the starting grid (before adding LGRs) - if (!deck_string.empty()) { // via deck - Opm::Parser parser; - const auto deck = parser.parseString(deck_string); - Opm::EclipseState ecl_state(deck); - Opm::EclipseGrid eclipse_grid = ecl_state.getInputGrid(); + Opm::Parser parser; + const auto deck = parser.parseString(deck_string); + Opm::EclipseState ecl_state(deck); + Opm::EclipseGrid eclipse_grid = ecl_state.getInputGrid(); + + grid.processEclipseFormat(&eclipse_grid, &ecl_state, false, false, false); - grid.processEclipseFormat(&eclipse_grid, &ecl_state, false, false, false); - } - else { // via grid dimensions and cell size - grid.createCartesian(grid_dim, cell_sizes); - } - // Add LGR and update grid view + // Add LGR1 and update grid view + const std::vector> cells_per_dim_vec + = {{3, 3, 3}}; // 3x3x3 child cells in x-,y-, and z-direction per ACTIVE parent cell + const std::vector> startIJK_vec + = {{1, 1, 0}}; // starts at (1,1,0) in coarse grid - equivalent to (I1-1, J1-1, K1-1) from its CARFIN block + const std::vector> endIJK_vec + = {{3, 3, 1}}; // ends at (3,3,1) in coarse grid - equivalent to (I2, J2, K2) from its CARFIN block + const std::vector lgr_name_vec = {"LGR1"}; + grid.addLgrsUpdateLeafView(cells_per_dim_vec, startIJK_vec, endIJK_vec, lgr_name_vec); return grid; } -/* Tests lgrIJK() for a grid containing inactive cells within the LGR block. - -The test passes if initLgr() is not invoked in opm-common/opm/input/eclipse/EclipseState/EclipseState.cpp. -Currently, inactive cells in LGRs are not supported in "opm-common." -*/ +void +checkExpectedSize(const int& expected_elements, + const std::size_t& numLgrCells, + const std::size_t& cellIdxToLgrCartesianIdxSize, + const std::size_t& lgrCartesianIdxToCellIdxSize, + const std::size_t& lgr1IJKSize) +{ + BOOST_CHECK_EQUAL(numLgrCells, expected_elements); + BOOST_CHECK_EQUAL(cellIdxToLgrCartesianIdxSize, expected_elements); + BOOST_CHECK_EQUAL(lgrCartesianIdxToCellIdxSize, expected_elements); + BOOST_CHECK_EQUAL(lgr1IJKSize, expected_elements); +} + -BOOST_AUTO_TEST_CASE(thrwoWhenQueryingLgrIJKofInactiveLgrCell) +BOOST_AUTO_TEST_CASE(fullActiveParentCellsBlock) { const std::string deck_string = R"( RUNSPEC @@ -124,7 +129,7 @@ TOPS ACTNUM 1 1 1 1 1 1 - 1 1 0 + 1 1 1 / PORO 9*0.15 / @@ -146,446 +151,305 @@ REGIONS SOLUTION SCHEDULE )"; - - // LGR1: 3x3x1 child cells in x-,y-, and z-direction per ACTIVE parent cell - const std::vector> cells_per_dim_vec = { {3, 3, 3} }; - // LGR1 starts at (1,1,0) in coarse grid - equivalent to (I1-1, J1-1, K1-1) from its CARFIN block - const std::vector> startIJK_vec = { {1, 1, 0} }; - // LGR1 ends at (3,3,1) in coarse grid - equivalent to (I2, J2, K2) from its CARFIN block - const std::vector> endIJK_vec = { {3, 3, 1} }; - const std::vector lgr_name_vec = {"LGR1"}; - const auto grid = createGridAndAddLgrs(cells_per_dim_vec, startIJK_vec, endIJK_vec, lgr_name_vec, deck_string); - - // Get LGR levels - const auto& lgr_name_to_level = grid.getLgrNameToLevel(); // map std::string to int level: {"GLOBAL", 0}, {"LGR1", 1}, ... - const int lgr1_level = lgr_name_to_level.at("LGR1"); + const auto grid = createGridAndAddTestLgr(deck_string); - // Get vector of local ijk for each LGR - const std::vector> ijk_lgr1 = Opm::lgrIJK(grid, "LGR1"); + const auto [cellIdxToLgrCartesianIdx, lgrCartesianIdxToCellIdx, lgr1IJK] = Opm::lgrIJK(grid, "LGR1"); - // Note: k = 0, indicating a single-layer grid with dimensions 3x3x1. - // ACTNUM represents the active cell indicator for the parent grid of the LGR (Local Grid Refinement). - // The grid structure is as follows, where '1' denotes an active cell and '0' denotes an inactive cell: - // - // 1 1 1 - // 1 1 1 - // 1 1 0 - // - // The corresponding LGR block parent cell representation appears as: - // 1 1 - // 1 0 + // Get LGR level + const int lgr1_level = grid.getLgrNameToLevel().at("LGR1"); + const int numLgrCells = grid.levelGridView(lgr1_level).size(0); // Verify the size matches expected elements - BOOST_TEST(grid.levelGridView(lgr1_level).size(0) == 81); // 3 ACTIVE parent cellS into 3x3x3 children each -> 81 - BOOST_TEST(ijk_lgr1.size() == 108); // Block size - inlcuding inactive cells (6x6x3) - + const int expected_elements = 108; // 4 parent cells into 3x3x3 children each -> 108 + checkExpectedSize(expected_elements, + numLgrCells, + cellIdxToLgrCartesianIdx.size(), + lgrCartesianIdxToCellIdx.size(), + lgr1IJK.size()); + + // Validate all ijk's are non-negative + for (const auto& ijk : lgr1IJK) { + BOOST_TEST(ijk[0] >= 0); + BOOST_TEST(ijk[1] >= 0); + BOOST_TEST(ijk[2] >= 0); + } + + // Invalid LGR should throw an exception + BOOST_CHECK_THROW(Opm::lgrIJK(grid, "LGR2DOESNOTEXIST"), std::runtime_error); + // LGR1 dimension 6x6x3 // Visual representation per k-layer: - // - // level Cartesian cell indices internal ordering, i.e. element.index() in levelGridView(level); // - // k = 2 | 102 103 104 | | 78 79 80 | - // | 96 97 98 | INACTIVE | 75 76 77 | INACTIVE - // | 90 91 92 | | 72 73 74 | + // level Cartesian cell indices internal ordering, i.e. element.index() in + // levelGridView(level); + // + // k = 2 | 102 103 104 | 105 106 107 | 78 79 80 | 105 106 107 + // | 96 97 98 | 99 100 101 | 75 76 77 | 102 103 104 + // | 90 91 92 | 93 94 95 | 72 73 74 | 99 100 101 // ---------------------------- ---------------------------- // | 84 85 86 | 87 88 89 | 24 25 26 | 51 52 53 // | 78 79 80 | 81 82 83 | 21 22 23 | 48 49 50 // | 72 73 74 | 75 76 77 | 18 19 20 | 45 46 47 //------------------------------------ ---------------------------- - // k = 1 | 66 67 68 | | 69 70 71 | - // | 60 61 62 | INACTIVE | 66 67 68 | INACTIVE - // | 54 55 56 | | 63 64 65 | + // k = 1 | 66 67 68 | 69 70 71 | 69 70 71 | 96 97 98 + // | 60 61 62 | 63 64 65 | 66 67 68 | 93 94 95 + // | 54 55 56 | 57 58 59 | 63 64 65 | 90 91 92 // ---------------------------- ---------------------------- // | 48 49 50 | 51 52 53 | 15 16 17 | 42 43 44 // | 42 43 44 | 45 46 47 | 12 13 14 | 39 40 41 // | 36 37 38 | 39 40 41 | 9 10 11 | 36 37 38 //------------------------------------ ---------------------------- - // k = 0 | 30 31 32 | | 60 61 62 | - // | 24 25 26 | INACTIVE | 57 58 59 | INACTIVE - // | 18 19 20 | | 54 55 56 | + // k = 0 | 30 31 32 | 33 34 35 | 60 61 62 | 87 88 89 + // | 24 25 26 | 27 28 29 | 57 58 59 | 84 85 86 + // | 18 19 20 | 21 22 23 | 54 55 56 | 81 82 83 // ---------------------------- ---------------------------- // | 12 13 14 | 15 16 17 | 6 7 8 | 33 34 35 // | 6 7 8 | 9 10 11 | 3 4 5 | 30 31 32 - // | 0 1 2 | 3 4 5 | 0 1 2 | 27 28 29 - - - // Verify that cell with local (level Cartesian) index 97 in LGR1 has local ijk = {1,4,2} - BOOST_TEST(ijk_lgr1[97][0] == 1); - BOOST_TEST(ijk_lgr1[97][1] == 4); - BOOST_TEST(ijk_lgr1[97][2] == 2); + // | 0 1 2 | 3 4 5 | 0 1 2 | 27 28 92 - // Verify that cell with local (level Cartesian) index 88 in LGR1 has local ijk = {4,2,2} - BOOST_TEST(ijk_lgr1[88][0] == 4); - BOOST_TEST(ijk_lgr1[88][1] == 2); - BOOST_TEST(ijk_lgr1[88][2] == 2); + // Verify that cell with level (local) Cartesian index 25 in LGR1 corresponds to cell index 58 + // and has local ijk = {1,4,0} + BOOST_TEST_REQUIRE(cellIdxToLgrCartesianIdx[58] == 25); + BOOST_TEST_REQUIRE(lgrCartesianIdxToCellIdx.count(25)); + BOOST_TEST(lgrCartesianIdxToCellIdx.at(25) == 58); - // Accessing inactive (non-existing) cells - for (int i = 0; i < 3; ++i) { - BOOST_TEST(ijk_lgr1[107][i] == -1); - BOOST_TEST(ijk_lgr1[70][i] == -1); - } - // Potential improvement: If lgrIJK() returns a map instead of vector, then - // ijk_lgr1.at( non-exisiting-inactive-cell-cartesian-index ) throws, which - // might be better than accesing an invalid value {-1,-1,-1}. -} + BOOST_TEST(lgr1IJK[58][0] == 1); + BOOST_TEST(lgr1IJK[58][1] == 4); + BOOST_TEST(lgr1IJK[58][2] == 0); -BOOST_AUTO_TEST_CASE(fullInactiveParentCellsBlockTurnsIntoLgrWithZeroCellsButFullCartesianSize) -{ + // Verify that cell with level (local) Cartesian index 64 in LGR1 corresponds to cell index 94 + // and has local ijk = {4,4,1} + BOOST_TEST_REQUIRE(cellIdxToLgrCartesianIdx[94] == 64); + BOOST_TEST_REQUIRE(lgrCartesianIdxToCellIdx.count(64)); + BOOST_TEST(lgrCartesianIdxToCellIdx.at(64) == 94); - // To create a grid with one LGR('LGR1') with parent cells block - // dimensions 1x1x2, parent cell indices = {0, 1}. Both cells are - // INACTIVE - const std::string deck_string = - R"( RUNSPEC - DIMENS - 1 1 5 / - GRID - COORD - 0 0 0 - 0 0 1 - 1 0 0 - 1 0 1 - 0 1 0 - 0 1 1 - 1 1 0 - 1 1 1 - / - ZCORN - 4*0 - 8*1 - 8*2 - 8*3 - 8*4 - 4*5 - / - ACTNUM - 0 - 0 - 0 - 1 - 1 - / - CARFIN - -- NAME I1-I2 J1-J2 K1-K2 NX NY NZ - 'LGR1' 1 1 1 1 1 2 2 2 4/ - ENDFIN - PORO - 5*0.15 - /)"; - - const std::vector> cells_per_dim_vec = { {2, 2, 2} }; // LGR1: 2x2x2 child cells in x-,y-, and z-direction per parent cell - const std::vector> startIJK_vec = { {0, 0, 0} }; // LGR1 starts at (0,0,0) in coarse grid - equivalent to (I1-1, J1-1, K1-1) from its CARFIN block - const std::vector> endIJK_vec = { {1, 1, 2} }; // LGR1 ends at (1,1,2) in coarse grid - equivalent to (I2, J2, K2) from its CARFIN block - const std::vector lgr_name_vec = {"LGR1"}; + BOOST_TEST(lgr1IJK[94][0] == 4); + BOOST_TEST(lgr1IJK[94][1] == 4); + BOOST_TEST(lgr1IJK[94][2] == 1); - const auto grid = createGridAndAddLgrs(cells_per_dim_vec, startIJK_vec, endIJK_vec, lgr_name_vec, deck_string); + // Verify that cell with level (local) Cartesian index 98 in LGR1 corresponds to cell index 77 + // and has local ijk = {2,4,2} + BOOST_TEST_REQUIRE(cellIdxToLgrCartesianIdx[77] == 98); + BOOST_TEST_REQUIRE(lgrCartesianIdxToCellIdx.count(98)); + BOOST_TEST(lgrCartesianIdxToCellIdx.at(98) == 77); - // Get vector of local ijk for each LGR - std::vector> ijk_lgr1 = Opm::lgrIJK(grid, "LGR1"); - - // Get LGR levels - const auto& lgr_name_to_level = grid.getLgrNameToLevel(); // map std::string to int level: {"GLOBAL", 0}, {"LGR1", 1}, ... - const int lgr1_level = lgr_name_to_level.at("LGR1"); + BOOST_TEST(lgr1IJK[77][0] == 2); + BOOST_TEST(lgr1IJK[77][1] == 4); + BOOST_TEST(lgr1IJK[77][2] == 2); +} - // Verify the size matches expected elements - const int lgr_cartesian_size = 16; // 2x2x4 INACTIVE and ACTIVE cells. +/* Tests lgrIJK() for a grid containing inactive cells within the LGR block. - BOOST_CHECK_EQUAL(grid.levelGridView(lgr1_level).size(0), 0); // 2 INACTIVE parent cells into 2x2x2 children each -> 0 - BOOST_CHECK_EQUAL(ijk_lgr1.size(), lgr_cartesian_size); -} + The two tests below pass if initLgr() is not invoked in opm-common/opm/input/eclipse/EclipseState/EclipseState.cpp. + Currently, inactive cells in LGRs are not supported in "opm-common." +*/ -BOOST_AUTO_TEST_CASE(fullActiveParentCellsBlockTurnsIntoLgrWithFullCartesianSizeAndSameTotalCells) +BOOST_AUTO_TEST_CASE(lgrWithActiveAndInactiveCells, *boost::unit_test::disabled()) { - // LGR1 parent cell indices = {0 ACTIVE, 1 ACTIVE}, LGR2 parent cell indices = {3 AVTIVE, 4 ACTIVE}. - const std::string deck_string = - R"( RUNSPEC - DIMENS - 1 1 5 / - GRID - COORD - 0 0 0 - 0 0 1 - 1 0 0 - 1 0 1 - 0 1 0 - 0 1 1 - 1 1 0 + const std::string deck_string = R"( +RUNSPEC +DIMENS + 3 3 1 / +GRID +CARFIN +-- NAME I1-I2 J1-J2 K1-K2 NX NY NZ +'LGR1' 2 3 2 3 1 1 6 6 3/ +ENDFIN +DX + 9*1000 / +DY + 9*1000 / +DZ + 9*20 / +TOPS + 9*8325 / + ACTNUM 1 1 1 + 1 1 1 + 1 1 0 / - ZCORN - 4*0 - 8*1 - 8*2 - 8*3 - 8*4 - 4*5 - / - ACTNUM - 1 - 1 - 0 - 1 - 1 - / - CARFIN - -- NAME I1-I2 J1-J2 K1-K2 NX NY NZ - 'LGR1' 1 1 1 1 1 2 2 2 4/ - ENDFIN - CARFIN - -- NAME I1-I2 J1-J2 K1-K2 NX NY NZ - 'LGR2' 1 1 1 1 4 5 3 3 6/ - ENDFIN - PORO - 5*0.15 - /)"; - - const std::vector> cells_per_dim_vec = { - {2, 2, 2}, // LGR1: 2x2x2 child cells in x-,y-, and z-direction per parent cell - {3, 3, 3} // LGR2: 3x3x3 child cells in x-,y-, and z-direction per parent cell - }; - const std::vector> startIJK_vec = { - {0, 0, 0}, // LGR1 starts at (0,0,0) in coarse grid - equivalent to (I1-1, J1-1, K1-1) from its CARFIN block - {0, 0, 3} // LGR2 starts at (0,0,3) in coarse grid - equivalent to (I1-1, J1-1, K1-1) from its CARFIN block - }; - const std::vector> endIJK_vec = { - {1, 1, 2}, // LGR1 ends at (1,1,2) in coarse grid - equivalent to (I2, J2, K2) from its CARFIN block - {1, 1, 5} // LGR2 ends at (1,1,5) in coarse grid - equivalent to (I2, J2, K2) from its CARFIN block - }; - const std::vector lgr_name_vec = {"LGR1", "LGR2"}; - - const auto grid = createGridAndAddLgrs(cells_per_dim_vec, startIJK_vec, endIJK_vec, lgr_name_vec, deck_string); - - // Get LGR levels - const auto& lgr_name_to_level = grid.getLgrNameToLevel(); // map std::string to int level: {"GLOBAL", 0}, {"LGR1", 1}, ... - const int lgr1_level = lgr_name_to_level.at("LGR1"); - const int lgr2_level = lgr_name_to_level.at("LGR2"); - - // Get vector of local ijk for each LGR - std::vector> ijk_lgr1 = Opm::lgrIJK(grid, "LGR1"); - std::vector> ijk_lgr2 = Opm::lgrIJK(grid, "LGR2"); - - // Ensure it's not empty - BOOST_TEST(!ijk_lgr1.empty()); // 2 active parent cell refined into 2x2x2 refined cells - BOOST_TEST(!ijk_lgr2.empty()); // 2 active parent cell refined into 3x3x3 refined cells - - // Verify the size matches expected elements - BOOST_TEST(ijk_lgr1.size() == grid.levelGridView(lgr1_level).size(0)); // 2 parent cells into 2x2x2 children each -> 16 - BOOST_TEST(ijk_lgr1.size() == 16); - BOOST_TEST(ijk_lgr2.size() == grid.levelGridView(lgr2_level).size(0)); // 2 parent cells into 3x3x3 children each -> 54 - BOOST_TEST(ijk_lgr2.size() == 54); - - // Validate all ijk's are non-negative - for (const auto& ijk_set : {ijk_lgr1, ijk_lgr2}) { - for (const auto& ijk : ijk_set) { - BOOST_TEST(ijk[0] >= 0); - BOOST_TEST(ijk[1] >= 0); - BOOST_TEST(ijk[2] >= 0); - } - } +PORO + 9*0.15 / +PERMX + 9*1 / +COPY + PERMX PERMZ / + PERMX PERMY / +/ +EDIT +OIL +GAS +TITLE +The title +START +16 JUN 1988 / +PROPS +REGIONS +SOLUTION +SCHEDULE +)"; - // Invalid LGR should throw an exception - BOOST_CHECK_THROW(Opm::lgrIJK(grid, "LGR3DOESNOTEXIST"), std::runtime_error); + const auto grid = createGridAndAddTestLgr(deck_string); - // LGR1 Dimension: 2x2x4 (Width x Height x Depth) - // Visual representation of level Cartesian cell indices per k-layer: + // Note: k = 0, indicating a single-layer grid with dimensions 3x3x1. + // ACTNUM represents the active cell indicator for the parent grid of the LGR (Local Grid Refinement). + // The grid structure is as follows, where '1' denotes an active cell and '0' denotes an inactive cell: // - // k = 3 | 14 15 - // | 12 13 - // ----------------- - // k = 2 | 10 11 - // | 8 9 - // ----------------- - // k = 1 | 6 7 - // | 4 5 - // ----------------- - // k = 0 | 2 3 - // | 0 1 - - // Verify that cell with local (level Cartesian) index 11 in LGR1 has local ijk = {1,1,2} - BOOST_TEST(ijk_lgr1[11][0] == 1); - BOOST_TEST(ijk_lgr1[11][1] == 1); - BOOST_TEST(ijk_lgr1[11][2] == 2); - - // Verify that cell with local (level Cartesian) index 4 in LGR1 has local ijk = {0,0,1} - BOOST_TEST(ijk_lgr1[4][0] == 0); - BOOST_TEST(ijk_lgr1[4][1] == 0); - BOOST_TEST(ijk_lgr1[4][2] == 1); - - - // LGR2 dimension 3x3x6 - // Visual representation of level Cartesian cell indices per k-layer: + // 1 1 1 + // 1 1 1 + // 1 1 0 // - // k = 5 | 51 52 53 - // | 48 49 50 - // | 45 46 47 - //---------------------- - // k = 4 | 42 43 44 - // | 39 40 41 - // | 36 37 38 - //---------------------- - // k = 3 | 33 34 35 - // | 30 31 32 - // | 27 28 29 - //---------------------- - // k = 2 | 24 25 26 - // | 21 22 23 - // | 18 19 20 - //---------------------- - // k = 1 | 15 16 17 - // | 12 13 14 - // | 9 10 11 - //---------------------- - // k = 0 | 6 7 8 - // | 3 4 5 - // | 0 1 2 - - // Verify that cell with local (level Cartesian) index 48 in LGR2 has local ijk = {0,1,5} - BOOST_TEST(ijk_lgr2[48][0] == 0); - BOOST_TEST(ijk_lgr2[48][1] == 1); - BOOST_TEST(ijk_lgr2[48][2] == 5); - - // Verify that cell with local (level Cartesian) index 31 in LGR2 has local ijk = {1,1,3} - BOOST_TEST(ijk_lgr2[31][0] == 1); - BOOST_TEST(ijk_lgr2[31][1] == 1); - BOOST_TEST(ijk_lgr2[31][2] == 3); - - // Verify that cell with local (level Cartesian) index 25 in LGR1 has local ijk = {1,2,2} - BOOST_TEST(ijk_lgr2[25][0] == 1); - BOOST_TEST(ijk_lgr2[25][1] == 2); - BOOST_TEST(ijk_lgr2[25][2] == 2); -} + // The corresponding LGR block parent cell representation appears as: + // 1 1 + // 1 0 -BOOST_AUTO_TEST_CASE(internal_order_differs_from_underlying_cartesian_level_grids) -{ - const std::array cell_sizes = {1.0, 1.0, 1.0}; - const std::array grid_dim = {4,3,3}; - - const std::vector> cells_per_dim_vec = { - {3, 3, 3}, // LGR1: 3x3x3 child cells in x-,y-, and z-direction per parent cell - {3, 3, 4} // LGR2: 3x3x4 child cells in x-,y-, and z-direction per parent cell - }; - const std::vector> startIJK_vec = { - {1, 1, 1}, // LGR1 starts at (1,1,1) in coarse grid - {2, 0, 2} // LGR2 starts at (2,0,2) in coarse grid - }; - const std::vector> endIJK_vec = { - {3, 3, 2}, // LGR1 ends at (3,3,2) in coarse grid - {4, 2, 3} // LGR2 ends at (4,2,3) in coarse grid - }; - const std::vector lgr_name_vec = {"LGR1", "LGR2"}; - - const auto grid = createGridAndAddLgrs(cells_per_dim_vec, startIJK_vec, endIJK_vec, lgr_name_vec, "", cell_sizes, grid_dim); - - // Get LGR levels - const auto& lgr_name_to_level = grid.getLgrNameToLevel(); // map std::string to int level: {"GLOBAL", 0}, {"LGR1", 1}, ... - const int lgr1_level = lgr_name_to_level.at("LGR1"); - const int lgr2_level = lgr_name_to_level.at("LGR2"); - - // Get vector of local ijk for each LGR - std::vector> ijk_lgr1 = Opm::lgrIJK(grid, "LGR1"); - std::vector> ijk_lgr2 = Opm::lgrIJK(grid, "LGR2"); + // Get LGR level + const int lgr1_level = grid.getLgrNameToLevel().at("LGR1"); + // Total active refined cells on the level grid + const int numLgrCells = grid.levelGridView(lgr1_level).size(0); + + const auto [cellIdxToLgrCartesianIdx, lgrCartesianIdxToCellIdx, lgr1IJK] = Opm::lgrIJK(grid, "LGR1"); // Verify the size matches expected elements - BOOST_TEST(ijk_lgr1.size() == grid.levelGridView(lgr1_level).size(0)); // 4 parent cells into 3x3x3 children each -> 108 - BOOST_TEST(ijk_lgr1.size() == 108); - BOOST_TEST(ijk_lgr2.size() == grid.levelGridView(lgr2_level).size(0)); // 4 parent cells into 3x3x4 children each -> 144 - BOOST_TEST(ijk_lgr2.size() == 144); + const int expected_elements = 81; // 3 ACTIVE parent cellS into 3x3x3 children each -> 81 + checkExpectedSize(expected_elements, + numLgrCells, + cellIdxToLgrCartesianIdx.size(), + lgrCartesianIdxToCellIdx.size(), + lgr1IJK.size()); // LGR1 dimension 6x6x3 // Visual representation per k-layer: - // - // level Cartesian cell indices internal ordering, i.e. element.index() in levelGridView(level); // - // k = 2 | 102 103 104 | 105 106 107 | 78 79 80 | 105 106 107 - // | 96 97 98 | 99 100 101 | 75 76 77 | 102 103 104 - // | 90 91 92 | 93 94 95 | 72 73 74 | 99 100 101 + // level Cartesian cell indices internal ordering, i.e. element.index() in + // levelGridView(level); + // + // k = 2 | 102 103 104 | | 78 79 80 | + // | 96 97 98 | INACTIVE | 75 76 77 | INACTIVE + // | 90 91 92 | | 72 73 74 | // ---------------------------- ---------------------------- // | 84 85 86 | 87 88 89 | 24 25 26 | 51 52 53 // | 78 79 80 | 81 82 83 | 21 22 23 | 48 49 50 // | 72 73 74 | 75 76 77 | 18 19 20 | 45 46 47 //------------------------------------ ---------------------------- - // k = 1 | 66 67 68 | 69 70 71 | 69 70 71 | 96 97 98 - // | 60 61 62 | 63 64 65 | 66 67 68 | 93 94 95 - // | 54 55 56 | 57 58 59 | 63 64 65 | 90 91 92 + // k = 1 | 66 67 68 | | 69 70 71 | + // | 60 61 62 | INACTIVE | 66 67 68 | INACTIVE + // | 54 55 56 | | 63 64 65 | // ---------------------------- ---------------------------- // | 48 49 50 | 51 52 53 | 15 16 17 | 42 43 44 // | 42 43 44 | 45 46 47 | 12 13 14 | 39 40 41 // | 36 37 38 | 39 40 41 | 9 10 11 | 36 37 38 //------------------------------------ ---------------------------- - // k = 0 | 30 31 32 | 33 34 35 | 60 61 62 | 87 88 89 - // | 24 25 26 | 27 28 29 | 57 58 59 | 84 85 86 - // | 18 19 20 | 21 22 23 | 54 55 56 | 81 82 83 + // k = 0 | 30 31 32 | | 60 61 62 | + // | 24 25 26 | INACTIVE | 57 58 59 | INACTIVE + // | 18 19 20 | | 54 55 56 | // ---------------------------- ---------------------------- // | 12 13 14 | 15 16 17 | 6 7 8 | 33 34 35 // | 6 7 8 | 9 10 11 | 3 4 5 | 30 31 32 // | 0 1 2 | 3 4 5 | 0 1 2 | 27 28 29 - // Verify that cell with local (level Cartesian) index 25 in LGR1 has local ijk = {1,4,0} - BOOST_TEST(ijk_lgr1[25][0] == 1); - BOOST_TEST(ijk_lgr1[25][1] == 4); - BOOST_TEST(ijk_lgr1[25][2] == 0); + // Verify that cell with level (local) Cartesian index 97 in LGR1 corresponds to cell index 76 + // and has local ijk = {1,4,2} + BOOST_TEST_REQUIRE(cellIdxToLgrCartesianIdx[76] == 97); + BOOST_TEST_REQUIRE(lgrCartesianIdxToCellIdx.count(97)); + BOOST_TEST(lgrCartesianIdxToCellIdx.at(97) == 76); + + BOOST_TEST(lgr1IJK[76][0] == 1); + BOOST_TEST(lgr1IJK[76][1] == 4); + BOOST_TEST(lgr1IJK[76][2] == 2); + - // Verify that cell with local (level Cartesian) index 64 in LGR1 has local ijk = {4,4,1} - BOOST_TEST(ijk_lgr1[64][0] == 4); - BOOST_TEST(ijk_lgr1[64][1] == 4); - BOOST_TEST(ijk_lgr1[64][2] == 1); + // Verify that cell with level (local) Cartesian index 88 in LGR1 corresponds to cell index 52 + // has local ijk = {4,2,2} + BOOST_TEST_REQUIRE(cellIdxToLgrCartesianIdx[52] == 88); + BOOST_TEST_REQUIRE(lgrCartesianIdxToCellIdx.count(97)); + BOOST_TEST_REQUIRE(lgrCartesianIdxToCellIdx.at(88) == 52); - // Verify that cell with local (level Cartesian) index 98 in LGR1 has local ijk = {2,4,2} - BOOST_TEST(ijk_lgr1[98][0] == 2); - BOOST_TEST(ijk_lgr1[98][1] == 4); - BOOST_TEST(ijk_lgr1[98][2] == 2); + BOOST_TEST(lgr1IJK[52][0] == 4); + BOOST_TEST(lgr1IJK[52][1] == 2); + BOOST_TEST(lgr1IJK[52][2] == 2); + // Accessing inactive (non-existing) cells + BOOST_CHECK_THROW(lgrCartesianIdxToCellIdx.at(70), std::out_of_range); + BOOST_CHECK_THROW(lgrCartesianIdxToCellIdx.at(170), std::out_of_range); +} - // LGR2 dimension 6x6x4 - // Visual representation of level Cartesian cell indices per k-layer: +BOOST_AUTO_TEST_CASE(fullInactiveParentCellsBlock, *boost::unit_test::disabled()) +{ + const std::string deck_string = R"( +RUNSPEC +DIMENS + 3 3 1 / +GRID +CARFIN +-- NAME I1-I2 J1-J2 K1-K2 NX NY NZ +'LGR1' 2 3 2 3 1 1 6 6 3/ +ENDFIN +DX + 9*1000 / +DY + 9*1000 / +DZ + 9*20 / +TOPS + 9*8325 / + ACTNUM + 1 1 1 + 1 0 0 + 1 0 0 + / +PORO + 9*0.15 / +PERMX + 9*1 / +COPY + PERMX PERMZ / + PERMX PERMY / +/ +EDIT +OIL +GAS +TITLE +The title +START +16 JUN 1988 / +PROPS +REGIONS +SOLUTION +SCHEDULE +)"; + const auto grid = createGridAndAddTestLgr(deck_string); + + // Note: k = 0, indicating a single-layer grid with dimensions 3x3x1. + // ACTNUM represents the active cell indicator for the parent grid of the LGR (Local Grid Refinement). + // The grid structure is as follows, where '1' denotes an active cell and '0' denotes an inactive cell: // - // k = 3 | 138 139 140 | 141 142 143 - // | 132 133 134 | 135 136 137 - // | 126 127 128 | 129 130 131 - // ---------------------------- - // | 120 121 122 | 123 124 125 - // | 114 115 116 | 117 118 119 - // | 108 109 110 | 111 112 113 - //------------------------------------ - // k = 2 | 102 103 104 | 105 106 107 - // | 96 97 98 | 99 100 101 - // | 90 91 92 | 93 94 95 - // ---------------------------- - // | 84 85 86 | 87 88 89 - // | 78 79 80 | 81 82 83 - // | 72 73 74 | 75 76 77 - //------------------------------------ - // k = 1 | 66 67 68 | 69 70 71 - // | 60 61 62 | 63 64 65 - // | 54 55 56 | 57 58 59 - // ---------------------------- - // | 48 49 50 | 51 52 53 - // | 42 43 44 | 45 46 47 - // | 36 37 38 | 39 40 41 - //------------------------------------ - // k = 0 | 30 31 32 | 33 34 35 - // | 24 25 26 | 27 28 29 - // | 18 19 20 | 21 22 23 - // ---------------------------- - // | 12 13 14 | 15 16 17 - // | 6 7 8 | 9 10 11 - // | 0 1 2 | 3 4 5 - - // Verify that cell with local (level Cartesian) index 139 in LGR2 has local ijk = {1,5,3} - BOOST_TEST(ijk_lgr2[139][0] == 1); - BOOST_TEST(ijk_lgr2[139][1] == 5); - BOOST_TEST(ijk_lgr2[139][2] == 3); - - // Verify that cell with local (level Cartesian) index 119 in LGR2 has local ijk = {5,1,3} - BOOST_TEST(ijk_lgr2[119][0] == 5); - BOOST_TEST(ijk_lgr2[119][1] == 1); - BOOST_TEST(ijk_lgr2[119][2] == 3); - - // Verify that cell with local (level Cartesian) index 121 in LGR1 has local ijk = {1,2,3} - BOOST_TEST(ijk_lgr2[121][0] == 1); - BOOST_TEST(ijk_lgr2[121][1] == 2); - BOOST_TEST(ijk_lgr2[121][2] == 3); + // 1 1 1 + // 1 0 0 + // 1 0 0 + // + // The corresponding LGR block parent cell representation appears as: + // 0 0 + // 0 0 + + // Get LGR level + const int lgr1_level = grid.getLgrNameToLevel().at("LGR1"); + // Total active refined cells on the level grid + const int numLgrCells = grid.levelGridView(lgr1_level).size(0); // here, 0 + + const auto [cellIdxToLgrCartesianIdx, lgrCartesianIdxToCellIdx, lgr1IJK] = Opm::lgrIJK(grid, "LGR1"); + + // Verify the size matches expected elements + const int expected_elements = 0; + checkExpectedSize(expected_elements, + numLgrCells, + cellIdxToLgrCartesianIdx.size(), + lgrCartesianIdxToCellIdx.size(), + lgr1IJK.size()); + + // Accessing inactive (non-existing) cell + BOOST_CHECK_THROW(lgrCartesianIdxToCellIdx.at(0), std::out_of_range); }