/* Copyright 2021,2025 SINTEF Digital, Mathematics and Cybernetics. Copyright 2025 Equinor ASA This file is part of the Open Porous Media project (OPM). OPM 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. OPM 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 for more details. You should have received a copy of the GNU General Public License along with OPM. If not, see . */ #include #define BOOST_TEST_MODULE OPM_test_partitionCells #define BOOST_TEST_NO_MAIN #include #include #include #include #include #include #include #include #include #include #include #include // Helper functions namespace { Opm::Connection createConnection(int i, int j, int k, int global_index) { return Opm::Connection(i, j, k, // i,j,k global_index, // global_index 0, // complnum Opm::Connection::State::OPEN, // state Opm::Connection::Direction::Z, // direction Opm::Connection::CTFKind::DeckValue, // kind 0, // satTableId 0.0, // depth Opm::Connection::CTFProperties(), // properties 0, // sort_value false); // defaultSatTabId } Dune::cpgrid::OpmWellType createWell(const std::string& name) { using namespace Opm; return Dune::cpgrid::OpmWellType(name, name, 0, 0, 0, 0, 0.0, WellType(), Well::ProducerCMode(), Connection::Order::TRACK, UnitSystem::newMETRIC(), 0.0, 0.0, false, false, 0, Well::GasInflowEquation()); } std::vector createWellsWithConnections( const std::vector>>& well_specs) { std::vector wells; for (const auto& [well_name, cell_indices] : well_specs) { auto well_conn = std::make_shared(); for (int idx : cell_indices) { well_conn->add(createConnection(idx, 0, 0, idx)); } auto well = createWell(well_name); well.updateConnections(well_conn, true); wells.push_back(well); } return wells; } Dune::CpGrid createTestGrid(const std::array& dims, const std::array& size) { Dune::CpGrid grid; grid.createCartesian(dims, size); return grid; } template Opm::ZoltanPartitioningControl createZoltanControl(const Dune::CpGrid& grid) { Opm::ZoltanPartitioningControl zoltan_ctrl; zoltan_ctrl.domain_imbalance = 1.1; zoltan_ctrl.index = [](const auto& element) { return element.index(); }; zoltan_ctrl.local_to_global = [&grid](const int local_idx) { return grid.globalCell()[local_idx]; }; return zoltan_ctrl; } } BOOST_AUTO_TEST_CASE(FileBased) { auto [part, num_part] = Opm::partitionCellsFromFile("test10.partition", 10); BOOST_CHECK_EQUAL(num_part, 3); std::vector expected = { 0, 0, 1, 1, 2, 2, 1, 1, 0, 0 }; BOOST_CHECK_EQUAL_COLLECTIONS(expected.begin(), expected.end(), part.begin(), part.end()); } BOOST_AUTO_TEST_CASE(FileBasedWrongNumberOfCells) { auto func = []() { auto [part, num_part] = Opm::partitionCellsFromFile("test10.partition", 11); }; BOOST_CHECK_THROW(func(), std::runtime_error); } BOOST_AUTO_TEST_CASE(Simple1) { auto [part, num_part] = Opm::partitionCellsSimple(10, 3); BOOST_CHECK_EQUAL(num_part, 3); std::vector expected = { 0, 0, 0, 0, 1, 1, 1, 2, 2, 2 }; BOOST_CHECK_EQUAL_COLLECTIONS(expected.begin(), expected.end(), part.begin(), part.end()); } BOOST_AUTO_TEST_CASE(Simple2) { auto [part, num_part] = Opm::partitionCellsSimple(10, 7); BOOST_CHECK_EQUAL(num_part, 7); std::vector expected = { 0, 0, 1, 1, 2, 2, 3, 4, 5, 6 }; BOOST_CHECK_EQUAL_COLLECTIONS(expected.begin(), expected.end(), part.begin(), part.end()); } BOOST_AUTO_TEST_CASE(PartitionCellsTest) { auto grid = createTestGrid({3, 4, 1}, {3.0, 4.0, 1.0}); using Entity = typename Dune::CpGrid::LeafGridView::template Codim<0>::Entity; auto zoltan_ctrl = createZoltanControl(grid); // Test simple partitioning { auto [part, num_part] = Opm::partitionCells("simple", 3, grid.leafGridView(), std::vector{}, std::unordered_map>{}, zoltan_ctrl, 0); BOOST_CHECK_EQUAL(num_part, 3); BOOST_CHECK_EQUAL(part.size(), 12); // Check that all partition numbers are valid for (const auto& p : part) { BOOST_CHECK(p >= 0 && p < 3); } } #if HAVE_MPI && HAVE_ZOLTAN // Test Zoltan partitioning { auto [part, num_part] = Opm::partitionCells("zoltan", 3, grid.leafGridView(), std::vector{}, std::unordered_map>{}, zoltan_ctrl, 0); BOOST_CHECK_EQUAL(num_part, 3); BOOST_CHECK_EQUAL(part.size(), 12); // Check that all partition numbers are valid for (const auto& p : part) { BOOST_CHECK(p >= 0 && p < 3); } } #endif } BOOST_AUTO_TEST_CASE(PartitionCellsWithWellMergeTest) { // Create a 1D grid with 7 cells // Visual representation of the grid and wells: // Cell indices: 0 1 2 3 4 5 6 // Well layout: W1---W1 | W2---W2 | | // Expected: Cell 0 and 1 should be merged // Cell 3 and 4 should be merged // Cells 2, 5, and 6 can be in any partition auto grid = createTestGrid({7, 1, 1}, {7.0, 1.0, 1.0}); // Setup Zoltan control parameters using Entity = typename Dune::CpGrid::LeafGridView::template Codim<0>::Entity; auto zoltan_ctrl = createZoltanControl(grid); auto wells = createWellsWithConnections({ {"TESTW1", {0, 1}}, // Well 1 connects cells 0,1 {"TESTW2", {3, 4}} // Well 2 connects cells 3,4 }); // Create well connections object Dune::cpgrid::WellConnections wellConnections(wells, std::unordered_map>{}, grid); #if HAVE_MPI && HAVE_ZOLTAN // Test Zoltan partitioning with well cells auto [part, num_part] = Opm::partitionCells("zoltan", 4, grid.leafGridView(), wells, std::unordered_map>{}, zoltan_ctrl, 0); // Verify number of partitions BOOST_CHECK_EQUAL(num_part, 4); BOOST_CHECK_EQUAL(part.size(), 7); // The key tests: // 1. Cells 0 and 1 (well1) should be in the same partition BOOST_CHECK_EQUAL(part[0], part[1]); // 2. Cells 3 and 4 (well2) should be in the same partition BOOST_CHECK_EQUAL(part[3], part[4]); // 3. Cells 2, 5, and 6 can be in any partition, but well1 and well2 cells // don't need to be in the same partition #endif } BOOST_AUTO_TEST_CASE(PartitionCellsWithOverlappingWellsTest) { // Create a 1D grid with 7 cells // Visual representation of the grid and wells: // Cell indices: 0 1 2 3 4 5 6 // Well layout: W1---W1---W1 | | | // W2---W2 // Expected: Cells 1,2,3 should be in same partition (Well 1) // Cells 3,4 should be in same partition (Well 2) // Therefore cells 1,2,3,4 should all end up in same partition auto grid = createTestGrid({7, 1, 1}, {7.0, 1.0, 1.0}); using Entity = typename Dune::CpGrid::LeafGridView::template Codim<0>::Entity; auto zoltan_ctrl = createZoltanControl(grid); auto wells = createWellsWithConnections({ {"TESTW1", {1, 2, 3}}, // Well 1 connects cells 1,2,3 {"TESTW2", {3, 4}} // Well 2 connects cells 3,4 }); #if HAVE_MPI && HAVE_ZOLTAN // Test Zoltan partitioning with overlapping well cells auto [part, num_part] = Opm::partitionCells("zoltan", 4, grid.leafGridView(), wells, std::unordered_map>{}, zoltan_ctrl, 0); // Verify number of partitions BOOST_CHECK_EQUAL(num_part, 4); BOOST_CHECK_EQUAL(part.size(), 7); // The key tests: // 1. All cells connected by Well 1 should be in the same partition BOOST_CHECK_EQUAL(part[1], part[2]); BOOST_CHECK_EQUAL(part[2], part[3]); // 2. All cells connected by Well 2 should be in the same partition BOOST_CHECK_EQUAL(part[3], part[4]); // 3. Due to the overlap at cell 3, all well-connected cells should be in the same partition BOOST_CHECK_EQUAL(part[1], part[4]); // 4. Cells 0, 5, and 6 can be in any partition #endif } BOOST_AUTO_TEST_CASE(PartitionCellsWithOverlappingWells3DTest) { // Create a 3x3x3 grid with three wells: // - Well1: Vertical well through (1,1,0) -> (1,1,1) -> (1,1,2) // - Well2: Diagonal well through (0,0,0) -> (1,1,1) -> (2,2,2) [overlaps with Well1 at (1,1,1)] // - Well3: Horizontal well through (0,2,0) -> (1,2,0) -> (2,2,0) [no overlap] auto grid = createTestGrid({3, 3, 3}, {3.0, 3.0, 3.0}); using Entity = typename Dune::CpGrid::LeafGridView::template Codim<0>::Entity; auto zoltan_ctrl = createZoltanControl(grid); // Helper to convert i,j,k coordinates to global index for 3x3x3 grid auto ijkToGlobal = [](int i, int j, int k) { return i + (3 * j) + (9 * k); }; // Create wells with proper 3D connections auto wells = std::vector(); // Well 1 - vertical well { auto well_conn = std::make_shared(); well_conn->add(createConnection(1, 1, 0, ijkToGlobal(1, 1, 0))); // Global index: 4 well_conn->add(createConnection(1, 1, 1, ijkToGlobal(1, 1, 1))); // Global index: 13 well_conn->add(createConnection(1, 1, 2, ijkToGlobal(1, 1, 2))); // Global index: 22 auto well = createWell("VERTICAL"); well.updateConnections(well_conn, true); wells.push_back(well); } // Well 2 - diagonal well (overlaps with Well 1 at (1,1,1)) { auto well_conn = std::make_shared(); well_conn->add(createConnection(0, 0, 0, ijkToGlobal(0, 0, 0))); // Global index: 0 well_conn->add(createConnection(1, 1, 1, ijkToGlobal(1, 1, 1))); // Global index: 13 well_conn->add(createConnection(2, 2, 2, ijkToGlobal(2, 2, 2))); // Global index: 26 auto well = createWell("DIAGONAL"); well.updateConnections(well_conn, true); wells.push_back(well); } // Well 3 - horizontal well (no overlap) { auto well_conn = std::make_shared(); well_conn->add(createConnection(0, 2, 0, ijkToGlobal(0, 2, 0))); // Global index: 6 well_conn->add(createConnection(1, 2, 0, ijkToGlobal(1, 2, 0))); // Global index: 7 well_conn->add(createConnection(2, 2, 0, ijkToGlobal(2, 2, 0))); // Global index: 8 auto well = createWell("HORIZONTAL"); well.updateConnections(well_conn, true); wells.push_back(well); } // Create well connections object Dune::cpgrid::WellConnections wellConnections(wells, std::unordered_map>{}, grid); #if HAVE_MPI && HAVE_ZOLTAN // Test Zoltan partitioning with overlapping well cells auto [part, num_part] = Opm::partitionCells("zoltan", 10, grid.leafGridView(), wells, std::unordered_map>{}, zoltan_ctrl, 0); // Verify number of partitions BOOST_CHECK_EQUAL(num_part, 10); BOOST_CHECK_EQUAL(part.size(), 27); // 3x3x3 grid // The key tests: // 1. All cells in Well 1 (vertical) should be in the same partition BOOST_CHECK_EQUAL(part[ijkToGlobal(1,1,0)], part[ijkToGlobal(1,1,1)]); BOOST_CHECK_EQUAL(part[ijkToGlobal(1,1,1)], part[ijkToGlobal(1,1,2)]); // 2. All cells in Well 2 (diagonal) should be in the same partition BOOST_CHECK_EQUAL(part[ijkToGlobal(0,0,0)], part[ijkToGlobal(1,1,1)]); BOOST_CHECK_EQUAL(part[ijkToGlobal(1,1,1)], part[ijkToGlobal(2,2,2)]); // 3. Due to overlap at (1,1,1), all cells from Well 1 and Well 2 should be in same partition BOOST_CHECK_EQUAL(part[ijkToGlobal(1,1,0)], part[ijkToGlobal(0,0,0)]); BOOST_CHECK_EQUAL(part[ijkToGlobal(1,1,2)], part[ijkToGlobal(2,2,2)]); // 4. All cells in Well 3 (horizontal) should be in the same partition BOOST_CHECK_EQUAL(part[ijkToGlobal(0,2,0)], part[ijkToGlobal(1,2,0)]); BOOST_CHECK_EQUAL(part[ijkToGlobal(1,2,0)], part[ijkToGlobal(2,2,0)]); // 5. Well 3 can be in a different partition from Well 1 and 2 since it doesn't overlap // No test needed as it can be in any partition #endif } BOOST_AUTO_TEST_CASE(PartitionCellsComplexWellNetworkTest) { // Create a 5x5x4 grid to accommodate a complex well network auto grid = createTestGrid({5, 5, 4}, {5.0, 5.0, 4.0}); using Entity = typename Dune::CpGrid::LeafGridView::template Codim<0>::Entity; auto zoltan_ctrl = createZoltanControl(grid); // Helper to convert i,j,k coordinates to global index for 5x5x4 grid auto ijkToGlobal = [](int i, int j, int k) { return i + (5 * j) + (25 * k); }; // Create wells with complex connections auto wells = std::vector(); // Well 1 - Main vertical producer with two branches { auto well_conn = std::make_shared(); // Main wellbore - vertical well_conn->add(createConnection(2, 2, 0, ijkToGlobal(2, 2, 0))); // Bottom well_conn->add(createConnection(2, 2, 1, ijkToGlobal(2, 2, 1))); well_conn->add(createConnection(2, 2, 2, ijkToGlobal(2, 2, 2))); well_conn->add(createConnection(2, 2, 3, ijkToGlobal(2, 2, 3))); // Top // Branch 1 - horizontal in x direction well_conn->add(createConnection(3, 2, 2, ijkToGlobal(3, 2, 2))); well_conn->add(createConnection(4, 2, 2, ijkToGlobal(4, 2, 2))); // Branch 2 - horizontal in y direction well_conn->add(createConnection(2, 3, 1, ijkToGlobal(2, 3, 1))); well_conn->add(createConnection(2, 4, 1, ijkToGlobal(2, 4, 1))); auto well = createWell("PRODUCER1"); well.updateConnections(well_conn, true); wells.push_back(well); } // Well 2 - Diagonal injector crossing the producer { auto well_conn = std::make_shared(); well_conn->add(createConnection(0, 0, 0, ijkToGlobal(0, 0, 0))); well_conn->add(createConnection(1, 1, 1, ijkToGlobal(1, 1, 1))); well_conn->add(createConnection(2, 2, 2, ijkToGlobal(2, 2, 2))); // Intersects with Well 1 well_conn->add(createConnection(3, 3, 3, ijkToGlobal(3, 3, 3))); auto well = createWell("INJECTOR1"); well.updateConnections(well_conn, true); wells.push_back(well); } // Well 3 - Horizontal well that crosses both previous wells { auto well_conn = std::make_shared(); well_conn->add(createConnection(0, 2, 2, ijkToGlobal(0, 2, 2))); well_conn->add(createConnection(1, 2, 2, ijkToGlobal(1, 2, 2))); well_conn->add(createConnection(2, 2, 2, ijkToGlobal(2, 2, 2))); // Intersects with Well 1 and 2 well_conn->add(createConnection(3, 2, 2, ijkToGlobal(3, 2, 2))); // Intersects with Well 1's branch well_conn->add(createConnection(4, 2, 2, ijkToGlobal(4, 2, 2))); auto well = createWell("PRODUCER2"); well.updateConnections(well_conn, true); wells.push_back(well); } // Well 4 - L-shaped well connecting to the network { auto well_conn = std::make_shared(); well_conn->add(createConnection(2, 4, 1, ijkToGlobal(2, 4, 1))); // Connects to Well 1's branch well_conn->add(createConnection(2, 4, 2, ijkToGlobal(2, 4, 2))); well_conn->add(createConnection(2, 4, 3, ijkToGlobal(2, 4, 3))); well_conn->add(createConnection(3, 4, 3, ijkToGlobal(3, 4, 3))); well_conn->add(createConnection(4, 4, 3, ijkToGlobal(4, 4, 3))); auto well = createWell("PRODUCER3"); well.updateConnections(well_conn, true); wells.push_back(well); } // Well 5 - Isolated well (not connected to the network) { auto well_conn = std::make_shared(); well_conn->add(createConnection(0, 4, 0, ijkToGlobal(0, 4, 0))); well_conn->add(createConnection(1, 4, 0, ijkToGlobal(1, 4, 0))); auto well = createWell("ISOLATED"); well.updateConnections(well_conn, true); wells.push_back(well); } #if HAVE_MPI && HAVE_ZOLTAN // Test Zoltan partitioning with complex well network auto [part, num_part] = Opm::partitionCells("zoltan", 15, grid.leafGridView(), wells, std::unordered_map>{}, zoltan_ctrl, 0); // Verify number of partitions and grid size BOOST_CHECK_EQUAL(num_part, 15); BOOST_CHECK_EQUAL(part.size(), 100); // 5x5x4 grid // Helper to check if two cells are in the same partition auto inSamePartition = [&ppart = part](int idx1, int idx2) { return ppart[idx1] == ppart[idx2]; }; // Test 1: All cells in Well 1 (main bore and branches) should be in the same partition // Main wellbore BOOST_CHECK(inSamePartition(ijkToGlobal(2,2,0), ijkToGlobal(2,2,1))); BOOST_CHECK(inSamePartition(ijkToGlobal(2,2,1), ijkToGlobal(2,2,2))); BOOST_CHECK(inSamePartition(ijkToGlobal(2,2,2), ijkToGlobal(2,2,3))); // Branch 1 BOOST_CHECK(inSamePartition(ijkToGlobal(2,2,2), ijkToGlobal(3,2,2))); BOOST_CHECK(inSamePartition(ijkToGlobal(3,2,2), ijkToGlobal(4,2,2))); // Branch 2 BOOST_CHECK(inSamePartition(ijkToGlobal(2,2,1), ijkToGlobal(2,3,1))); BOOST_CHECK(inSamePartition(ijkToGlobal(2,3,1), ijkToGlobal(2,4,1))); // Test 2: Well 2 (diagonal) should be in same partition as Well 1 due to intersection BOOST_CHECK(inSamePartition(ijkToGlobal(0,0,0), ijkToGlobal(2,2,2))); BOOST_CHECK(inSamePartition(ijkToGlobal(3,3,3), ijkToGlobal(2,2,2))); // Test 3: Well 3 (horizontal) should be in same partition as Well 1 and 2 BOOST_CHECK(inSamePartition(ijkToGlobal(0,2,2), ijkToGlobal(2,2,2))); BOOST_CHECK(inSamePartition(ijkToGlobal(4,2,2), ijkToGlobal(2,2,2))); // Test 4: Well 4 (L-shaped) should be in same partition as Well 1 due to connection BOOST_CHECK(inSamePartition(ijkToGlobal(2,4,1), ijkToGlobal(2,2,1))); BOOST_CHECK(inSamePartition(ijkToGlobal(4,4,3), ijkToGlobal(2,2,1))); // Test 5: Well 5 (isolated) should maintain its own cells in same partition BOOST_CHECK(inSamePartition(ijkToGlobal(0,4,0), ijkToGlobal(1,4,0))); // But should be in different partition from the connected network BOOST_CHECK(part[ijkToGlobal(0,4,0)] != part[ijkToGlobal(2,2,2)]); // Test 6: Verify the connected network (Wells 1-4) forms a single partition std::set network_partition_ids; network_partition_ids.insert(part[ijkToGlobal(2,2,2)]); // Well 1 network_partition_ids.insert(part[ijkToGlobal(1,1,1)]); // Well 2 network_partition_ids.insert(part[ijkToGlobal(2,2,2)]); // Well 3 network_partition_ids.insert(part[ijkToGlobal(2,4,1)]); // Well 4 BOOST_CHECK_EQUAL(network_partition_ids.size(), 1); #endif } BOOST_AUTO_TEST_CASE(PartitionCellsWithNonReachableCellsTest) { // Create a 3x3x3 grid where: // - cell (0,0,0) is isolated (surrounded by inactive cells) // - cell (0,2,0) is isolated (surrounded by inactive cells) // - cells needed for wells are active // Grid structure (top view): // Layer k=0: Layer k=1: Layer k=2: W = well cells // [I][X][X ] [X][ ][ ] [X][ ][ ] X = inactive cells // [X][X][W2] [X][W1][W2] [X][W1][W2] I = isolated active cell // [I][X][ ] [X][ ][ ] [X][ ][ ] [ ] = active cells const std::string deckString = R"(RUNSPEC DIMENS 3 3 3 / GRID COORD 0 0 0 0 0 1 1 0 0 1 0 1 2 0 0 2 0 1 3 0 0 3 0 1 0 1 0 0 1 1 1 1 0 1 1 1 2 1 0 2 1 1 3 1 0 3 1 1 0 2 0 0 2 1 1 2 0 1 2 1 2 2 0 2 2 1 3 2 0 3 2 1 0 3 0 0 3 1 1 3 0 1 3 1 2 3 0 2 3 1 3 3 0 3 3 1 / ZCORN 36*0 36*1 36*1 36*2 36*2 36*3 / ACTNUM -- First layer (3x3) 1 0 0 -- cell (0,0,0) is isolated 0 0 1 1 0 1 -- cell (1,1,1) is isolated -- Middle layer (3x3) 0 1 1 0 1 1 0 1 1 -- Top layer (3x3) 0 1 1 0 1 1 0 1 1 / END )"; // Parse and create grid Opm::Parser parser; const auto deck = parser.parseString(deckString); Opm::EclipseGrid ecl_grid(deck); Dune::CpGrid grid; grid.processEclipseFormat(&ecl_grid, nullptr, false, false, false); // Verify grid properties const auto& grid_view = grid.leafGridView(); const std::size_t num_active_cells = grid_view.size(0); BOOST_CHECK_EQUAL(num_active_cells, 16); // 27 total cells - 11 inactive = 16 active // Helper functions using Entity = typename Dune::CpGrid::LeafGridView::template Codim<0>::Entity; auto zoltan_ctrl = createZoltanControl(grid); auto ijkToGlobal = [](int i, int j, int k) { return i + (3 * j) + (9 * k); }; auto wells = std::vector(); // Well 1 - Middle column well { auto well_conn = std::make_shared(); well_conn->add(createConnection(1, 1, 1, ijkToGlobal(1, 1, 1))); well_conn->add(createConnection(1, 1, 2, ijkToGlobal(1, 1, 2))); auto well = createWell("WELL1"); well.updateConnections(well_conn, true); wells.push_back(well); } // Well 2 - Right column well { auto well_conn = std::make_shared(); well_conn->add(createConnection(2, 1, 0, ijkToGlobal(2, 1, 0))); well_conn->add(createConnection(2, 1, 1, ijkToGlobal(2, 1, 1))); well_conn->add(createConnection(2, 1, 2, ijkToGlobal(2, 1, 2))); auto well = createWell("WELL2"); well.updateConnections(well_conn, true); wells.push_back(well); } #if HAVE_MPI && HAVE_ZOLTAN auto [part, num_part] = Opm::partitionCells("zoltan", 5, grid.leafGridView(), wells, std::unordered_map>{}, zoltan_ctrl, 0); BOOST_CHECK_EQUAL(num_part, 5); BOOST_CHECK_EQUAL(part.size(), 16); // For this test global indices != local indices, so we need to // create a map from global index to local index std::map g2l; for (const auto& element : elements(grid.leafGridView(), Dune::Partitions::interior)) { const auto globalIndex = grid.globalCell()[zoltan_ctrl.index(element)]; g2l[globalIndex] = zoltan_ctrl.index(element); } // Verify that the two isolated cells are assigned to the -1 partition BOOST_CHECK_EQUAL(part[g2l[ijkToGlobal(0,0,0)]], -1); BOOST_CHECK_EQUAL(part[g2l[ijkToGlobal(0,2,0)]], -1); // Well 1: All cells should be in the same partition BOOST_CHECK(part[g2l[ijkToGlobal(1,1,1)]] == part[g2l[ijkToGlobal(1,1,2)]]); // Well 2: All cells should be in the same partition BOOST_CHECK(part[g2l[ijkToGlobal(2,1,0)]] == part[g2l[ijkToGlobal(2,1,1)]]); BOOST_CHECK(part[g2l[ijkToGlobal(2,1,1)]] == part[g2l[ijkToGlobal(2,1,2)]]); // Wells can be in different partitions as they don't connect BOOST_CHECK(part[g2l[ijkToGlobal(1,1,1)]] != part[g2l[ijkToGlobal(2,1,2)]]); #endif } BOOST_AUTO_TEST_CASE(PartitionCellsWithNeighborConnectivityTest) { // Create a 1D grid with 7 cells and two wells that are not directly connected // but will be connected through their neighboring cells when using neighbor_levels=1 // Visual representation: // Cell indices: 0 1 2 3 4 5 6 // Well layout: W1 W2 // Connected: * * * * * // The wells will be connected because cell 2 is a neighbor of W1 and W2 auto grid = createTestGrid({7, 1, 1}, {7.0, 1.0, 1.0}); using Entity = typename Dune::CpGrid::LeafGridView::template Codim<0>::Entity; auto zoltan_ctrl = createZoltanControl(grid); // Create two wells that are not directly connected auto wells = createWellsWithConnections({ {"WELL1", {1}}, // Well 1 in cell 1 {"WELL2", {3}} // Well 2 in cell 3 (not adjacent to Well 1) }); #if HAVE_MPI && HAVE_ZOLTAN // Test Zoltan partitioning with neighbor connectivity auto [part, num_part] = Opm::partitionCells("zoltan", 3, grid.leafGridView(), wells, std::unordered_map>{}, zoltan_ctrl, 1); // Verify number of partitions BOOST_CHECK_EQUAL(num_part, 3); BOOST_CHECK_EQUAL(part.size(), 7); // 7x1x1 grid // Check that the wells are connected through their neighbors BOOST_CHECK(part[0] == part[1]); BOOST_CHECK(part[1] == part[2]); BOOST_CHECK(part[2] == part[3]); BOOST_CHECK(part[3] == part[4]); // Check that the remaining cells are in different partitions BOOST_CHECK(part[0] != part[5]); BOOST_CHECK(part[5] != part[6]); #endif } bool init_unit_test_func() { return true; } int main(int argc, char** argv) { Dune::MPIHelper::instance(argc, argv); boost::unit_test::unit_test_main(&init_unit_test_func, argc, argv); }