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