Nektar++
MeshPartition.cpp
Go to the documentation of this file.
1 ///////////////////////////////////////////////////////////////////////////////
2 //
3 // File: MeshPartition.cpp
4 //
5 // For more information, please see: http://www.nektar.info/
6 //
7 // The MIT License
8 //
9 // Copyright (c) 2006 Division of Applied Mathematics, Brown University (USA),
10 // Department of Aeronautics, Imperial College London (UK), and Scientific
11 // Computing and Imaging Institute, University of Utah (USA).
12 //
13 // Permission is hereby granted, free of charge, to any person obtaining a
14 // copy of this software and associated documentation files (the "Software"),
15 // to deal in the Software without restriction, including without limitation
16 // the rights to use, copy, modify, merge, publish, distribute, sublicense,
17 // and/or sell copies of the Software, and to permit persons to whom the
18 // Software is furnished to do so, subject to the following conditions:
19 //
20 // The above copyright notice and this permission notice shall be included
21 // in all copies or substantial portions of the Software.
22 //
23 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
24 // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
26 // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
28 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
29 // DEALINGS IN THE SOFTWARE.
30 //
31 // Description:
32 //
33 //
34 ////////////////////////////////////////////////////////////////////////////////
35 
36 #ifndef TIXML_USE_STL
37 #define TIXML_USE_STL
38 #endif
39 
40 #include <boost/core/ignore_unused.hpp>
41 
44 
45 #include <iomanip>
46 #include <iostream>
47 #include <map>
48 #include <vector>
49 
50 #include <tinyxml.h>
51 
56 
58 
59 #include <boost/algorithm/string.hpp>
60 #include <boost/format.hpp>
61 #include <boost/graph/adjacency_iterator.hpp>
62 #include <boost/graph/adjacency_list.hpp>
63 #include <boost/graph/detail/edge.hpp>
64 
65 namespace Nektar
66 {
67 namespace SpatialDomains
68 {
69 
71 {
72  static MeshPartitionFactory instance;
73  return instance;
74 }
75 
77  LibUtilities::CommSharedPtr comm, int meshDim,
78  std::map<int, MeshEntity> element,
79  CompositeDescriptor compMap)
80  : m_session(session), m_comm(comm), m_dim(meshDim), m_numFields(0),
81  m_elements(element), m_compMap(compMap), m_fieldNameToId(),
82  m_weightingRequired(false), m_weightBnd(false), m_weightDofs(false),
83  m_parallel(false)
84 {
85  // leave the meshpartition method of reading expansions and conditions
88 
89  for (auto elIt = m_elements.cbegin(); elIt != m_elements.cend();)
90  {
91  if (elIt->second.ghost)
92  {
93  m_ghostElmts[elIt->first] = elIt->second;
94  elIt = m_elements.erase(elIt);
95  }
96  else
97  {
98  ++elIt;
99  }
100  }
101 }
102 
104 {
105 }
106 
107 void MeshPartition::PartitionMesh(int nParts, bool shared, bool overlapping,
108  int nLocal)
109 {
110  boost::ignore_unused(nLocal);
111 
112  ASSERTL0(m_parallel || m_elements.size() >= nParts,
113  "Too few elements for this many processes.");
114  m_shared = shared;
115 
116  ASSERTL0(!m_parallel || shared,
117  "Parallel partitioning requires shared filesystem.");
118 
120  {
121  WeightElements();
122  }
123  CreateGraph();
124 
125  PartitionGraph(nParts, overlapping);
126 }
127 
129 {
130  // Find the Expansions tag
131  TiXmlElement *expansionTypes = m_session->GetElement("Nektar/Expansions");
132 
133  // Find the Expansion type
134  TiXmlElement *expansion = expansionTypes->FirstChildElement();
135  std::string expType = expansion->Value();
136 
137  // Expansiontypes will contain plenty of data, where relevant at this stage
138  // are composite ID(s) that this expansion type describes, nummodes and a
139  // list of fields that this expansion relates to. If this does not exist the
140  // variable is only set to "DefaultVar".
141  if (expType == "E")
142  {
143  while (expansion)
144  {
145  std::vector<unsigned int> composite;
146  std::vector<unsigned int> nummodes;
147  std::vector<std::string> fieldName;
148 
149  const char *nModesStr = expansion->Attribute("NUMMODES");
150  ASSERTL0(nModesStr,
151  "NUMMODES was not defined in EXPANSION section of input");
152  std::string numModesStr = nModesStr;
153  bool valid =
154  ParseUtils::GenerateVector(numModesStr.c_str(), nummodes);
155  ASSERTL0(valid, "Unable to correctly parse the number of modes.");
156 
157  if (nummodes.size() == 1)
158  {
159  for (int i = 1; i < m_dim; i++)
160  {
161  nummodes.push_back(nummodes[0]);
162  }
163  }
164  ASSERTL0(nummodes.size() == m_dim,
165  "Number of modes should match mesh dimension");
166 
167  const char *fStr = expansion->Attribute("FIELDS");
168  if (fStr)
169  {
170  std::string fieldStr = fStr;
171  bool valid =
172  ParseUtils::GenerateVector(fieldStr.c_str(), fieldName);
173  ASSERTL0(valid, "Unable to correctly parse the field string in "
174  "ExpansionTypes.");
175 
176  for (int i = 0; i < fieldName.size(); ++i)
177  {
178  if (m_fieldNameToId.count(fieldName[i]) == 0)
179  {
180  int k = m_fieldNameToId.size();
181  m_fieldNameToId[fieldName[i]] = k;
182  m_numFields++;
183  }
184  }
185  }
186  else
187  {
188  fieldName.push_back("DefaultVar");
189  int k = m_fieldNameToId.size();
190 
191  if (m_fieldNameToId.count("DefaultVar") == 0)
192  {
193  ASSERTL0(
194  k == 0,
195  "Omitting field variables and explicitly listing "
196  "them in different ExpansionTypes is wrong practise");
197 
198  m_fieldNameToId["DefaultVar"] = k;
199  m_numFields++;
200  }
201  }
202 
203  std::string compositeStr = expansion->Attribute("COMPOSITE");
204  ASSERTL0(compositeStr.length() > 3,
205  "COMPOSITE must be specified in expansion definition");
206  int beg = compositeStr.find_first_of("[");
207  int end = compositeStr.find_first_of("]");
208  std::string compositeListStr =
209  compositeStr.substr(beg + 1, end - beg - 1);
210  bool parseGood = ParseUtils::GenerateSeqVector(
211  compositeListStr.c_str(), composite);
212  ASSERTL0(parseGood && !composite.empty(),
213  (std::string("Unable to read composite index range: ") +
214  compositeListStr)
215  .c_str());
216 
217  // construct mapping (elmt id, field name) -> nummodes
218  for (int i = 0; i < composite.size(); ++i)
219  {
220  auto &shapeType = m_compMap[composite[i]].first;
221  auto &elmtIds = m_compMap[composite[i]].second;
222 
223  for (int j = 0; j < fieldName.size(); j++)
224  {
225  for (auto &elid : elmtIds)
226  {
227  m_expansions[elid][fieldName[j]] = nummodes;
228  m_shape[elid] = shapeType;
229  }
230  }
231  }
232 
233  expansion = expansion->NextSiblingElement("E");
234  }
235  }
236  else if (expType == "F")
237  {
238  ASSERTL0(expansion->Attribute("FILE"),
239  "Attribute FILE expected for type F expansion");
240  std::string filenameStr = expansion->Attribute("FILE");
241  ASSERTL0(!filenameStr.empty(),
242  "A filename must be specified for the FILE "
243  "attribute of expansion");
244 
245  // Create fieldIO object to load file
246  // need a serial communicator to avoid problems with
247  // shared file system
250  std::string iofmt =
251  LibUtilities::FieldIO::GetFileType(filenameStr, comm);
254  iofmt, comm, m_session->GetSharedFilesystem());
255 
256  // Load field definitions from file
257  std::vector<LibUtilities::FieldDefinitionsSharedPtr> fielddefs;
258  f->Import(filenameStr, fielddefs);
259 
260  // Parse field definitions
261  for (int i = 0; i < fielddefs.size(); ++i)
262  {
263  // Name of fields
264  for (int j = 0; j < fielddefs[i]->m_fields.size(); ++j)
265  {
266  std::string fieldName = fielddefs[i]->m_fields[j];
267  if (m_fieldNameToId.count(fieldName) == 0)
268  {
269  int k = m_fieldNameToId.size();
270  m_fieldNameToId[fieldName] = k;
271  m_numFields++;
272  }
273  }
274  // Number of modes and shape for each element
275  int numHomoDir = fielddefs[i]->m_numHomogeneousDir;
276  int cnt = 0;
277  for (int j = 0; j < fielddefs[i]->m_elementIDs.size(); ++j)
278  {
279  int elid = fielddefs[i]->m_elementIDs[j];
280  std::vector<unsigned int> nummodes;
281  for (int k = 0; k < m_dim; k++)
282  {
283  nummodes.push_back(fielddefs[i]->m_numModes[cnt++]);
284  }
285  if (fielddefs[i]->m_uniOrder)
286  {
287  cnt = 0;
288  }
289  else
290  {
291  cnt += numHomoDir;
292  }
293  for (int k = 0; k < fielddefs[i]->m_fields.size(); k++)
294  {
295  std::string fieldName = fielddefs[i]->m_fields[k];
296  m_expansions[elid][fieldName] = nummodes;
297  }
298  m_shape[elid] = fielddefs[i]->m_shapeType;
299  }
300  }
301  }
302  else
303  {
304  ASSERTL0(false,
305  "Expansion type not defined or not supported at the moment");
306  }
307 }
308 
309 void MeshPartition::PrintPartInfo(std::ostream &out)
310 {
311  int nElmt = boost::num_vertices(m_graph);
312  int nPart = m_localPartition.size();
313 
314  out << "# Partition information:" << std::endl;
315  out << "# No. elements : " << nElmt << std::endl;
316  out << "# No. partitions: " << nPart << std::endl;
317  out << "# ID nElmt nLocDof nBndDof" << std::endl;
318 
319  BoostVertexIterator vertit, vertit_end;
320  std::vector<int> partElmtCount(nPart, 0);
321  std::vector<int> partLocCount(nPart, 0);
322  std::vector<int> partBndCount(nPart, 0);
323 
324  std::map<int, int> elmtSizes;
325  std::map<int, int> elmtBndSizes;
326 
327  for (std::map<int, NummodesPerField>::iterator expIt = m_expansions.begin();
328  expIt != m_expansions.end(); ++expIt)
329  {
330  int elid = expIt->first;
331  NummodesPerField npf = expIt->second;
332 
333  for (NummodesPerField::iterator it = npf.begin(); it != npf.end(); ++it)
334  {
335  ASSERTL0(it->second.size() == m_dim,
336  " Number of directional"
337  " modes in expansion spec for element id = " +
338  boost::lexical_cast<std::string>(elid) +
339  " and field " +
340  boost::lexical_cast<std::string>(it->first) +
341  " does not correspond to mesh dimension");
342 
343  int na = it->second[0];
344  int nb = 0;
345  int nc = 0;
346  if (m_dim >= 2)
347  {
348  nb = it->second[1];
349  }
350  if (m_dim == 3)
351  {
352  nc = it->second[2];
353  }
354 
355  elmtSizes[elid] =
356  CalculateElementWeight(m_shape[elid], false, na, nb, nc);
357  elmtBndSizes[elid] =
358  CalculateElementWeight(m_shape[elid], true, na, nb, nc);
359  }
360  }
361 
362  for (boost::tie(vertit, vertit_end) = boost::vertices(m_graph);
363  vertit != vertit_end; ++vertit)
364  {
365  int partId = m_graph[*vertit].partition;
366  partElmtCount[partId]++;
367  partLocCount[partId] += elmtSizes[m_graph[*vertit].id];
368  partBndCount[partId] += elmtBndSizes[m_graph[*vertit].id];
369  }
370 
371  for (int i = 0; i < nPart; ++i)
372  {
373  out << i << " " << partElmtCount[i] << " " << partLocCount[i] << " "
374  << partBndCount[i] << std::endl;
375  }
376 }
377 
379 {
380  if (!m_session->DefinesElement("Nektar/Conditions/SolverInfo"))
381  {
382  // No SolverInfo = no change of default action to weight
383  // mesh graph.
384  return;
385  }
386 
387  TiXmlElement *solverInfoElement =
388  m_session->GetElement("Nektar/Conditions/SolverInfo");
389 
390  TiXmlElement *solverInfo = solverInfoElement->FirstChildElement("I");
391  ASSERTL0(solverInfo, "Cannot read SolverInfo tags");
392 
393  while (solverInfo)
394  {
395  // read the property name
396  ASSERTL0(solverInfo->Attribute("PROPERTY"),
397  "Missing PROPERTY attribute in solver info "
398  "section. ");
399  std::string solverProperty = solverInfo->Attribute("PROPERTY");
400  ASSERTL0(!solverProperty.empty(),
401  "Solver info properties must have a non-empty "
402  "name. ");
403  // make sure that solver property is capitalised
404  std::string solverPropertyUpper = boost::to_upper_copy(solverProperty);
405 
406  // read the value
407  ASSERTL0(solverInfo->Attribute("VALUE"),
408  "Missing VALUE attribute in solver info section. ");
409  std::string solverValue = solverInfo->Attribute("VALUE");
410  ASSERTL0(!solverValue.empty(),
411  "Solver info properties must have a non-empty value");
412  // make sure that property value is capitalised
413  std::string propertyValueUpper = boost::to_upper_copy(solverValue);
414 
415  if (solverPropertyUpper == "WEIGHTPARTITIONS")
416  {
417  if (propertyValueUpper == "DOF")
418  {
419  m_weightingRequired = true;
420  m_weightDofs = true;
421  }
422  else if (propertyValueUpper == "BOUNDARY")
423  {
424  m_weightingRequired = true;
425  m_weightBnd = true;
426  }
427  else if (propertyValueUpper == "BOTH")
428  {
429  m_weightingRequired = true;
430  m_weightDofs = true;
431  m_weightBnd = true;
432  }
433  return;
434  }
435  solverInfo = solverInfo->NextSiblingElement("I");
436  }
437 }
438 
439 /*
440  * Calculate element weights based on
441  * - element type (Q,T,H,P,R,A)
442  * - nummodes in expansion which this element belongs to via composite.
443  *
444  * For each element we prepare two vertex weightings, one associated
445  * with the number of matrix elements associated with it (to balance
446  * matrix multiplication work) and another associated
447  * with all work which scales linearly with the number of its
448  * coefficients: communication, vector updates etc.
449  *
450  * \todo Refactor this code to explicitly represent performance model
451  * and flexibly generate graph vertex weights depending on perf data.
452  */
454 {
455  std::vector<unsigned int> weight(m_numFields, 1);
456  std::map<int, MeshEntity>::iterator eIt;
457  for (eIt = m_elements.begin(); eIt != m_elements.end(); ++eIt)
458  {
459  m_vertWeights[eIt->second.origId] = weight;
460  m_vertBndWeights[eIt->second.origId] = weight;
461  m_edgeWeights[eIt->second.origId] = weight;
462  }
463 
464  for (std::map<int, NummodesPerField>::iterator expIt = m_expansions.begin();
465  expIt != m_expansions.end(); ++expIt)
466  {
467  int elid = expIt->first;
468  NummodesPerField npf = expIt->second;
469 
470  for (NummodesPerField::iterator it = npf.begin(); it != npf.end(); ++it)
471  {
472  ASSERTL0(it->second.size() == m_dim,
473  " Number of directional"
474  " modes in expansion spec for element id = " +
475  boost::lexical_cast<std::string>(elid) +
476  " and field " +
477  boost::lexical_cast<std::string>(it->first) +
478  " does not correspond to mesh dimension");
479 
480  int na = it->second[0];
481  int nb = 0;
482  int nc = 0;
483  if (m_dim >= 2)
484  {
485  nb = it->second[1];
486  }
487  if (m_dim == 3)
488  {
489  nc = it->second[2];
490  }
491 
492  // Assume for parallel partitioning that this is just missing from
493  // our partition.
494  if (m_vertWeights.find(elid) == m_vertWeights.end())
495  {
496  continue;
497  }
498 
499  m_vertWeights[elid][m_fieldNameToId[it->first]] =
500  CalculateElementWeight(m_shape[elid], false, na, nb, nc);
501  m_vertBndWeights[elid][m_fieldNameToId[it->first]] =
502  CalculateElementWeight(m_shape[elid], true, na, nb, nc);
503  m_edgeWeights[elid][m_fieldNameToId[it->first]] =
504  CalculateEdgeWeight(m_shape[elid], na, nb, nc);
505  }
506  } // for i
507 }
508 
510 {
511  // Maps edge/face to first mesh element id.
512  // On locating second mesh element id, graph edge is created instead.
513  std::unordered_map<int, std::vector<int>> vGraphEdges;
514  int vcnt = 0;
515 
516  for (auto &elmt : m_elements)
517  {
518  auto vert = boost::add_vertex(m_graph);
519  m_graph[vert].id = elmt.first;
520  m_graph[vert].partition = 0;
521 
523  {
524  m_graph[vert].weight = m_vertWeights[elmt.second.origId];
525  m_graph[vert].bndWeight = m_vertBndWeights[elmt.second.origId];
526  m_graph[vert].edgeWeight = m_edgeWeights[elmt.second.origId];
527  }
528 
529  // Process element entries and add graph edges
530  for (auto &eId : elmt.second.list)
531  {
532  // Look to see if we've examined this edge/face before
533  // If so, we've got both graph vertices so add edge
534  auto edgeIt = vGraphEdges.find(eId);
535  if (edgeIt != vGraphEdges.end())
536  {
537  for (auto &iId : edgeIt->second)
538  {
539  BoostEdge e = boost::add_edge(vcnt, iId, m_graph).first;
540  m_graph[e].id = vcnt;
541  }
542  vGraphEdges[eId].push_back(vcnt);
543  }
544  else
545  {
546  std::vector<int> Id;
547  Id.push_back(vcnt);
548  vGraphEdges[eId] = Id;
549  }
550  }
551 
552  // Increment counter for graph vertex id.
553  ++vcnt;
554  }
555 
556  // Now process ghost elements.
557  for (auto &ghost : m_ghostElmts)
558  {
559  auto vert = boost::add_vertex(m_graph);
560  m_graph[vert].id = ghost.first;
561  m_graph[vert].partition = -1;
562 
563  for (auto &facet : ghost.second.list)
564  {
565  auto edgeIt = vGraphEdges.find(facet);
566  if (edgeIt != vGraphEdges.end())
567  {
568  for (auto &iId : edgeIt->second)
569  {
570  BoostEdge e = boost::add_edge(vcnt, iId, m_graph).first;
571  m_graph[e].id = vcnt;
572  }
573  vGraphEdges[facet].push_back(vcnt);
574  }
575  }
576 
577  // Increment counter for graph vertex id.
578  ++vcnt;
579  }
580 }
581 
582 /**
583  * @brief Partition the graph.
584  *
585  * This routine partitions the graph @p pGraph into @p nParts, producing
586  * subgraphs that are populated in @p pLocalPartition. If the @p
587  * overlapping option is set (which is used for post-processing
588  * purposes), the resulting partitions are extended to cover
589  * neighbouring elements by additional vertex on the dual graph, which
590  * produces overlapping partitions (i.e. the intersection of two
591  * connected partitions is non-empty).
592  *
593  * @param nParts Number of partitions.
594  * @param pLocalPartition Vector of sub-graphs representing each
595  * @param overlapping True if resulting partitions should overlap.
596  */
597 void MeshPartition::PartitionGraph(int nParts, bool overlapping)
598 {
599  int i;
600  int nGraphVerts = boost::num_vertices(m_graph);
601  int nGhost = m_ghostElmts.size();
602  int nLocal = nGraphVerts - nGhost;
603 
604  int ncon = 1;
605  if (m_weightDofs && m_weightBnd)
606  {
607  ncon = 2;
608  }
609 
610  // Convert boost graph into CSR format
611  BoostVertexIterator vertit, vertit_end;
612  BoostAdjacencyIterator adjvertit, adjvertit_end;
613  Array<OneD, int> part(nGraphVerts, 0);
614 
615  if (m_comm->GetRowComm()->TreatAsRankZero() || m_parallel)
616  {
617  int acnt = 0;
618  int vcnt = 0;
619  int nWeight = ncon * nLocal;
620 
621  Array<OneD, int> xadj(nLocal + 1);
622  std::vector<int> adjncy_tmp, adjwgt_tmp;
623  Array<OneD, int> vwgt(nWeight, 1);
624  Array<OneD, int> vsize(nLocal, 1);
625 
626  // Initialise starting point of adjacency array.
627  xadj[0] = 0;
628 
629  for (boost::tie(vertit, vertit_end) = boost::vertices(m_graph);
630  vertit != vertit_end && vcnt < nLocal; ++vertit)
631  {
632  for (boost::tie(adjvertit, adjvertit_end) =
633  boost::adjacent_vertices(*vertit, m_graph);
634  adjvertit != adjvertit_end; ++adjvertit, ++acnt)
635  {
636  adjncy_tmp.push_back(m_graph[*adjvertit].id);
638  {
639  adjwgt_tmp.push_back(m_graph[*vertit].edgeWeight[0]);
640  }
641  else
642  {
643  adjwgt_tmp.push_back(1);
644  }
645  }
646 
647  xadj[++vcnt] = acnt;
648 
650  {
651  int ccnt = 0;
652  if (m_weightDofs)
653  {
654  vwgt[ncon * (vcnt - 1) + ccnt] = m_graph[*vertit].weight[0];
655  ccnt++;
656  }
657  if (m_weightBnd)
658  {
659  vwgt[ncon * (vcnt - 1) + ccnt] =
660  m_graph[*vertit].bndWeight[0];
661  }
662  }
663  }
664 
665  Array<OneD, int> adjncy(adjncy_tmp.size(), &adjncy_tmp[0]);
666  Array<OneD, int> adjwgt(adjwgt_tmp.size(), &adjwgt_tmp[0]);
667 
668  // Call partitioner to partition graph
669  int vol = 0;
670 
671  try
672  {
673  //////////////////////////////////////////////////////
674  // On a cartesian communicator do mesh partiotion just on the first
675  // column
676  // so there is no doubt the partitions are all the same in all the
677  // columns
678  if (m_comm->GetColumnComm()->GetRank() == 0)
679  {
680  // Attempt partitioning.
681  v_PartitionGraphImpl(nLocal, ncon, xadj, adjncy, vwgt, vsize,
682  adjwgt, nParts, vol, part);
683 
684  // Check the partitioner produced a valid partition and fix if
685  // not.
686  if (!m_parallel)
687  {
688  CheckPartitions(nParts, part);
689  }
690 
691  if (!m_shared)
692  {
693  // distribute among columns
694  for (i = 1; i < m_comm->GetColumnComm()->GetSize(); ++i)
695  {
696  m_comm->GetColumnComm()->Send(i, part);
697  }
698  }
699  }
700  else
701  {
702  m_comm->GetColumnComm()->Recv(0, part);
703  }
704  }
705  catch (...)
706  {
707  NEKERROR(ErrorUtil::efatal, "Error in calling graph partitioner.");
708  }
709  }
710  else if (!m_parallel)
711  {
712  m_comm->GetRowComm()->Recv(0, part);
713  }
714 
715  // Create storage for this (and possibly other) process's partitions.
716  i = 0;
717 
718  if (!m_parallel)
719  {
720  // Populate subgraph(s) for each rank.
721  for (boost::tie(vertit, vertit_end) = boost::vertices(m_graph);
722  vertit != vertit_end; ++vertit, ++i)
723  {
724  m_localPartition[part[i]].push_back(m_graph[*vertit].id);
725  }
726  }
727  else
728  {
729  // Figure out how many vertices we're going to get from each processor.
730  int nproc = m_comm->GetSize(), rank = m_comm->GetRank();
731  std::vector<int> numToSend(nproc, 0), numToRecv(nproc);
732  std::map<int, std::vector<int>> procMap;
733 
734  for (boost::tie(vertit, vertit_end) = boost::vertices(m_graph);
735  vertit != vertit_end && i < nLocal; ++vertit, ++i)
736  {
737  int toProc = part[i];
738  numToSend[toProc]++;
739  procMap[toProc].push_back(m_graph[*vertit].id);
740  }
741 
742  m_comm->AlltoAll(numToSend, numToRecv);
743 
744  // Build offsets for all-to-all communication
745  std::vector<int> sendOffsetMap(nproc), recvOffsetMap(nproc);
746 
747  sendOffsetMap[0] = 0;
748  recvOffsetMap[0] = 0;
749  for (int i = 1; i < nproc; ++i)
750  {
751  sendOffsetMap[i] = sendOffsetMap[i - 1] + numToSend[i - 1];
752  recvOffsetMap[i] = recvOffsetMap[i - 1] + numToRecv[i - 1];
753  }
754 
755  // Build data to send
756  int totalSend = Vmath::Vsum(nproc, &numToSend[0], 1);
757  int totalRecv = Vmath::Vsum(nproc, &numToRecv[0], 1);
758 
759  std::vector<int> sendData(totalSend), recvData(totalRecv);
760 
761  int cnt = 0;
762  for (auto &verts : procMap)
763  {
764  for (auto &vert : verts.second)
765  {
766  sendData[cnt++] = vert;
767  }
768  }
769 
770  // Send ID map to processors
771  m_comm->AlltoAllv(sendData, numToSend, sendOffsetMap, recvData,
772  numToRecv, recvOffsetMap);
773 
774  // Finally, populate m_localPartition for this processor. Could contain
775  // duplicates so erase those first.
776  std::unordered_set<int> uniqueIDs;
777  for (auto &id : recvData)
778  {
779  uniqueIDs.insert(id);
780  }
781 
782  m_localPartition[rank].insert(m_localPartition[rank].begin(),
783  uniqueIDs.begin(), uniqueIDs.end());
784  }
785 
786  // If the overlapping option is set (for post-processing purposes),
787  // add vertices that correspond to the neighbouring elements.
788  if (overlapping)
789  {
790  ASSERTL0(!m_parallel, "Overlapping partitioning not supported in "
791  "parallel execution");
792 
793  for (boost::tie(vertit, vertit_end) = boost::vertices(m_graph);
794  vertit != vertit_end; ++vertit)
795  {
796  for (boost::tie(adjvertit, adjvertit_end) =
797  boost::adjacent_vertices(*vertit, m_graph);
798  adjvertit != adjvertit_end; ++adjvertit)
799  {
800  if (part[*adjvertit] != part[*vertit])
801  {
802  m_localPartition[part[*vertit]].push_back(
803  m_graph[*adjvertit].id);
804  }
805  }
806  }
807  }
808 }
809 
811 {
812  unsigned int i = 0;
813  unsigned int cnt = 0;
814  bool valid = true;
815 
816  // Check that every process has at least one element assigned
817  for (i = 0; i < nParts; ++i)
818  {
819  cnt = std::count(pPart.begin(), pPart.end(), i);
820  if (cnt == 0)
821  {
822  valid = false;
823  }
824  }
825 
826  // If the graph partitioner produced an invalid partition, repartition
827  // naively. Elements are assigned to processes in a round-robin fashion.
828  // It is assumed that graph partitioner failure only occurs when the number
829  // of elements is approx. the number of processes, so this approach should
830  // not be too inefficient communication-wise.
831  if (!valid)
832  {
833  for (i = 0; i < pPart.size(); ++i)
834  {
835  pPart[i] = i % nParts;
836  }
837  }
838 }
839 
840 void MeshPartition::GetElementIDs(const int procid,
841  std::vector<unsigned int> &elmtid)
842 {
843  BoostVertexIterator vertit, vertit_end;
844 
845  auto it = m_localPartition.find(procid);
846 
847  ASSERTL0(it != m_localPartition.end(), "Unable to find local partition");
848 
849  elmtid = m_localPartition[procid];
850 }
851 
853  bool bndWeight, int na, int nb,
854  int nc)
855 {
856  int weight = 0;
857 
858  switch (elmtType)
859  {
861  weight = bndWeight
863  na, nb, nc)
865  na, nb, nc);
866  break;
868  weight =
869  bndWeight
871  na, nb, nc)
873  na, nb, nc);
874  break;
876  weight = bndWeight
878  na, nb, nc)
880  na, nb, nc);
881  break;
883  weight = bndWeight
885  na, nb, nc)
887  na, nb, nc);
888  break;
890  weight =
891  bndWeight
893  nb)
895  nb);
896  break;
898  weight =
899  bndWeight
901  nb)
903  break;
905  weight =
906  bndWeight
909  break;
911  weight = 1;
912  break;
913  default:
914  break;
915  }
916 
917  return weight;
918 }
919 
920 /**
921  * Calculate the number of modes needed for communication when
922  * in partition boundary, to be used as weighting for edges.
923  * Since we do not know exactly which face this refers to, assume
924  * the max order and quad face (for prisms) as arbitrary choices
925  */
927  int nb, int nc)
928 {
929  int weight = 0;
930  int n = std::max(na, std::max(nb, nc));
931  switch (elmtType)
932  {
935  break;
938  break;
941  break;
944  break;
947  weight = n;
948  break;
950  weight = 1;
951  break;
952  default:
953  break;
954  }
955 
956  return weight;
957 }
958 } // namespace SpatialDomains
959 } // namespace Nektar
#define ASSERTL0(condition, msg)
Definition: ErrorUtil.hpp:215
#define NEKERROR(type, msg)
Assert Level 0 – Fundamental assert which is used whether in FULLDEBUG, DEBUG or OPT compilation mode...
Definition: ErrorUtil.hpp:209
#define SPATIAL_DOMAINS_EXPORT
static const std::string GetFileType(const std::string &filename, CommSharedPtr comm)
Determine file type of given input file.
Definition: FieldIO.cpp:97
Provides a generic Factory class.
Definition: NekFactory.hpp:105
tBaseSharedPtr CreateInstance(tKey idKey, tParam... args)
Create an instance of the class referred to by idKey.
Definition: NekFactory.hpp:144
static bool GenerateVector(const std::string &str, std::vector< T > &out)
Takes a comma-separated string and converts it to entries in a vector.
Definition: ParseUtils.cpp:131
static bool GenerateSeqVector(const std::string &str, std::vector< unsigned int > &out)
Takes a comma-separated compressed string and converts it to entries in a vector.
Definition: ParseUtils.cpp:105
boost::graph_traits< BoostGraph >::vertex_iterator BoostVertexIterator
virtual void v_PartitionGraphImpl(int &nVerts, int &nVertConds, Nektar::Array< Nektar::OneD, int > &xadj, Nektar::Array< Nektar::OneD, int > &adjcy, Nektar::Array< Nektar::OneD, int > &vertWgt, Nektar::Array< Nektar::OneD, int > &vertSize, Nektar::Array< Nektar::OneD, int > &edgeWgt, int &nparts, int &volume, Nektar::Array< Nektar::OneD, int > &part)=0
void PartitionGraph(int nParts, bool overlapping=false)
Partition the graph.
std::map< int, NummodesPerField > m_expansions
boost::graph_traits< BoostGraph >::adjacency_iterator BoostAdjacencyIterator
std::map< int, MultiWeight > m_edgeWeights
std::map< std::string, int > m_fieldNameToId
std::map< int, MeshEntity > m_ghostElmts
LibUtilities::SessionReaderSharedPtr m_session
std::map< int, MultiWeight > m_vertBndWeights
std::map< int, MultiWeight > m_vertWeights
void PrintPartInfo(std::ostream &out)
void GetElementIDs(const int procid, std::vector< unsigned int > &tmp)
boost::graph_traits< BoostGraph >::edge_descriptor BoostEdge
std::map< int, std::vector< unsigned int > > m_localPartition
int CalculateElementWeight(LibUtilities::ShapeType elmtType, bool bndWeight, int na, int nb, int nc)
std::map< std::string, NumModes > NummodesPerField
int CalculateEdgeWeight(LibUtilities::ShapeType elmtType, int na, int nb, int nc)
void PartitionMesh(int nParts, bool shared=false, bool overlapping=false, int nLocal=0)
std::map< int, MeshEntity > m_elements
MeshPartition(const LibUtilities::SessionReaderSharedPtr session, LibUtilities::CommSharedPtr comm, int meshDim, std::map< int, MeshEntity > element, CompositeDescriptor compMap)
LibUtilities::CommSharedPtr m_comm
void CheckPartitions(int nParts, Array< OneD, int > &pPart)
std::map< int, LibUtilities::ShapeType > m_shape
int getNumberOfCoefficients(int Na, int Nb, int Nc)
Definition: ShapeType.hpp:158
int getNumberOfBndCoefficients(int Na, int Nb, int Nc)
Definition: ShapeType.hpp:166
int getNumberOfCoefficients(int Na, int Nb, int Nc)
Definition: ShapeType.hpp:284
int getNumberOfBndCoefficients(int Na, int Nb, int Nc)
Definition: ShapeType.hpp:295
int getNumberOfCoefficients(int Na, int Nb, int Nc)
Definition: ShapeType.hpp:237
int getNumberOfBndCoefficients(int Na, int Nb, int Nc)
Definition: ShapeType.hpp:264
int getNumberOfCoefficients(int Na, int Nb)
Definition: ShapeType.hpp:138
int getNumberOfBndCoefficients(int Na, int Nb)
Definition: ShapeType.hpp:148
int getNumberOfBndCoefficients(int Na, int Nb, int Nc)
Definition: ShapeType.hpp:215
int getNumberOfCoefficients(int Na, int Nb, int Nc)
Definition: ShapeType.hpp:192
int getNumberOfCoefficients(int Na, int Nb)
Definition: ShapeType.hpp:114
int getNumberOfBndCoefficients(int Na, int Nb)
Definition: ShapeType.hpp:126
std::shared_ptr< FieldIO > FieldIOSharedPtr
Definition: FieldIO.h:327
std::shared_ptr< SessionReader > SessionReaderSharedPtr
FieldIOFactory & GetFieldIOFactory()
Returns the FieldIO factory.
Definition: FieldIO.cpp:72
CommFactory & GetCommFactory()
std::shared_ptr< Comm > CommSharedPtr
Pointer to a Communicator object.
Definition: Comm.h:54
std::map< int, std::pair< LibUtilities::ShapeType, std::vector< int > > > CompositeDescriptor
Definition: MeshGraph.h:63
MeshPartitionFactory & GetMeshPartitionFactory()
The above copyright notice and this permission notice shall be included.
Definition: CoupledSolver.h:2
T Vsum(int n, const T *x, const int incx)
Subtract return sum(x)
Definition: Vmath.cpp:895