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