oomph::METIS Namespace Reference

Namespace for METIS graph partitioning routines. More...

Typedefs

typedef void(* ErrorToWeightFctPt) (const double &spatial_error, const double &max_error, const double &min_error, int &weight)
 

Functions

void default_error_to_weight_fct (const double &spatial_error, const double &max_error, const double &min_error, int &weight)
 
void uniform_partition_mesh (Problem *problem_pt, const unsigned &ndomain, Vector< unsigned > &element_domain)
 
void partition_mesh (Problem *problem_pt, const unsigned &ndomain, const unsigned &objective, Vector< unsigned > &element_domain)
 
void partition_mesh (OomphCommunicator *comm_pt, Mesh *mesh_pt, const unsigned &ndomain, const unsigned &objective, Vector< unsigned > &element_domain)
 

Variables

ErrorToWeightFctPt Error_to_weight_fct_pt = &default_error_to_weight_fct
 

Detailed Description

Namespace for METIS graph partitioning routines.

Typedef Documentation

◆ ErrorToWeightFctPt

typedef void(* oomph::METIS::ErrorToWeightFctPt) (const double &spatial_error, const double &max_error, const double &min_error, int &weight)

Typedef for function pointer to to function that translates spatial error into weight for METIS partitioning.

Function Documentation

◆ default_error_to_weight_fct()

void oomph::METIS::default_error_to_weight_fct ( const double spatial_error,
const double max_error,
const double min_error,
int weight 
)

Default function that translates spatial error into weight for METIS partitioning (unit weight regardless of input).

Default function that translates spatial error into weight for METIS partitioning (unit weight regardless of input)

57  {
58  weight = 1;
59  }

Referenced by partition_mesh().

◆ partition_mesh() [1/2]

void oomph::METIS::partition_mesh ( OomphCommunicator comm_pt,
Mesh mesh_pt,
const unsigned ndomain,
const unsigned objective,
Vector< unsigned > &  element_domain 
)

Use METIS to assign each element to a domain. On return, element_domain[ielem] contains the number of the domain [0,1,...,ndomain-1] to which element ielem has been assigned.

  • objective=0: minimise edgecut.
  • objective=1: minimise total communications volume.

Partioning is based on nodal graph of mesh.

◆ partition_mesh() [2/2]

void oomph::METIS::partition_mesh ( Problem problem_pt,
const unsigned ndomain,
const unsigned objective,
Vector< unsigned > &  element_domain 
)

Use METIS to assign each element to a domain. On return, element_domain[ielem] contains the number of the domain [0,1,...,ndomain-1] to which element ielem has been assigned.

  • objective=0: minimise edgecut.
  • objective=1: minimise total communications volume.

Partioning is based on nodal graph of mesh.

Use METIS to assign each element to a domain. On return, element_domain[ielem] contains the number of the domain [0,1,...,ndomain-1] to which element ielem has been assigned.

  • objective=0: minimise edgecut.
  • objective=1: minimise total communications volume.

Partioning is based on dual graph of mesh.

120  {
121  // Global mesh
122  Mesh* mesh_pt = problem_pt->mesh_pt();
123 
124  // Number of elements
125  unsigned nelem = mesh_pt->nelement();
126 
127 #ifdef PARANOID
128  if (nelem != element_domain.size())
129  {
130  std::ostringstream error_stream;
131  error_stream << "element_domain Vector has wrong length " << nelem << " "
132  << element_domain.size() << std::endl;
133 
134  throw OomphLibError(
135  error_stream.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
136  }
137 #endif
138 
139  // Setup dual graph
140  //------------------
141 
142  // Start timer
143  clock_t cpu_start = clock();
144 
145  // Container to collect all elements associated with given global eqn number
146  std::map<unsigned, std::set<unsigned>> elements_connected_with_global_eqn;
147 
148  // Container for all unique global eqn numbers
149  std::set<unsigned> all_global_eqns;
150 
151  // Loop over all elements
152  for (unsigned e = 0; e < nelem; e++)
153  {
154  GeneralisedElement* el_pt = mesh_pt->element_pt(e);
155 
156  // Add all global eqn numbers
157  unsigned ndof = el_pt->ndof();
158  for (unsigned j = 0; j < ndof; j++)
159  {
160  // Get global eqn number
161  unsigned eqn_number = el_pt->eqn_number(j);
162  elements_connected_with_global_eqn[eqn_number].insert(e);
163  all_global_eqns.insert(eqn_number);
164  }
165  }
166 
167  // Now reverse the lookup scheme to find out all elements
168  // that are connected because they share the same global eqn
169  Vector<std::set<unsigned>> connected_elements(nelem);
170 
171  // Counter for total number of entries in connected_elements structure
172  unsigned count = 0;
173 
174  // Loop over all global eqns
175  for (std::set<unsigned>::iterator it = all_global_eqns.begin();
176  it != all_global_eqns.end();
177  it++)
178  {
179  // Get set of elements connected with this data item
180  std::set<unsigned> elements = elements_connected_with_global_eqn[*it];
181 
182  // Double loop over connnected elements: Everybody's connected to
183  // everybody
184  for (std::set<unsigned>::iterator it1 = elements.begin();
185  it1 != elements.end();
186  it1++)
187  {
188  for (std::set<unsigned>::iterator it2 = elements.begin();
189  it2 != elements.end();
190  it2++)
191  {
192  if ((*it1) != (*it2))
193  {
194  connected_elements[(*it1)].insert(*it2);
195  }
196  }
197  }
198  }
199 
200 
201  // Now convert into C-style packed array for interface with METIS
202  int* xadj = new int[nelem + 1];
203  Vector<int> adjacency_vector;
204 
205  // Reserve (too much) space
206  adjacency_vector.reserve(count);
207 
208  // Initialise counters
209  unsigned ientry = 0;
210 
211  // Loop over all elements
212  for (unsigned e = 0; e < nelem; e++)
213  {
214  // First entry for current element
215  xadj[e] = ientry;
216 
217  // Loop over elements that are connected to current element
218  typedef std::set<unsigned>::iterator IT;
219  for (IT it = connected_elements[e].begin();
220  it != connected_elements[e].end();
221  it++)
222  {
223  // Copy into adjacency array
224  adjacency_vector.push_back(*it);
225 
226  // We've just made another entry
227  ientry++;
228  }
229 
230  // Entry after last entry for current element:
231  xadj[e + 1] = ientry;
232  }
233 
234  // End timer
235  clock_t cpu_end = clock();
236 
237  // Doc
238  double cpu0 = double(cpu_end - cpu_start) / CLOCKS_PER_SEC;
239  oomph_info
240  << "CPU time for setup of METIS data structures [nelem="
241  << nelem << "]: " << cpu0 << " sec" << std::endl;
242 
243 
244  // Call METIS graph partitioner
245  //-----------------------------
246 
247  // Start timer
248  cpu_start = clock();
249 
250  // Number of vertices in graph
251  int nvertex = nelem;
252 
253  // No vertex weights
254  int* vwgt = 0;
255 
256  // No edge weights
257  int* adjwgt = 0;
258 
259  // Flag indicating that graph isn't weighted: 0; vertex weights only: 2
260  // Note that wgtflag==2 requires nodal weights to be stored in vwgt.
261  int wgtflag = 0;
262 
263  // Use C-style numbering (first array entry is zero)
264  int numflag = 0;
265 
266  // Number of desired partitions
267  int nparts = ndomain;
268 
269  // Use default options
270  int* options = new int[10];
271  options[0] = 0;
272 
273 #ifdef OOMPH_TRANSITION_TO_VERSION_3
274  switch (objective)
275  {
276  case 0:
277  // Edge-cut minimization
278  options[0] = METIS_OBJTYPE_CUT;
279  break;
280 
281  case 1:
282  // communication volume minimisation
283  options[0] = METIS_OBJTYPE_VOL;
284  break;
285 
286  default:
287  std::ostringstream error_stream;
288  error_stream << "Wrong objective for METIS. objective = " << objective
289  << std::endl;
290 
291  throw OomphLibError(
292  error_stream.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
293  }
294 #endif
295 
296  // Number of cut edges in graph
297  int* edgecut = new int[nelem];
298 
299  // Array containing the partition information
300  int* part = new int[nelem];
301 
302  // Can we get an error estimate?
303 
304  unsigned n_mesh = problem_pt->nsub_mesh();
305 
306  if (n_mesh == 0)
307  {
308  RefineableMeshBase* mmesh_pt = dynamic_cast<RefineableMeshBase*>(mesh_pt);
309  if (mmesh_pt != 0)
310  {
311  // Bias distribution?
313  {
314  oomph_info
315  << "Biasing element distribution via spatial error estimate\n";
316 
317  // Adjust flag and provide storage for weights
318  wgtflag = 2;
319  vwgt = new int[nelem];
320 
321  // Get error for all elements
322  Vector<double> elemental_error(nelem);
323  mmesh_pt->spatial_error_estimator_pt()->get_element_errors(
324  mesh_pt, elemental_error);
325 
326  double max_error =
327  *(std::max_element(elemental_error.begin(), elemental_error.end()));
328  double min_error =
329  *(std::min_element(elemental_error.begin(), elemental_error.end()));
330 
331  // Bias weights
332  int weight = 1;
333  for (unsigned e = 0; e < nelem; e++)
334  {
335  // Translate error into weight
337  elemental_error[e], max_error, min_error, weight);
338  vwgt[e] = weight;
339  }
340  }
341  }
342  }
343  else // There are submeshes
344  {
345  // Are any of the submeshes refineable?
346  bool refineable_submesh_exists = false;
347  // Vector to store "start and end point" for loops in submeshes
348  Vector<unsigned> loop_helper(n_mesh + 1);
349  loop_helper[0] = 0;
350 
351  // Loop over submeshes
352  for (unsigned i_mesh = 0; i_mesh < n_mesh; i_mesh++)
353  {
354  // Store the end of the loop
355  loop_helper[i_mesh + 1] =
356  problem_pt->mesh_pt(i_mesh)->nelement() + loop_helper[i_mesh];
357 
358  RefineableMeshBase* mmesh_pt =
359  dynamic_cast<RefineableMeshBase*>(problem_pt->mesh_pt(i_mesh));
360  if (mmesh_pt != 0)
361  {
362  refineable_submesh_exists = true;
363  }
364  }
365 
366  // If a refineable submesh exists
367  if (refineable_submesh_exists)
368  {
369  // Bias distribution?
371  {
372  oomph_info
373  << "Biasing element distribution via spatial error estimate\n";
374 
375  // Adjust flag and provide storage for weights
376  wgtflag = 2;
377  vwgt = new int[nelem];
378 
379  // Loop over submeshes
380  for (unsigned i_mesh = 0; i_mesh < n_mesh; i_mesh++)
381  {
382  RefineableMeshBase* mmesh_pt =
383  dynamic_cast<RefineableMeshBase*>(problem_pt->mesh_pt(i_mesh));
384  if (mmesh_pt != 0)
385  {
386  // Get error for all elements
387  unsigned nsub_elem =
388  loop_helper[i_mesh + 1] - loop_helper[i_mesh];
389  Vector<double> elemental_error(nsub_elem);
390  mmesh_pt->spatial_error_estimator_pt()->get_element_errors(
391  problem_pt->mesh_pt(i_mesh), elemental_error);
392 
393  double max_error = *(std::max_element(elemental_error.begin(),
394  elemental_error.end()));
395  double min_error = *(std::min_element(elemental_error.begin(),
396  elemental_error.end()));
397 
398  // Bias weights
399  int weight = 1;
400  unsigned start = loop_helper[i_mesh];
401  unsigned end = loop_helper[i_mesh + 1];
402  for (unsigned e = start; e < end; e++)
403  {
404  unsigned error_index = e - start;
405  // Translate error into weight
407  elemental_error[error_index], max_error, min_error, weight);
408  vwgt[e] = weight;
409  }
410  }
411  else // This mesh is not refineable
412  {
413  // There's no error estimator, so use the default weight
414  int weight = 1;
415  unsigned start = loop_helper[i_mesh];
416  unsigned end = loop_helper[i_mesh + 1];
417  for (unsigned e = start; e < end; e++)
418  {
419  vwgt[e] = weight;
420  }
421  }
422  }
423  }
424  }
425  }
426 
427 #ifdef OOMPH_TRANSITION_TO_VERSION_3
428 
429  // Call partitioner
430  METIS_PartGraphKway(&nvertex,
431  xadj,
432  &adjacency_vector[0],
433  vwgt,
434  adjwgt,
435  &wgtflag,
436  &numflag,
437  &nparts,
438  options,
439  edgecut,
440  part);
441 #else
442  // original code to delete in version 3
443 
444  // Call partitioner
445  if (objective == 0)
446  {
447  // Partition with the objective of minimising the edge cut
448  METIS_PartGraphKway(&nvertex,
449  xadj,
450  &adjacency_vector[0],
451  vwgt,
452  adjwgt,
453  &wgtflag,
454  &numflag,
455  &nparts,
456  options,
457  edgecut,
458  part);
459  }
460  else if (objective == 1)
461  {
462  // Partition with the objective of minimising the total communication
463  // volume
464  METIS_PartGraphVKway(&nvertex,
465  xadj,
466  &adjacency_vector[0],
467  vwgt,
468  adjwgt,
469  &wgtflag,
470  &numflag,
471  &nparts,
472  options,
473  edgecut,
474  part);
475  }
476  else
477  {
478  std::ostringstream error_stream;
479  error_stream << "Wrong objective for METIS. objective = " << objective
480  << std::endl;
481 
482  throw OomphLibError(
483  error_stream.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
484  }
485 #endif
486 
487 #ifdef PARANOID
488  std::vector<bool> done(nparts, false);
489 #endif
490 
491  // Copy across
492  for (unsigned e = 0; e < nelem; e++)
493  {
494  element_domain[e] = part[e];
495 #ifdef PARANOID
496  done[part[e]] = true;
497 #endif
498  }
499 
500 
501 #ifdef PARANOID
502  // Check
503  std::ostringstream error_stream;
504  bool shout = false;
505  for (int p = 0; p < nparts; p++)
506  {
507  if (!done[p])
508  {
509  shout = true;
510  error_stream << "No elements on processor " << p
511  << "when trying to partition " << nelem << "elements over "
512  << nparts << " processors!\n";
513  }
514  }
515  if (shout)
516  {
517  throw OomphLibError(
518  error_stream.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
519  }
520 #endif
521 
522 
523  // End timer
524  cpu_end = clock();
525 
526  // Doc
527  double cpu1 = double(cpu_end - cpu_start) / CLOCKS_PER_SEC;
528  oomph_info
529  << "CPU time for METIS mesh partitioning [nelem="
530  << nelem << "]: " << cpu1 << " sec" << std::endl;
531 
532 
533  // Cleanup
534  delete[] xadj;
535  delete[] part;
536  delete[] edgecut;
537  delete[] options;
538  }
Array< double, 1, 3 > e(1./3., 0.5, 2.)
float * p
Definition: Tutorial_Map_using.cpp:9
static constexpr lastp1_t end
Definition: IndexedViewHelper.h:79
double min_error
Definition: MortaringCantileverCompareToNonMortaring.cpp:189
double max_error
Definition: MortaringCantileverCompareToNonMortaring.cpp:188
void start(const unsigned &i)
(Re-)start i-th timer
Definition: oomph_utilities.cc:243
ErrorToWeightFctPt Error_to_weight_fct_pt
Definition: partitioning.cc:63
void default_error_to_weight_fct(const double &spatial_error, const double &max_error, const double &min_error, int &weight)
Definition: partitioning.cc:53
OomphInfo oomph_info
Definition: oomph_definitions.cc:319
#define OOMPH_EXCEPTION_LOCATION
Definition: oomph_definitions.h:61
#define OOMPH_CURRENT_FUNCTION
Definition: oomph_definitions.h:86
void METIS_PartGraphVKway(int *, idxtype *, idxtype *, idxtype *, idxtype *, int *, int *, int *, int *, int *, idxtype *)
void METIS_PartGraphKway(int *, idxtype *, idxtype *, idxtype *, idxtype *, int *, int *, int *, int *, int *, idxtype *)
std::ptrdiff_t j
Definition: tut_arithmetic_redux_minmax.cpp:2

References default_error_to_weight_fct(), e(), oomph::Mesh::element_pt(), Eigen::placeholders::end, oomph::GeneralisedElement::eqn_number(), Error_to_weight_fct_pt, oomph::ErrorEstimator::get_element_errors(), j, MeshRefinement::max_error, oomph::Problem::mesh_pt(), oomph::METIS_PartGraphKway(), oomph::METIS_PartGraphVKway(), MeshRefinement::min_error, oomph::GeneralisedElement::ndof(), oomph::Mesh::nelement(), oomph::Problem::nsub_mesh(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION, oomph::oomph_info, p, oomph::RefineableMeshBase::spatial_error_estimator_pt(), and oomph::CumulativeTimings::start().

◆ uniform_partition_mesh()

void oomph::METIS::uniform_partition_mesh ( Problem problem_pt,
const unsigned ndomain,
Vector< unsigned > &  element_domain 
)

Partition mesh uniformly by dividing elements equally over the partitions, in the order in which they are returned by problem. On return, element_domain[ielem] contains the number of the domain [0,1,...,ndomain-1] to which element ielem has been assigned.

79  {
80  // Number of elements
81  unsigned nelem = problem_pt->mesh_pt()->nelement();
82 
83 #ifdef PARANOID
84  if (nelem != element_domain.size())
85  {
86  std::ostringstream error_stream;
87  error_stream << "element_domain Vector has wrong length " << nelem << " "
88  << element_domain.size() << std::endl;
89 
90  throw OomphLibError(
92  }
93 #endif
94 
95 
96  // Uniform partitioning
97  unsigned nel_per_domain = int(float(nelem) / float(ndomain));
98  for (unsigned ielem = 0; ielem < nelem; ielem++)
99  {
100  unsigned idomain = unsigned(float(ielem) / float(nel_per_domain));
101  element_domain[ielem] = idomain;
102  }
103  }
return int(ret)+1

References int(), oomph::Problem::mesh_pt(), oomph::Mesh::nelement(), OOMPH_CURRENT_FUNCTION, and OOMPH_EXCEPTION_LOCATION.

Variable Documentation

◆ Error_to_weight_fct_pt

ErrorToWeightFctPt oomph::METIS::Error_to_weight_fct_pt = &default_error_to_weight_fct

Function pointer to to function that translates spatial error into weight for METIS partitioning.

Referenced by partition_mesh().