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
42
43#include <iomanip>
44#include <iostream>
45#include <map>
46#include <vector>
47
48#include <tinyxml.h>
49
54
56
57#include <boost/algorithm/string.hpp>
58#include <boost/format.hpp>
59#include <boost/graph/adjacency_iterator.hpp>
60#include <boost/graph/adjacency_list.hpp>
61#include <boost/graph/detail/edge.hpp>
62
64{
65
67{
68 static MeshPartitionFactory instance;
69 return instance;
70}
71
73 LibUtilities::CommSharedPtr comm, int meshDim,
74 std::map<int, MeshEntity> element,
75 CompositeDescriptor compMap)
76 : m_session(session), m_comm(comm), m_dim(meshDim), m_numFields(0),
77 m_elements(element), m_compMap(compMap), m_fieldNameToId(),
78 m_weightingRequired(false), m_weightBnd(false), m_weightDofs(false),
79 m_parallel(false)
80{
81 // leave the meshpartition method of reading expansions and conditions
84
85 for (auto elIt = m_elements.cbegin(); elIt != m_elements.cend();)
86 {
87 if (elIt->second.ghost)
88 {
89 m_ghostElmts[elIt->first] = elIt->second;
90 elIt = m_elements.erase(elIt);
91 }
92 else
93 {
94 ++elIt;
95 }
96 }
97}
98
100{
101}
102
103void MeshPartition::PartitionMesh(int nParts, bool shared, bool overlapping,
104 [[maybe_unused]] int nLocal)
105{
106 ASSERTL0(m_parallel || m_elements.size() >= nParts,
107 "Too few elements for this many processes.");
108 m_shared = shared;
109
110 ASSERTL0(!m_parallel || shared,
111 "Parallel partitioning requires shared filesystem.");
112
114 {
116 }
117 CreateGraph();
118
119 PartitionGraph(nParts, overlapping);
120}
121
123{
124 // Find the Expansions tag
125 TiXmlElement *expansionTypes = m_session->GetElement("Nektar/Expansions");
126
127 // Use fine-level expansion for mesh partition (Parallel-in-Time)
129
130 // Find the Expansion type
131 TiXmlElement *expansion = expansionTypes->FirstChildElement();
132
133 std::string expType = expansion->Value();
134
135 // Expansiontypes will contain plenty of data, where relevant at this stage
136 // are composite ID(s) that this expansion type describes, nummodes and a
137 // list of fields that this expansion relates to. If this does not exist the
138 // variable is only set to "DefaultVar".
139 if (expType == "E")
140 {
141 while (expansion)
142 {
143 std::vector<unsigned int> composite;
144 std::vector<unsigned int> nummodes;
145 std::vector<std::string> fieldName;
146
147 const char *nModesStr = expansion->Attribute("NUMMODES");
148 ASSERTL0(nModesStr,
149 "NUMMODES was not defined in EXPANSION section of input");
150 std::string numModesStr = nModesStr;
151 bool valid =
152 ParseUtils::GenerateVector(numModesStr.c_str(), nummodes);
153 ASSERTL0(valid, "Unable to correctly parse the number of modes.");
154
155 if (nummodes.size() == 1)
156 {
157 for (int i = 1; i < m_dim; i++)
158 {
159 nummodes.push_back(nummodes[0]);
160 }
161 }
162 ASSERTL0(nummodes.size() == m_dim,
163 "Number of modes should match mesh dimension");
164
165 const char *fStr = expansion->Attribute("FIELDS");
166 if (fStr)
167 {
168 std::string fieldStr = fStr;
169 bool valid =
170 ParseUtils::GenerateVector(fieldStr.c_str(), fieldName);
171 ASSERTL0(valid, "Unable to correctly parse the field string in "
172 "ExpansionTypes.");
173
174 for (int i = 0; i < fieldName.size(); ++i)
175 {
176 if (m_fieldNameToId.count(fieldName[i]) == 0)
177 {
178 int k = m_fieldNameToId.size();
179 m_fieldNameToId[fieldName[i]] = k;
180 m_numFields++;
181 }
182 }
183 }
184 else
185 {
186 fieldName.push_back("DefaultVar");
187 int k = m_fieldNameToId.size();
188
189 if (m_fieldNameToId.count("DefaultVar") == 0)
190 {
191 ASSERTL0(
192 k == 0,
193 "Omitting field variables and explicitly listing "
194 "them in different ExpansionTypes is wrong practise");
195
196 m_fieldNameToId["DefaultVar"] = k;
197 m_numFields++;
198 }
199 }
200
201 std::string compositeStr = expansion->Attribute("COMPOSITE");
202 ASSERTL0(compositeStr.length() > 3,
203 "COMPOSITE must be specified in expansion definition");
204 int beg = compositeStr.find_first_of("[");
205 int end = compositeStr.find_first_of("]");
206 std::string compositeListStr =
207 compositeStr.substr(beg + 1, end - beg - 1);
208 bool parseGood = ParseUtils::GenerateSeqVector(
209 compositeListStr.c_str(), composite);
210 ASSERTL0(parseGood && !composite.empty(),
211 (std::string("Unable to read composite index range: ") +
212 compositeListStr)
213 .c_str());
214
215 // construct mapping (elmt id, field name) -> nummodes
216 for (int i = 0; i < composite.size(); ++i)
217 {
218 auto &shapeType = m_compMap[composite[i]].first;
219 auto &elmtIds = m_compMap[composite[i]].second;
220
221 for (int j = 0; j < fieldName.size(); j++)
222 {
223 for (auto &elid : elmtIds)
224 {
225 m_expansions[elid][fieldName[j]] = nummodes;
226 m_shape[elid] = shapeType;
227 }
228 }
229 }
230
231 expansion = expansion->NextSiblingElement("E");
232 }
233 }
234 else if (expType == "F")
235 {
236 ASSERTL0(expansion->Attribute("FILE"),
237 "Attribute FILE expected for type F expansion");
238 std::string filenameStr = expansion->Attribute("FILE");
239 ASSERTL0(!filenameStr.empty(),
240 "A filename must be specified for the FILE "
241 "attribute of expansion");
242
243 // Create fieldIO object to load file
244 // need a serial communicator to avoid problems with
245 // shared file system
248 std::string iofmt =
249 LibUtilities::FieldIO::GetFileType(filenameStr, comm);
252 iofmt, comm, m_session->GetSharedFilesystem());
253
254 // Load field definitions from file
255 std::vector<LibUtilities::FieldDefinitionsSharedPtr> fielddefs;
256 f->Import(filenameStr, fielddefs);
257
258 // Parse field definitions
259 for (int i = 0; i < fielddefs.size(); ++i)
260 {
261 // Name of fields
262 for (int j = 0; j < fielddefs[i]->m_fields.size(); ++j)
263 {
264 std::string fieldName = fielddefs[i]->m_fields[j];
265 if (m_fieldNameToId.count(fieldName) == 0)
266 {
267 int k = m_fieldNameToId.size();
268 m_fieldNameToId[fieldName] = k;
269 m_numFields++;
270 }
271 }
272 // Number of modes and shape for each element
273 int numHomoDir = fielddefs[i]->m_numHomogeneousDir;
274 int cnt = 0;
275 for (int j = 0; j < fielddefs[i]->m_elementIDs.size(); ++j)
276 {
277 int elid = fielddefs[i]->m_elementIDs[j];
278 std::vector<unsigned int> nummodes;
279 for (int k = 0; k < m_dim; k++)
280 {
281 nummodes.push_back(fielddefs[i]->m_numModes[cnt++]);
282 }
283 if (fielddefs[i]->m_uniOrder)
284 {
285 cnt = 0;
286 }
287 else
288 {
289 cnt += numHomoDir;
290 }
291 for (int k = 0; k < fielddefs[i]->m_fields.size(); k++)
292 {
293 std::string fieldName = fielddefs[i]->m_fields[k];
294 m_expansions[elid][fieldName] = nummodes;
295 }
296 m_shape[elid] = fielddefs[i]->m_shapeType;
297 }
298 }
299 }
300 else
301 {
302 ASSERTL0(false,
303 "Expansion type not defined or not supported at the moment");
304 }
305}
306
307void MeshPartition::PrintPartInfo(std::ostream &out)
308{
309 int nElmt = boost::num_vertices(m_graph);
310 int nPart = m_localPartition.size();
311
312 out << "# Partition information:" << std::endl;
313 out << "# No. elements : " << nElmt << std::endl;
314 out << "# No. partitions: " << nPart << std::endl;
315 out << "# ID nElmt nLocDof nBndDof" << std::endl;
316
317 BoostVertexIterator vertit, vertit_end;
318 std::vector<int> partElmtCount(nPart, 0);
319 std::vector<int> partLocCount(nPart, 0);
320 std::vector<int> partBndCount(nPart, 0);
321
322 std::map<int, int> elmtSizes;
323 std::map<int, int> elmtBndSizes;
324
325 for (std::map<int, NummodesPerField>::iterator expIt = m_expansions.begin();
326 expIt != m_expansions.end(); ++expIt)
327 {
328 int elid = expIt->first;
329 NummodesPerField npf = expIt->second;
330
331 for (NummodesPerField::iterator it = npf.begin(); it != npf.end(); ++it)
332 {
333 ASSERTL0(it->second.size() == m_dim,
334 " Number of directional"
335 " modes in expansion spec for element id = " +
336 std::to_string(elid) + " and field " + it->first +
337 " does not correspond to mesh dimension");
338
339 int na = it->second[0];
340 int nb = 0;
341 int nc = 0;
342 if (m_dim >= 2)
343 {
344 nb = it->second[1];
345 }
346 if (m_dim == 3)
347 {
348 nc = it->second[2];
349 }
350
351 elmtSizes[elid] =
352 CalculateElementWeight(m_shape[elid], false, na, nb, nc);
353 elmtBndSizes[elid] =
354 CalculateElementWeight(m_shape[elid], true, na, nb, nc);
355 }
356 }
357
358 for (boost::tie(vertit, vertit_end) = boost::vertices(m_graph);
359 vertit != vertit_end; ++vertit)
360 {
361 int partId = m_graph[*vertit].partition;
362 partElmtCount[partId]++;
363 partLocCount[partId] += elmtSizes[m_graph[*vertit].id];
364 partBndCount[partId] += elmtBndSizes[m_graph[*vertit].id];
365 }
366
367 for (int i = 0; i < nPart; ++i)
368 {
369 out << i << " " << partElmtCount[i] << " " << partLocCount[i] << " "
370 << partBndCount[i] << std::endl;
371 }
372}
373
375{
376 if (!m_session->DefinesElement("Nektar/Conditions/SolverInfo"))
377 {
378 // No SolverInfo = no change of default action to weight
379 // mesh graph.
380 return;
381 }
382
383 TiXmlElement *solverInfoElement =
384 m_session->GetElement("Nektar/Conditions/SolverInfo");
385
386 // Use fine-level solver info for mesh partition (Parallel-in-Time)
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 std::to_string(elid) + " and field " + it->first +
475 " does not correspond to mesh dimension");
476
477 int na = it->second[0];
478 int nb = 0;
479 int nc = 0;
480 if (m_dim >= 2)
481 {
482 nb = it->second[1];
483 }
484 if (m_dim == 3)
485 {
486 nc = it->second[2];
487 }
488
489 // Assume for parallel partitioning that this is just missing from
490 // our partition.
491 if (m_vertWeights.find(elid) == m_vertWeights.end())
492 {
493 continue;
494 }
495
496 m_vertWeights[elid][m_fieldNameToId[it->first]] =
497 CalculateElementWeight(m_shape[elid], false, na, nb, nc);
498 m_vertBndWeights[elid][m_fieldNameToId[it->first]] =
499 CalculateElementWeight(m_shape[elid], true, na, nb, nc);
500 m_edgeWeights[elid][m_fieldNameToId[it->first]] =
501 CalculateEdgeWeight(m_shape[elid], na, nb, nc);
502 }
503 } // for i
504}
505
507{
508 // Maps edge/face to first mesh element id.
509 // On locating second mesh element id, graph edge is created instead.
510 std::unordered_map<int, std::vector<int>> vGraphEdges;
511 int vcnt = 0;
512
513 for (auto &elmt : m_elements)
514 {
515 auto vert = boost::add_vertex(m_graph);
516 m_graph[vert].id = elmt.first;
517 m_graph[vert].partition = 0;
518
520 {
521 m_graph[vert].weight = m_vertWeights[elmt.second.origId];
522 m_graph[vert].bndWeight = m_vertBndWeights[elmt.second.origId];
523 m_graph[vert].edgeWeight = m_edgeWeights[elmt.second.origId];
524 }
525
526 // Process element entries and add graph edges
527 for (auto &eId : elmt.second.list)
528 {
529 // Look to see if we've examined this edge/face before
530 // If so, we've got both graph vertices so add edge
531 auto edgeIt = vGraphEdges.find(eId);
532 if (edgeIt != vGraphEdges.end())
533 {
534 for (auto &iId : edgeIt->second)
535 {
536 BoostEdge e = boost::add_edge(vcnt, iId, m_graph).first;
537 m_graph[e].id = vcnt;
538 }
539 vGraphEdges[eId].push_back(vcnt);
540 }
541 else
542 {
543 std::vector<int> Id;
544 Id.push_back(vcnt);
545 vGraphEdges[eId] = Id;
546 }
547 }
548
549 // Increment counter for graph vertex id.
550 ++vcnt;
551 }
552
553 // Now process ghost elements.
554 for (auto &ghost : m_ghostElmts)
555 {
556 auto vert = boost::add_vertex(m_graph);
557 m_graph[vert].id = ghost.first;
558 m_graph[vert].partition = -1;
559
560 for (auto &facet : ghost.second.list)
561 {
562 auto edgeIt = vGraphEdges.find(facet);
563 if (edgeIt != vGraphEdges.end())
564 {
565 for (auto &iId : edgeIt->second)
566 {
567 BoostEdge e = boost::add_edge(vcnt, iId, m_graph).first;
568 m_graph[e].id = vcnt;
569 }
570 vGraphEdges[facet].push_back(vcnt);
571 }
572 }
573
574 // Increment counter for graph vertex id.
575 ++vcnt;
576 }
577}
578
579/**
580 * @brief Partition the graph.
581 *
582 * This routine partitions the graph @p pGraph into @p nParts, producing
583 * subgraphs that are populated in @p pLocalPartition. If the @p
584 * overlapping option is set (which is used for post-processing
585 * purposes), the resulting partitions are extended to cover
586 * neighbouring elements by additional vertex on the dual graph, which
587 * produces overlapping partitions (i.e. the intersection of two
588 * connected partitions is non-empty).
589 *
590 * @param nParts Number of partitions.
591 * @param pLocalPartition Vector of sub-graphs representing each
592 * @param overlapping True if resulting partitions should overlap.
593 */
594void MeshPartition::PartitionGraph(int nParts, bool overlapping)
595{
596 int i;
597 int nGraphVerts = boost::num_vertices(m_graph);
598 int nGhost = m_ghostElmts.size();
599 int nLocal = nGraphVerts - nGhost;
600
601 int ncon = 1;
603 {
604 ncon = 2;
605 }
606
607 // Convert boost graph into CSR format
608 BoostVertexIterator vertit, vertit_end;
609 BoostAdjacencyIterator adjvertit, adjvertit_end;
610 Array<OneD, int> part(nGraphVerts, 0);
611
612 if (m_comm->GetRowComm()->TreatAsRankZero() || m_parallel)
613 {
614 int acnt = 0;
615 int vcnt = 0;
616 int nWeight = ncon * nLocal;
617
618 Array<OneD, int> xadj(nLocal + 1);
619 std::vector<int> adjncy_tmp, adjwgt_tmp;
620 Array<OneD, int> vwgt(nWeight, 1);
621 Array<OneD, int> vsize(nLocal, 1);
622
623 // Initialise starting point of adjacency array.
624 xadj[0] = 0;
625
626 for (boost::tie(vertit, vertit_end) = boost::vertices(m_graph);
627 vertit != vertit_end && vcnt < nLocal; ++vertit)
628 {
629 for (boost::tie(adjvertit, adjvertit_end) =
630 boost::adjacent_vertices(*vertit, m_graph);
631 adjvertit != adjvertit_end; ++adjvertit, ++acnt)
632 {
633 adjncy_tmp.push_back(m_graph[*adjvertit].id);
635 {
636 adjwgt_tmp.push_back(m_graph[*vertit].edgeWeight[0]);
637 }
638 else
639 {
640 adjwgt_tmp.push_back(1);
641 }
642 }
643
644 xadj[++vcnt] = acnt;
645
647 {
648 int ccnt = 0;
649 if (m_weightDofs)
650 {
651 vwgt[ncon * (vcnt - 1) + ccnt] = m_graph[*vertit].weight[0];
652 ccnt++;
653 }
654 if (m_weightBnd)
655 {
656 vwgt[ncon * (vcnt - 1) + ccnt] =
657 m_graph[*vertit].bndWeight[0];
658 }
659 }
660 }
661
662 Array<OneD, int> adjncy(adjncy_tmp.size(), &adjncy_tmp[0]);
663 Array<OneD, int> adjwgt(adjwgt_tmp.size(), &adjwgt_tmp[0]);
664
665 // Call partitioner to partition graph
666 int vol = 0;
667
668 try
669 {
670 //////////////////////////////////////////////////////
671 // On a cartesian communicator do mesh partition just on the first
672 // column
673 // so there is no doubt the partitions are all the same in all the
674 // columns
675 if (m_comm->GetColumnComm()->GetRank() == 0)
676 {
677 // Attempt partitioning.
678 v_PartitionGraphImpl(nLocal, ncon, xadj, adjncy, vwgt, vsize,
679 adjwgt, nParts, vol, part);
680
681 // Check the partitioner produced a valid partition and fix if
682 // not.
683 if (!m_parallel)
684 {
685 CheckPartitions(nParts, part);
686 }
687
688 if (!m_shared)
689 {
690 // distribute among columns
691 for (i = 1; i < m_comm->GetColumnComm()->GetSize(); ++i)
692 {
693 m_comm->GetColumnComm()->Send(i, part);
694 }
695 }
696 }
697 else
698 {
699 m_comm->GetColumnComm()->Recv(0, part);
700 }
701 }
702 catch (...)
703 {
704 NEKERROR(ErrorUtil::efatal, "Error in calling graph partitioner.");
705 }
706 }
707 else if (!m_parallel)
708 {
709 m_comm->GetRowComm()->Recv(0, part);
710 }
711
712 // Create storage for this (and possibly other) process's partitions.
713 i = 0;
714
715 if (!m_parallel)
716 {
717 // Populate subgraph(s) for each rank.
718 for (boost::tie(vertit, vertit_end) = boost::vertices(m_graph);
719 vertit != vertit_end; ++vertit, ++i)
720 {
721 m_localPartition[part[i]].push_back(m_graph[*vertit].id);
722 }
723 }
724 else
725 {
726 // Figure out how many vertices we're going to get from each processor.
727 int nproc = m_comm->GetSpaceComm()->GetSize(),
728 rank = m_comm->GetSpaceComm()->GetRank();
729 std::vector<int> numToSend(nproc, 0), numToRecv(nproc);
730 std::map<int, std::vector<int>> procMap;
731
732 for (boost::tie(vertit, vertit_end) = boost::vertices(m_graph);
733 vertit != vertit_end && i < nLocal; ++vertit, ++i)
734 {
735 int toProc = part[i];
736 numToSend[toProc]++;
737 procMap[toProc].push_back(m_graph[*vertit].id);
738 }
739
740 m_comm->GetSpaceComm()->AlltoAll(numToSend, numToRecv);
741
742 // Build offsets for all-to-all communication
743 std::vector<int> sendOffsetMap(nproc), recvOffsetMap(nproc);
744
745 sendOffsetMap[0] = 0;
746 recvOffsetMap[0] = 0;
747 for (int i = 1; i < nproc; ++i)
748 {
749 sendOffsetMap[i] = sendOffsetMap[i - 1] + numToSend[i - 1];
750 recvOffsetMap[i] = recvOffsetMap[i - 1] + numToRecv[i - 1];
751 }
752
753 // Build data to send
754 int totalSend = Vmath::Vsum(nproc, &numToSend[0], 1);
755 int totalRecv = Vmath::Vsum(nproc, &numToRecv[0], 1);
756
757 std::vector<int> sendData(totalSend), recvData(totalRecv);
758
759 int cnt = 0;
760 for (auto &verts : procMap)
761 {
762 for (auto &vert : verts.second)
763 {
764 sendData[cnt++] = vert;
765 }
766 }
767
768 // Send ID map to processors
769 m_comm->GetSpaceComm()->AlltoAllv(sendData, numToSend, sendOffsetMap,
770 recvData, numToRecv, recvOffsetMap);
771
772 // Finally, populate m_localPartition for this processor. Could contain
773 // duplicates so erase those first.
774 std::unordered_set<int> uniqueIDs;
775 for (auto &id : recvData)
776 {
777 uniqueIDs.insert(id);
778 }
779
780 m_localPartition[rank].insert(m_localPartition[rank].begin(),
781 uniqueIDs.begin(), uniqueIDs.end());
782 }
783
784 // If the overlapping option is set (for post-processing purposes),
785 // add vertices that correspond to the neighbouring elements.
786 if (overlapping)
787 {
788 ASSERTL0(!m_parallel, "Overlapping partitioning not supported in "
789 "parallel execution");
790
791 for (boost::tie(vertit, vertit_end) = boost::vertices(m_graph);
792 vertit != vertit_end; ++vertit)
793 {
794 for (boost::tie(adjvertit, adjvertit_end) =
795 boost::adjacent_vertices(*vertit, m_graph);
796 adjvertit != adjvertit_end; ++adjvertit)
797 {
798 if (part[*adjvertit] != part[*vertit])
799 {
800 m_localPartition[part[*vertit]].push_back(
801 m_graph[*adjvertit].id);
802 }
803 }
804 }
805 }
806}
807
809{
810 unsigned int i = 0;
811 unsigned int cnt = 0;
812 bool valid = true;
813
814 // Check that every process has at least one element assigned
815 for (i = 0; i < nParts; ++i)
816 {
817 cnt = std::count(pPart.begin(), pPart.end(), i);
818 if (cnt == 0)
819 {
820 valid = false;
821 }
822 }
823
824 // If the graph partitioner produced an invalid partition, repartition
825 // naively. Elements are assigned to processes in a round-robin fashion.
826 // It is assumed that graph partitioner failure only occurs when the number
827 // of elements is approx. the number of processes, so this approach should
828 // not be too inefficient communication-wise.
829 if (!valid)
830 {
831 for (i = 0; i < pPart.size(); ++i)
832 {
833 pPart[i] = i % nParts;
834 }
835 }
836}
837
838void MeshPartition::GetElementIDs(const int procid,
839 std::vector<unsigned int> &elmtid)
840{
841 BoostVertexIterator vertit, vertit_end;
842
843 auto it = m_localPartition.find(procid);
844
845 ASSERTL0(it != m_localPartition.end(), "Unable to find local partition");
846
847 elmtid = m_localPartition[procid];
848}
849
851 bool bndWeight, int na, int nb,
852 int nc)
853{
854 int weight = 0;
855
856 switch (elmtType)
857 {
859 weight = bndWeight
861 na, nb, nc)
863 na, nb, nc);
864 break;
866 weight =
867 bndWeight
869 na, nb, nc)
871 na, nb, nc);
872 break;
874 weight = bndWeight
876 na, nb, nc)
878 na, nb, nc);
879 break;
881 weight = bndWeight
883 na, nb, nc)
885 na, nb, nc);
886 break;
888 weight =
889 bndWeight
891 nb)
893 nb);
894 break;
896 weight =
897 bndWeight
899 nb)
901 break;
903 weight =
904 bndWeight
907 break;
909 weight = 1;
910 break;
911 default:
912 break;
913 }
914
915 return weight;
916}
917
918/**
919 * Calculate the number of modes needed for communication when
920 * in partition boundary, to be used as weighting for edges.
921 * Since we do not know exactly which face this refers to, assume
922 * the max order and quad face (for prisms) as arbitrary choices
923 */
925 int nb, int nc)
926{
927 int weight = 0;
928 int n = std::max(na, std::max(nb, nc));
929 switch (elmtType)
930 {
933 break;
936 break;
939 break;
942 break;
945 weight = n;
946 break;
948 weight = 1;
949 break;
950 default:
951 break;
952 }
953
954 return weight;
955}
956} // namespace Nektar::SpatialDomains
#define ASSERTL0(condition, msg)
Definition: ErrorUtil.hpp:208
#define NEKERROR(type, msg)
Assert Level 0 – Fundamental assert which is used whether in FULLDEBUG, DEBUG or OPT compilation mode...
Definition: ErrorUtil.hpp:202
#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:95
Provides a generic Factory class.
Definition: NekFactory.hpp:104
tBaseSharedPtr CreateInstance(tKey idKey, tParam... args)
Create an instance of the class referred to by idKey.
Definition: NekFactory.hpp:143
static void GetXMLElementTimeLevel(TiXmlElement *&element, const size_t timeLevel, const bool enableCheck=true)
Get XML elment time level (Parallel-in-Time)
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:130
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:104
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:153
int getNumberOfBndCoefficients(int Na, int Nb, int Nc)
Definition: ShapeType.hpp:161
int getNumberOfCoefficients(int Na, int Nb, int Nc)
Definition: ShapeType.hpp:279
int getNumberOfBndCoefficients(int Na, int Nb, int Nc)
Definition: ShapeType.hpp:290
int getNumberOfCoefficients(int Na, int Nb, int Nc)
Definition: ShapeType.hpp:232
int getNumberOfBndCoefficients(int Na, int Nb, int Nc)
Definition: ShapeType.hpp:259
int getNumberOfCoefficients(int Na, int Nb)
Definition: ShapeType.hpp:133
int getNumberOfBndCoefficients(int Na, int Nb)
Definition: ShapeType.hpp:143
int getNumberOfBndCoefficients(int Na, int Nb, int Nc)
Definition: ShapeType.hpp:210
int getNumberOfCoefficients(int Na, int Nb, int Nc)
Definition: ShapeType.hpp:187
int getNumberOfCoefficients(int Na, int Nb)
Definition: ShapeType.hpp:109
int getNumberOfBndCoefficients(int Na, int Nb)
Definition: ShapeType.hpp:121
std::shared_ptr< FieldIO > FieldIOSharedPtr
Definition: FieldIO.h:322
std::shared_ptr< SessionReader > SessionReaderSharedPtr
FieldIOFactory & GetFieldIOFactory()
Returns the FieldIO factory.
Definition: FieldIO.cpp:70
CommFactory & GetCommFactory()
std::shared_ptr< Comm > CommSharedPtr
Pointer to a Communicator object.
Definition: Comm.h:55
std::map< int, std::pair< LibUtilities::ShapeType, std::vector< int > > > CompositeDescriptor
Definition: MeshGraph.h:61
MeshPartitionFactory & GetMeshPartitionFactory()
T Vsum(int n, const T *x, const int incx)
Subtract return sum(x)
Definition: Vmath.hpp:608