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