NEMoSys  0.63.0
A modular, extensible resource with robust automated mesh generation, mesh quality analysis, adaptive mesh refinement, and data transfer between arbitrary meshes.
meshPartitioner.C
Go to the documentation of this file.
1 /*******************************************************************************
2 * Promesh *
3 * Copyright (C) 2022, IllinoisRocstar LLC. All rights reserved. *
4 * *
5 * Promesh is the property of IllinoisRocstar LLC. *
6 * *
7 * IllinoisRocstar LLC *
8 * Champaign, IL *
9 * www.illinoisrocstar.com *
10 * promesh@illinoisrocstar.com *
11 *******************************************************************************/
12 /*******************************************************************************
13 * This file is part of Promesh *
14 * *
15 * This version of Promesh is free software: you can redistribute it and/or *
16 * modify it under the terms of the GNU Lesser General Public License as *
17 * published by the Free Software Foundation, either version 3 of the License, *
18 * or (at your option) any later version. *
19 * *
20 * Promesh is distributed in the hope that it will be useful, but WITHOUT ANY *
21 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS *
22 * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more *
23 * details. *
24 * *
25 * You should have received a copy of the GNU Lesser General Public License *
26 * along with this program. If not, see <https://www.gnu.org/licenses/>. *
27 * *
28 *******************************************************************************/
29 /* implementation of mesh partition class(es) */
30 
32 
33 /* disable -- AEG
34 #include "IO/cgnsAnalyzer.H"
35 */
36 #include "Mesh/meshBase.H"
37 
38 #include <vtkCellTypes.h>
39 
40 /* Implementation of meshPartition class */
41 //meshPartition::meshPartition(int pidx, std::vector<int> glbNdePartedIdx, std::vector<int> glbElmPartedIdx)
43  const std::vector<int> &glbElmPartedIdx,
44  const std::vector<int> &glbElmConn,
45  MeshType_t inMshType)
46 {
47  mshType = inMshType;
48  pIdx = pidx;
49  nNde = 0;
50  nElm = 0;
52  nNdeElm = 4;
53  else if (mshType == MeshType_t::MESH_TRI_3)
54  nNdeElm = 3;
55  // finding partition elements
56  int glbElmIdx = 1;
57  for (int ie : glbElmPartedIdx)
58  {
59  if (ie == pIdx)
60  {
61  nElm++;
62  elmIdxGlobToPart[glbElmIdx] = nElm;
63  elmIdxPartToGlob[nElm] = glbElmIdx;
64  globElmIdx.push_back(glbElmIdx);
65  //std::cout << "My element = " << glbElmIdx << std::endl;
66  // attaching nodes of this element to the current partition
67  for (int iNde = 0; iNde < nNdeElm; iNde++)
68  {
69  int glbNdeIdx = glbElmConn[(glbElmIdx - 1) * nNdeElm + iNde];
70  auto it = ndeIdxGlobToPart.find(glbNdeIdx);
71  int partNdeIdx;
72  if (it == ndeIdxGlobToPart.end())
73  {
74  nNde++;
75  partNdeIdx = nNde;
76  ndeIdxGlobToPart[glbNdeIdx] = nNde;
77  ndeIdxPartToGlob[nNde] = glbNdeIdx;
78  globNdeIdx.push_back(glbNdeIdx);
79  //std::cout << "partition " << pIdx
80  // << " global node id " << glbNdeIdx
81  // << " is local " << nNde
82  // << std::endl;
83  }
84  else
85  {
86  partNdeIdx = it->second;
87  }
88  partElmConn.push_back(partNdeIdx);
89  }
90  }
91  glbElmIdx++;
92  }
93  //std::cout << "Min connectivity passed to partitioner = "
94  // << *std::min_element(glbElmConn.begin(), glbElmConn.end())
95  // << std::endl;
96 
97  // Debug information
98  //std::cout << "I am partition " << pidx << std::endl
99  // << " cycled on " << glbElmIdx - 1 << " global elements "
100  // << std::endl
101  // << " my min global element idx is "
102  // << *std::min_element(globElmIdx.begin(), globElmIdx.end())
103  // << std::endl
104  // << " my max global element idx is "
105  // << *std::max_element(globElmIdx.begin(), globElmIdx.end())
106  // << std::endl
107  // << " my nNde = " << nNde << " my nElm = " << nElm << std::endl;
108 }
109 
110 
111 std::vector<double> meshPartition::getCrds(const std::vector<double> &crds) const
112 {
113  std::vector<double> x;
114  for (int in : globNdeIdx)
115  x.push_back(crds[in - 1]);
116  return x;
117 }
118 
119 
120 std::vector<double> meshPartition::getElmSlns(const std::vector<double> &slns) const
121 {
122  std::vector<double> x;
123  for (int ie : globElmIdx)
124  x.push_back(slns[ie - 1]);
125  return x;
126 }
127 
128 
129 std::vector<double>
130 meshPartition::getElmSlnsVec(const std::vector<double> &slns, int nComp) const
131 {
132  std::vector<double> x;
133  for (int ie : globElmIdx)
134  for (int iComp = 0; iComp < nComp; iComp++)
135  x.push_back(slns[(ie - 1) * nComp + iComp]);
136  return x;
137 }
138 
139 
140 /* Implementation of partitioner class */
141 /* disable -- AEG
142 meshPartitioner::meshPartitioner(cgnsAnalyzer *inCg)
143 {
144  nNde = inCg->getNVertex();
145  nElm = inCg->getNElement();
146  elmConnVec = inCg->getElementConnectivity(-1);
147  elmConn.insert(elmConn.begin(), elmConnVec.begin(), elmConnVec.end());
148  // 0-indexing connectivity
149  for (int &it : elmConn)
150  it = it - 1;
151  nPart = 0;
152  // converting between CGNS to local type
153  switch (inCg->getElementType())
154  {
155  case CGNS_ENUMV(TETRA_4):
156  meshType = MESH_TETRA_4;
157  break;
158  case CGNS_ENUMV(TRI_3):
159  meshType = MESH_TRI_3;
160  break;
161  default:
162  std::cerr << "Unknown element type!" << std::endl;
163  break;
164  }
165 }
166 */
167 
168 
170 {
171  vtkSmartPointer<vtkCellTypes> celltypes = vtkSmartPointer<vtkCellTypes>::New();
172  inMB->getDataSet()->GetCellTypes(celltypes);
173  for (int i = 0; i < celltypes->GetNumberOfTypes(); ++i)
174  {
175  if (!(celltypes->IsType(VTK_TETRA)) && !(celltypes->IsType(VTK_TRIANGLE))) {
176  std::cerr
177  << "Partitioner works for 4-node tet and 3-node tri elements only."
178  << std::endl;
179  exit(-1);
180  }
181  }
182  nNde = inMB->getNumberOfPoints();
183  // FIXME: METIS uses int for vertex indices. NEMoSys uses nemId_t (= size_t).
184  // This is a narrowing conversion!
185  std::vector<nemId_t> elmConnVec_nemId_t = inMB->getConnectivities();
186  elmConnVec = std::vector<int>(elmConnVec_nemId_t.begin(),
187  elmConnVec_nemId_t.end());
188  elmConn = elmConnVec;
189  // 1 based index for elmConnVec, 0 based for elmConn
190  for (int &i : elmConnVec)
191  {
192  i = i + 1;
193  }
194  nElm = inMB->getNumberOfCells();
195  if (celltypes->IsType(VTK_TETRA))
196  meshType = MeshType_t::MESH_TETRA_4;
197  else if (celltypes->IsType(VTK_TRIANGLE))
198  meshType = MeshType_t::MESH_TRI_3;
199  else {
200  std::cerr << "Mesh with unsupported element types." << std::endl;
201  exit(-1);
202  }
203  nPart = 0;
204  std::cout << " ------------------- Partitioner Stats ---------------------\n";
205  std::cout << "Mesh type : "
206  << (meshType == MeshType_t::MESH_TRI_3 ? "Triangular"
207  : "Tetrahedral")
208  << "\n";
209  std::cout << "Number of nodes = " << nNde << "\n"
210  << "Number of elements = " << nElm << "\n";
211  std::cout << "Size of elmConn = " << elmConnVec.size() << "\n";
212  std::cout << "Min connectivity passed to partitioner = "
213  << *std::min_element(elmConnVec.begin(), elmConnVec.end()) << "\n";
214  std::cout << "Max connectivity passed to partitioner = "
215  << *std::max_element(elmConnVec.begin(), elmConnVec.end()) << "\n";
216  std::cout << " ----------------------------------------------------------"
217  << std::endl;
218 }
219 
220 #ifdef HAVE_GMSH
221 meshPartitioner::meshPartitioner(MAd::pMesh inMesh)
222 {
223  // only implemented for TETRA_4 elements
224  if (inMesh->nbQuads != 0 || inMesh->nbHexes != 0 || inMesh->nbPrisms != 0)
225  {
226  std::cerr << "Partitioner works 4-node tet and 3-node tri elements only."
227  << std::endl;
228  exit(-1);
229  }
230 
231  // during adaptation a series of new nodes will be created but
232  // MAdLib does not destroy old stand alone points so have to use
233  // maxId instead of number of nodes.
234  nNde = MAd::M_numVertices(inMesh);
235  elmConnVec = MAd::M_getConnectivities(inMesh);
236  elmConn.insert(elmConn.begin(), elmConnVec.begin(), elmConnVec.end());
237  // 0-indexing connectivity
238  for (int &it : elmConn)
239  it = it - 1;
240  if (M_numTets(inMesh) > 0)
241  {
242  nElm = MAd::M_numRegions(inMesh);
243  meshType = MeshType_t::MESH_TETRA_4;
244  }
245  else if (M_numTriangles(inMesh) > 0)
246  {
247  nElm = MAd::M_numTriangles(inMesh);
248  meshType = MeshType_t::MESH_TRI_3;
249  }
250  else
251  {
252  std::cerr << "Mesh with unsupported element types." << std::endl;
253  exit(-1);
254  }
255  nPart = 0;
256  std::cout << " ------------------- Partitioner Stats ---------------------\n";
257  std::cout << "Mesh type : " << (meshType == MeshType_t::MESH_TRI_3 ? "Triangular" : "Tetrahedral") << "\n";
258  std::cout << "Number of nodes = " << nNde << "\n"
259  << "Number of elements = " << nElm << "\n";
260  std::cout << "Size of elmConn = " << elmConnVec.size() << "\n";
261  std::cout << "Min connectivity passed to partitioner = "
262  << *std::min_element(elmConnVec.begin(), elmConnVec.end()) << "\n";
263  std::cout << "Max connectivity passed to partitioner = "
264  << *std::max_element(elmConnVec.begin(), elmConnVec.end()) << "\n";
265  std::cout << " -----------------------------------------------------------"
266  << std::endl;
267 }
268 #endif
269 
270 int meshPartitioner::partition(int nPartition)
271 {
272  setNPartition(nPartition);
273  return partition();
274 }
275 
277 {
278  // check
279  if (nPart == 0)
280  {
281  std::cerr << "Number of partitions must be set." << std::endl;
282  exit(-1);
283  }
284 
285  std::cout << "Partitioning the mesh." << std::endl;
286 #ifdef HAVE_METIS
287  // prepare metis datastructs
288  std::vector<idx_t> eptr(nElm + 1);
289  idx_t objval = 0;
290  epart.resize(nElm, 0);
291  npart.resize(nNde, 0);
292  int ncommon = 1;
293  switch (meshType)
294  {
296  eptr[0] = 0;
297  for (int iElm = 1; iElm <= nElm; iElm++)
298  eptr[iElm] = iElm * 4;
299  ncommon = 3;
300  break;
302  eptr[0] = 0;
303  for (int iElm = 1; iElm <= nElm; iElm++)
304  eptr[iElm] = iElm * 3;
305  ncommon = 2;
306  break;
307  default:
308  std::cerr << "Unknown or unimplemented element type." << std::endl;
309  }
310  // setting options (some default values, should be tailored)
311  int res;
312 
313  METIS_SetDefaultOptions(options);
314  options[METIS_OPTION_NUMBERING] = 0; // 0-based index
315  options[METIS_OPTION_CONTIG] = 1; // try for contiguous partitions
316  options[METIS_OPTION_PTYPE] = METIS_PTYPE_KWAY; // multilevel k-way partitioning
317  //options[METIS_OPTION_PTYPE] = METIS_PTYPE_RB; // multilevel recursive bisectioning
318  options[METIS_OPTION_OBJTYPE] = METIS_OBJTYPE_VOL; // minimize edge cut METIS_OBJTYPE_VOL(comm vol)
319  //options[METIS_OPTION_OBJTYPE] = METIS_OBJTYPE_CUT;
320  options[METIS_OPTION_UFACTOR] = 1; // load imbalance of 1.001
321  //options[METIS_OPTION_CTYPE] = METIS_CTYPE_SHEM; // sorted heavy-edge matching
322 
323  // To try to match METIS in Rocstar partitioner, the options can be changed to:
324  // - Add SHEM method: options[METIS_OPTION_CTYPE] = METIS_CTYPE_SHEM;
325  // - Replace KWAY with RB: options[METIS_OPTION_PTYPE] = METIS_PTYPE_RB
326  // - Replace VOL with CUT: options[METIS_OPTION_OBJTYPE] = METIS_OBJTYPE_CUT
327 
328  //options[METIS_OPTION_DBGLVL] = METIS_DBG_INFO | METIS_DBG_TIME |
329  // METIS_DBG_CONNINFO | METIS_DBG_CONTIGINFO;
330  res = METIS_OK;
331 
332  /*
333  // DEBUG: checking equivalent graph of the mesh
334  std::cout << "METIS: Calculating mesh dual graph...." << std::endl;
335  idx_t *xadj, *adjncy;
336  idx_t numflag = 0;
337  res = METIS_MeshToDual(&nElm, &nNde, &eptr[0], &elmConn[0], &ncommon,
338  &numflag, &xadj, &adjncy);
339  std::ofstream graphFile;
340  graphFile.open("dualGraph.txt");
341  for (int iElm = 0; iElm < nElm; iElm++)
342  {
343  graphFile << " Elm Idx " << iElm + 1 << " -> ";
344  for (int iAdj = xadj[iElm]; iAdj < xadj[iElm + 1]; iAdj++)
345  graphFile << adjncy[iAdj] << " ";
346  graphFile << " *** " << std::endl;
347  }
348  graphFile.close();
349  std::cout << "Dual graph for the mesh is written to -> dualGraph.txt "
350  << std::endl;
351  */
352 
353  // calling metis partioner
354  if (nPart > 1)
355  {
356  /*
357  // DEBUG Information
358  std::cout << "Sending data to METIS..." << std::endl;
359  std::cout << "nElm = " << nElm << std::endl;
360  std::cout << "nNde = " << nNde << std::endl;
361  std::cout << "eptr[0] = " << eptr[0] << std::endl;
362  std::cout << "eptr[end] = " << eptr[nElm] << std::endl;
363  std::cout << "eind = " << elmConn[0] << std::endl;
364  */
365 
366 // res = METIS_PartMeshNodal(&nElm, &nNde, &eptr[0], &elmConn[0], nullptr,
367 // nullptr, &nPart, nullptr, options, &objval,
368 // &epart[0], &npart[0]);
369  res = METIS_PartMeshDual(&nElm, &nNde, &eptr[0], &elmConn[0], nullptr,
370  nullptr, &ncommon, &nPart, nullptr, options,
371  &objval, &epart[0], &npart[0]);
372 
373  std::cout << "Received data from METIS" << std::endl;
374  }
375  // output partitioning results
376  // check success
377  if (res == METIS_OK)
378  {
379  std::cout << "Successfully partitioned the mesh." << std::endl;
380  // removing the first member of the npart as default
381  // index starts from 1
382  npart.erase(npart.begin());
383  buildPartitions();
384  return 0;
385  }
386  else
387  {
388  std::cerr << "Failed to partition with error code " << res << std::endl;
389  return 1;
390  }
391 #else
392  std::cerr
393  << "METIS is not enabled during build. Build NEMoSys with ENABLE_METIS to use this method."
394  << std::endl;
395  exit(1);
396 #endif
397 }
398 
399 
400 std::vector<double> meshPartitioner::getPartedNde() const
401 {
402  std::vector<double> ndeParted(npart.begin(), npart.end());
403  return ndeParted;
404 }
405 
406 std::vector<double> meshPartitioner::getPartedElm() const
407 {
408  std::vector<double> elmParted(epart.begin(), epart.end());
409  return elmParted;
410 }
411 
412 
413 void meshPartitioner::setPartedElm(const std::vector<double> &prtElm)
414 {
415  int minp, maxp;
416  minp = (int)*std::min_element(prtElm.begin(), prtElm.end());
417  maxp = (int)*std::max_element(prtElm.begin(), prtElm.end());
418  setNPartition(maxp - minp + 1);
419  epart.insert(epart.begin(), prtElm.begin(), prtElm.end());
420  buildPartitions();
421 }
422 
423 
425 {
426  for (int iPart = 0; iPart < nPart; iPart++)
427  {
428  auto *newPart = new meshPartition(iPart, epart, elmConnVec, meshType);
429  meshParts.push_back(newPart);
430  }
431 }
432 
433 
434 std::map<int, int> meshPartitioner::getPartToGlobNodeMap(int iPart) const
435 {
436  if (iPart > meshParts.size())
437  {
438  std::cerr << "requested partition number exceeds available partitions"
439  << std::endl;
440  exit(1);
441  }
442  return meshParts[iPart]->getPartToGlobNodeMap();
443 }
444 
445 std::map<int, int> meshPartitioner::getPartToGlobElmMap(int iPart) const
446 {
447  if (iPart > meshParts.size())
448  {
449  std::cerr << "requested partition number exceeds available partitions"
450  << std::endl;
451  exit(1);
452  }
453  return meshParts[iPart]->getPartToGlobElmMap();
454 }
std::vector< double > getPartedElm() const
std::map< int, int > getPartToGlobNodeMap(int iPart) const
std::map< int, int > getPartToGlobElmMap(int iPart) const
meshPartitioner(int nNde, int nElm, const std::vector< int > &elemConn, MeshType_t meshType)
A brief description of meshBase.
Definition: meshBase.H:64
geoMeshBase * New(MeshType meshType)
Create a new mesh object.
meshPartition(int pidx, const std::vector< int > &glbElmPartedIdx, const std::vector< int > &glbElmConn, MeshType_t inMshType)
std::vector< int > globNdeIdx
std::map< int, int > ndeIdxPartToGlob
std::vector< double > getElmSlns(const std::vector< double > &slns) const
MeshType_t
vtkSmartPointer< vtkDataSet > getDataSet() const
get this meshes&#39; dataSet
Definition: meshBase.H:308
virtual std::vector< nemId_t > getConnectivities() const =0
get connectivities.
std::vector< double > getElmSlnsVec(const std::vector< double > &slns, int nComp) const
nemId_t getNumberOfPoints() const
return the number of points
Definition: meshBase.H:545
MeshType_t mshType
std::map< int, int > ndeIdxGlobToPart
std::vector< int > globElmIdx
std::map< int, int > elmIdxGlobToPart
std::vector< int > partElmConn
nemId_t getNumberOfCells() const
return the number of cells
Definition: meshBase.H:550
std::vector< double > getCrds(const std::vector< double > &crds) const
std::vector< double > getPartedNde() const
void setPartedElm(const std::vector< double > &prtElm)
std::map< int, int > elmIdxPartToGlob