@@ -109,42 +109,24 @@ class ZoltanPartitioner
109
109
// /
110
110
// / \param[in] zoltan_ctrl Control parameters for on-rank subdomain
111
111
// / partitioning.
112
+ // /
113
+ // / \param[in] num_neighbor_levels Number of neighbor levels to include when connecting well cells.
114
+ // / Default is 0, which means only direct well connections are considered.
112
115
template <class GridView , class Element >
113
116
void buildLocalGraph (const GridView& grid_view,
114
117
const std::vector<Opm::Well>& wells,
115
118
const std::unordered_map<std::string, std::set<int >>& possibleFutureConnections,
116
119
const Opm::ZoltanPartitioningControl<Element>& zoltan_ctrl,
117
120
const int num_neighbor_levels);
118
121
119
- // / Find all neighbors of a given cell.
120
- // /
121
- // / \tparam GridView DUNE grid view type
122
- // /
123
- // / \param[in] grid_view Current rank's reachable cells.
124
- // / \param[in] cell_index Index of the cell to find neighbors for.
125
- // / \param[in] zoltan_ctrl Control parameters for on-rank subdomain partitioning.
126
- // /
127
- // / \return Set of neighbor cell indices.
128
- template <class GridView , class Element >
129
- std::set<int > findNeighbors (const GridView& grid_view,
130
- const int cell_index,
131
- const Opm::ZoltanPartitioningControl<Element>& zoltan_ctrl) const ;
132
-
133
122
// / Connect neighbors of cells up to a specified level.
134
123
// /
135
- // / \tparam GridView DUNE grid view type
124
+ // / \param[in,out] cells Initial cells to find neighbors of. Updated to include all neighbors
125
+ // / up to specified level.
136
126
// /
137
- // / \param[in] grid_view Current rank's reachable cells.
138
- // / \param[in] cells Initial set of cells.
139
127
// / \param[in] num_levels Number of neighbor levels to include.
140
- // / \param[in] zoltan_ctrl Control parameters for on-rank subdomain partitioning.
141
- // /
142
- // / \return Set of all connected cells including neighbors up to specified level.
143
- template <class GridView , class Element >
144
- std::set<int > connectNeighbors (const GridView& grid_view,
145
- const std::set<int >& cells,
146
- const int num_levels,
147
- const Opm::ZoltanPartitioningControl<Element>& zoltan_ctrl) const ;
128
+ void connectNeighbors (std::vector<int >& cells,
129
+ const int num_levels) const ;
148
130
149
131
// / Partition rank's interior cells into non-overlapping domains using
150
132
// / the Zoltan graph partitioning software package.
@@ -179,6 +161,9 @@ class ZoltanPartitioner
179
161
// / Zoltan partitioning backend.
180
162
Opm::ParallelNLDDPartitioningZoltan partitioner_;
181
163
164
+ // / Map of cell indices to their neighbors for fast lookup
165
+ std::unordered_map<int , std::unordered_set<int >> neighbor_map_;
166
+
182
167
// / Form internal connectivity graph between rank's reachable, interior
183
168
// / cells.
184
169
// /
@@ -197,7 +182,8 @@ class ZoltanPartitioner
197
182
template <class GridView , class Element >
198
183
std::unordered_map<int , int >
199
184
connectElements (const GridView& grid_view,
200
- const Opm::ZoltanPartitioningControl<Element>& zoltan_ctrl);
185
+ const Opm::ZoltanPartitioningControl<Element>& zoltan_ctrl,
186
+ const bool build_neighbor_map);
201
187
202
188
// / Adapt internal connectivity graph to account for connections induced
203
189
// / by well reservoir connections.
@@ -216,13 +202,11 @@ class ZoltanPartitioner
216
202
// /
217
203
// / \param[in] num_neighbor_levels Number of neighbor levels to include when connecting well cells.
218
204
// / Default is 0, which means only direct well connections are considered.
219
- template <typename Comm, class GridView , class Element >
205
+ template <typename Comm>
220
206
void connectWells (const Comm comm,
221
- const GridView& grid_view,
222
207
const std::vector<Opm::Well>& wells,
223
208
std::unordered_map<std::string, std::set<int >> possibleFutureConnections,
224
209
const std::unordered_map<int , int >& g2l,
225
- const Opm::ZoltanPartitioningControl<Element>& zoltan_ctrl,
226
210
const int num_neighbor_levels);
227
211
};
228
212
@@ -243,74 +227,40 @@ void ZoltanPartitioner::buildLocalGraph(const GridView&
243
227
const Opm::ZoltanPartitioningControl<Element>& zoltan_ctrl,
244
228
const int num_neighbor_levels)
245
229
{
246
- this ->connectWells (grid_view.comm (), grid_view, wells, possibleFutureConnections,
247
- this ->connectElements (grid_view, zoltan_ctrl), zoltan_ctrl, num_neighbor_levels);
230
+ const bool build_neighbor_map = num_neighbor_levels > 0 ;
231
+ auto g2l = this ->connectElements (grid_view, zoltan_ctrl, build_neighbor_map);
232
+ this ->connectWells (grid_view.comm (), wells, possibleFutureConnections, g2l, num_neighbor_levels);
248
233
}
249
234
250
- template <class GridView , class Element >
251
- std::set<int > ZoltanPartitioner::findNeighbors (const GridView& grid_view,
252
- const int cell_index,
253
- const Opm::ZoltanPartitioningControl<Element>& zoltan_ctrl) const
235
+ void ZoltanPartitioner::connectNeighbors (std::vector<int >& cells,
236
+ const int num_levels) const
254
237
{
255
- std::set<int > neighbors;
256
-
257
- for (const auto & element : elements (grid_view, Dune::Partitions::interior)) {
258
- if (zoltan_ctrl.index (element) != cell_index) {
259
- continue ;
260
- }
261
-
262
- for (const auto & is : intersections (grid_view, element)) {
263
- if (!is.neighbor ()) {
264
- continue ;
265
- }
266
-
267
- const auto & out = is.outside ();
268
- if (out.partitionType () != Dune::InteriorEntity) {
269
- continue ;
238
+ // Initialize search structures
239
+ std::unordered_set<int > visited (cells.begin (), cells.end ());
240
+ std::vector<int > frontier;
241
+ std::vector<int > new_frontier;
242
+ frontier.swap (cells);
243
+
244
+ // Breadth-first search for neighbor cells up to num_levels away
245
+ for (int level = 0 ; level < num_levels && !frontier.empty (); ++level) {
246
+ new_frontier.clear ();
247
+ new_frontier.reserve (frontier.size () * 2 );
248
+
249
+ // Expand current frontier using precomputed neighbor map
250
+ for (const int cell : frontier) {
251
+ for (const int neighbor : neighbor_map_.at (cell)) {
252
+ if (visited.insert (neighbor).second ) { // New discovery
253
+ new_frontier.push_back (neighbor);
254
+ }
270
255
}
271
-
272
- neighbors.insert (zoltan_ctrl.index (out));
273
256
}
274
- break ;
275
- }
276
257
277
- return neighbors;
278
- }
279
-
280
- template <class GridView , class Element >
281
- std::set<int > ZoltanPartitioner::connectNeighbors (const GridView& grid_view,
282
- const std::set<int >& cells,
283
- const int num_levels,
284
- const Opm::ZoltanPartitioningControl<Element>& zoltan_ctrl) const
285
- {
286
- if (num_levels <= 0 ) {
287
- return cells;
258
+ frontier.swap (new_frontier); // Prepare next level
288
259
}
289
- std::set<int > all_cells = cells;
290
- std::set<int > frontier = cells;
291
-
292
- for (int level = 0 ; level < num_levels; ++level) {
293
- std::set<int > new_frontier;
294
-
295
- for (const int cell : frontier) {
296
- auto neighbors = findNeighbors (grid_view, cell, zoltan_ctrl);
297
- new_frontier.insert (neighbors.begin (), neighbors.end ());
298
- }
299
-
300
- // Remove cells we've already processed
301
- for (const int cell : all_cells) {
302
- new_frontier.erase (cell);
303
- }
304
-
305
- // If no new cells found, we can stop
306
- if (new_frontier.empty ()) {
307
- break ;
308
- }
309
260
310
- all_cells.insert (new_frontier.begin (), new_frontier.end ());
311
- frontier = std::move (new_frontier);
312
- }
313
- return all_cells;
261
+ // Sort final result for faster processing in later steps
262
+ cells.assign (visited.begin (), visited.end ());
263
+ std::sort (cells.begin (), cells.end ());
314
264
}
315
265
316
266
template <class GridView , class Element >
@@ -344,16 +294,24 @@ ZoltanPartitioner::partition(const int num_
344
294
template <class GridView , class Element >
345
295
std::unordered_map<int , int >
346
296
ZoltanPartitioner::connectElements (const GridView& grid_view,
347
- const Opm::ZoltanPartitioningControl<Element>& zoltan_ctrl)
297
+ const Opm::ZoltanPartitioningControl<Element>& zoltan_ctrl,
298
+ const bool build_neighbor_map)
348
299
{
349
300
auto g2l = std::unordered_map<int , int >{};
301
+ if (build_neighbor_map) {
302
+ neighbor_map_.clear ();
303
+ }
350
304
351
305
for (const auto & element : elements (grid_view, Dune::Partitions::interior)) {
352
306
{
353
307
const auto c = zoltan_ctrl.index (element);
354
308
g2l.insert_or_assign (zoltan_ctrl.local_to_global (c), c);
355
- }
356
309
310
+ if (build_neighbor_map) {
311
+ // Initialize neighbor set for this cell
312
+ neighbor_map_[c];
313
+ }
314
+ }
357
315
for (const auto & is : intersections (grid_view, element)) {
358
316
if (! is.neighbor ()) {
359
317
continue ;
@@ -374,19 +332,23 @@ ZoltanPartitioner::connectElements(const GridView&
374
332
375
333
this ->partitioner_ .registerConnection (c1, c2);
376
334
this ->partitioner_ .registerConnection (c2, c1);
335
+
336
+ if (build_neighbor_map) {
337
+ // Store neighbors in both directions
338
+ neighbor_map_[c1].insert (c2);
339
+ neighbor_map_[c2].insert (c1);
340
+ }
377
341
}
378
342
}
379
343
380
344
return g2l;
381
345
}
382
346
383
- template <typename Comm, class GridView , class Element >
347
+ template <typename Comm>
384
348
void ZoltanPartitioner::connectWells (const Comm comm,
385
- const GridView& grid_view,
386
349
const std::vector<Opm::Well>& wells,
387
350
std::unordered_map<std::string, std::set<int >> possibleFutureConnections,
388
351
const std::unordered_map<int , int >& g2l,
389
- const Opm::ZoltanPartitioningControl<Element>& zoltan_ctrl,
390
352
const int num_neighbor_levels)
391
353
{
392
354
auto distributedWells = 0 ;
@@ -398,15 +360,13 @@ void ZoltanPartitioner::connectWells(const Comm
398
360
for (const auto & well : wells) {
399
361
auto cellIx = std::vector<int >{};
400
362
auto otherProc = 0 ;
401
- std::set<int > wellCells;
402
363
403
364
for (const auto & conn : well.getConnections ()) {
404
365
auto locPos = g2l.find (conn.global_index ());
405
366
if (locPos == g2l.end ()) {
406
367
++otherProc;
407
368
continue ;
408
369
}
409
- wellCells.insert (locPos->second );
410
370
cellIx.push_back (locPos->second );
411
371
}
412
372
const auto possibleFutureConnectionSetIt = possibleFutureConnections.find (well.name ());
@@ -417,7 +377,6 @@ void ZoltanPartitioner::connectWells(const Comm
417
377
++otherProc;
418
378
continue ;
419
379
}
420
- wellCells.insert (locPos->second );
421
380
cellIx.push_back (locPos->second );
422
381
}
423
382
}
@@ -427,19 +386,18 @@ void ZoltanPartitioner::connectWells(const Comm
427
386
continue ;
428
387
}
429
388
430
- // If neighbor levels > 0, expand the well cells to include neighbors
431
- if (num_neighbor_levels > 0 && !wellCells.empty ()) {
432
- wellCells = connectNeighbors (grid_view, wellCells, num_neighbor_levels, zoltan_ctrl);
433
- cellIx.clear ();
434
- cellIx.insert (cellIx.end (), wellCells.begin (), wellCells.end ());
389
+ // Process neighbors and get deduplicated result
390
+ if (num_neighbor_levels > 0 && !cellIx.empty ()) {
391
+ connectNeighbors (cellIx, num_neighbor_levels);
435
392
}
436
393
437
394
const auto nc = cellIx.size ();
438
- for (auto ic1 = 0 *nc; ic1 < nc; ++ic1) {
439
- for (auto ic2 = ic1 + 1 ; ic2 < nc; ++ic2) {
440
- this ->partitioner_ .registerConnection (cellIx[ic1], cellIx[ic2]);
441
- this ->partitioner_ .registerConnection (cellIx[ic2], cellIx[ic1]);
442
- }
395
+ if (nc == 0 ) {
396
+ continue ;
397
+ }
398
+ for (auto ic1 = 0 *nc; ic1 < nc-1 ; ++ic1) {
399
+ this ->partitioner_ .registerConnection (cellIx[ic1], cellIx[ic1+1 ]);
400
+ this ->partitioner_ .registerConnection (cellIx[ic1+1 ], cellIx[ic1]);
443
401
}
444
402
445
403
// Collect all cells for the well into a vertex group
@@ -708,7 +666,7 @@ Opm::partitionCells(const std::string& method,
708
666
[[maybe_unused]] const std::vector<Well>& wells,
709
667
[[maybe_unused]] const std::unordered_map<std::string, std::set<int >>& possibleFutureConnections,
710
668
[[maybe_unused]] const ZoltanPartitioningControl<Element>& zoltan_ctrl,
711
- const int num_neighbor_levels)
669
+ [[maybe_unused]] const int num_neighbor_levels)
712
670
{
713
671
if (method == " zoltan" ) {
714
672
#if HAVE_MPI && HAVE_ZOLTAN
0 commit comments