zoltan.C
Go to the documentation of this file.
1 /*---------------------------------------------------------------------------*\
2  ========= |
3  \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
4  \\ / O peration | Website: https://openfoam.org
5  \\ / A nd | Copyright (C) 2021-2024 OpenFOAM Foundation
6  \\/ M anipulation |
7 -------------------------------------------------------------------------------
8 License
9  This file is part of OpenFOAM.
10 
11  OpenFOAM is free software: you can redistribute it and/or modify it
12  under the terms of the GNU General Public License as published by
13  the Free Software Foundation, either version 3 of the License, or
14  (at your option) any later version.
15 
16  OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
17  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
18  FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
19  for more details.
20 
21  You should have received a copy of the GNU General Public License
22  along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
23 
24 \*---------------------------------------------------------------------------*/
25 
26 #include "zoltan.H"
27 #include "globalIndex.H"
28 #include "PstreamGlobals.H"
29 #include "Tuple3.H"
31 
32 #include "sigFpe.H"
33 #ifdef LINUX_GNUC
34  #ifndef __USE_GNU
35  #define __USE_GNU
36  #endif
37  #include <fenv.h>
38 #endif
39 
40 #include "zoltan.h"
41 
42 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
43 
44 namespace Foam
45 {
46 namespace decompositionMethods
47 {
49 
51  (
53  zoltan,
54  distributor
55  );
56 }
57 }
58 
59 
60 // * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
61 
62 static int get_number_of_vertices(void *data, int *ierr)
63 {
64  const Foam::pointField& points =
65  *static_cast<const Foam::pointField*>(data);
66 
67  *ierr = ZOLTAN_OK;
68 
69  return points.size();
70 }
71 
72 
73 static void get_vertex_list
74 (
75  void* data,
76  int nGID,
77  int nLID,
78  ZOLTAN_ID_PTR globalIDs,
79  ZOLTAN_ID_PTR localIDs,
80  int wgt_dim,
81  float* obj_wgts,
82  int* ierr
83 )
84 {
85  const Foam::Tuple3
86  <
87  const Foam::pointField&,
88  const Foam::scalarField&,
89  const Foam::globalIndex&
90  >& vertexData =
91  *static_cast
92  <
93  const Foam::Tuple3
94  <
95  const Foam::pointField&,
96  const Foam::scalarField&,
97  const Foam::globalIndex&
98  >*
99  >(data);
100 
101  const Foam::pointField& points = vertexData.first();
102  const Foam::scalarField& weights = vertexData.second();
103  const Foam::globalIndex& globalMap = vertexData.third();
104 
105  if (points.size() && wgt_dim != weights.size()/points.size())
106  {
107  *ierr = ZOLTAN_FATAL;
108  return;
109  }
110 
111  for (Foam::label i=0; i<points.size(); i++)
112  {
113  localIDs[i] = i;
114  globalIDs[i] = globalMap.toGlobal(i);
115 
116  for(int j=0; j<wgt_dim; j++)
117  {
118  obj_wgts[wgt_dim*i + j] = weights[wgt_dim*i + j];
119  }
120  }
121 
122  *ierr = ZOLTAN_OK;
123 }
124 
125 
126 static int get_mesh_dim(void* data, int* ierr)
127 {
128  return 3;
129 }
130 
131 
132 static void get_geom_list
133 (
134  void* data,
135  int nGID,
136  int nLID,
137  int nPoints,
138  ZOLTAN_ID_PTR globalIDs,
139  ZOLTAN_ID_PTR localIDs,
140  int nDim,
141  double* vertices,
142  int* ierr
143 )
144 {
145  const Foam::pointField& points =
146  *static_cast<const Foam::pointField*>(data);
147 
148  if
149  (
150  (nGID != 1)
151  || (nLID != 1)
152  || (nPoints != points.size())
153  || (nDim != 3)
154  )
155  {
156  *ierr = ZOLTAN_FATAL;
157  return;
158  }
159 
160  double* p = vertices;
161 
162  for (Foam::label celli=0; celli<nPoints; celli++)
163  {
164  const Foam::point& pt = points[celli];
165 
166  for (Foam::direction cmpt=0; cmpt<Foam::vector::nComponents; cmpt++)
167  {
168  *p++ = pt[cmpt];
169  }
170  }
171 
172  *ierr = ZOLTAN_OK;
173 }
174 
175 
177 (
178  void* data,
179  int nGID,
180  int nLID,
181  int nPoints,
182  ZOLTAN_ID_PTR globalIDs,
183  ZOLTAN_ID_PTR localIDs,
184  int* numEdges,
185  int* ierr
186 )
187 {
188  const Foam::CompactListList<Foam::label>& adjacency =
189  *static_cast<const Foam::CompactListList<Foam::label>*>(data);
190 
191  Foam::labelList sizes(adjacency.sizes());
192 
193  if ((nGID != 1) || (nLID != 1) || (nPoints != sizes.size()))
194  {
195  *ierr = ZOLTAN_FATAL;
196  return;
197  }
198 
199  for (Foam::label i=0; i<nPoints; i++)
200  {
201  numEdges[i] = sizes[i];
202  }
203 
204  *ierr = ZOLTAN_OK;
205 }
206 
207 
208 static void get_edge_list
209 (
210  void* data,
211  int nGID,
212  int nLID,
213  int nPoints,
214  ZOLTAN_ID_PTR globalIDs,
215  ZOLTAN_ID_PTR localIDs,
216  int* num_edges,
217  ZOLTAN_ID_PTR nborGID,
218  int* nborProc,
219  int wgt_dim,
220  float* ewgts,
221  int* ierr
222 )
223 {
224  const Foam::Tuple2
225  <
227  const Foam::globalIndex&
228  >& edgeData =
229  *static_cast
230  <
231  const Foam::Tuple2
232  <
234  const Foam::globalIndex&
235  >*
236  >(data);
237 
238  const Foam::CompactListList<Foam::label>& adjacency = edgeData.first();
239  const Foam::globalIndex& globalMap = edgeData.second();
240 
241  const Foam::labelList& m(adjacency.m());
242 
243  if
244  (
245  (nGID != 1)
246  || (nLID != 1)
247  )
248  {
249  *ierr = ZOLTAN_FATAL;
250  return;
251  }
252 
253  forAll(m, i)
254  {
255  nborGID[i] = m[i];
256  nborProc[i] = globalMap.whichProcID(m[i]);
257  }
258 
259  *ierr = ZOLTAN_OK;
260 }
261 
262 
264 (
265  const CompactListList<label>& adjacency,
266  const pointField& points,
267  const scalarField& pWeights,
268  List<label>& decomp
269 ) const
270 {
271  stringList args(1);
272  args[0] = "zoltan";
273 
274  const int argc = args.size();
275  char* argv[argc];
276  for (label i = 0; i < argc; i++)
277  {
278  argv[i] = strdup(args[i].c_str());
279  }
280 
281  float ver;
282  int rc = Zoltan_Initialize(argc, argv, &ver);
283 
284  if (rc != ZOLTAN_OK)
285  {
287  << "Failed initialising Zoltan" << exit(FatalError);
288  }
289 
290  struct Zoltan_Struct *zz = Zoltan_Create(PstreamGlobals::MPI_COMM_FOAM);
291 
292  const int nWeights = this->nWeights(points, pWeights);
293 
294  // Set internal parameters
295  Zoltan_Set_Param(zz, "return_lists", "export");
296  Zoltan_Set_Param(zz, "obj_weight_dim", name(nWeights).c_str());
297  Zoltan_Set_Param(zz, "edge_weight_dim", "0");
298 
299  // General default parameters
300  Zoltan_Set_Param(zz, "debug_level", "0");
301  Zoltan_Set_Param(zz, "imbalance_tol", "1.05");
302 
303  word lb_method("graph");
304 
305  // Set default method parameters
306  Zoltan_Set_Param(zz, "lb_method", lb_method.c_str());
307  Zoltan_Set_Param(zz, "lb_approach", "repartition");
308 
309  if (decompositionDict_.found("zoltanCoeffs"))
310  {
311  const dictionary& coeffsDict_ =
312  decompositionDict_.subDict("zoltanCoeffs");
313 
314  coeffsDict_.readIfPresent("lb_method", lb_method);
315 
316  forAllConstIter(IDLList<entry>, coeffsDict_, iter)
317  {
318  if (!iter().isDict())
319  {
320  const word& key = iter().keyword();
321  const word value(iter().stream());
322 
323  if (debug)
324  {
325  Info<< typeName << " : setting parameter " << key
326  << " to " << value << endl;
327  }
328 
329  Zoltan_Set_Param(zz, key.c_str(), value.c_str());
330  }
331  }
332  }
333 
334  if (nWeights > 1 && lb_method != "rcb")
335  {
337  << "Multiple constraints specified for lb_method " << lb_method
338  << " but is only supported by the rcb method"
339  << exit(FatalIOError);
340  }
341 
342  globalIndex globalMap(points.size());
343 
344  void* pointsPtr = &const_cast<pointField&>(points);
345  Zoltan_Set_Num_Obj_Fn(zz, get_number_of_vertices, pointsPtr);
346 
347  Tuple3<const pointField&, const scalarField&, const globalIndex&> vertexData
348  (
349  points,
350  pWeights,
351  globalMap
352  );
353  Zoltan_Set_Obj_List_Fn(zz, get_vertex_list, &vertexData);
354 
355  // Callbacks for geometry
356  Zoltan_Set_Num_Geom_Fn(zz, get_mesh_dim, pointsPtr);
357  Zoltan_Set_Geom_Multi_Fn(zz, get_geom_list, pointsPtr);
358 
359  // Callbacks for connectivity
360  void* adjacencyPtr = &const_cast<CompactListList<label>&>(adjacency);
361  Zoltan_Set_Num_Edges_Multi_Fn(zz, get_num_edges_list, adjacencyPtr);
362 
363  Tuple2<const CompactListList<label>&, const globalIndex&> edgeData
364  (
365  adjacency,
366  globalMap
367  );
368  Zoltan_Set_Edge_List_Multi_Fn(zz, get_edge_list, &edgeData);
369 
370  int changed, num_imp, num_exp, *imp_procs, *exp_procs;
371  int *imp_to_part, *exp_to_part;
372  int num_gid_entries, num_lid_entries;
373  ZOLTAN_ID_PTR imp_global_ids, exp_global_ids;
374  ZOLTAN_ID_PTR imp_local_ids, exp_local_ids;
375 
376  // Switch off FPU error trapping to work around divide-by-zero bug
377  // in the Zoltan graph and hypergraph methods
378  #ifdef FE_NOMASK_ENV
379  int oldExcepts = fedisableexcept
380  (
381  FE_DIVBYZERO
382  | FE_INVALID
383  | FE_OVERFLOW
384  );
385  #endif
386 
387  // Perform load balancing
388  Zoltan_LB_Partition
389  (
390  zz,
391  &changed,
392  &num_gid_entries,
393  &num_lid_entries,
394  &num_imp,
395  &imp_global_ids,
396  &imp_local_ids,
397  &imp_procs,
398  &imp_to_part,
399  &num_exp,
400  &exp_global_ids,
401  &exp_local_ids,
402  &exp_procs,
403  &exp_to_part
404  );
405 
406  // Reinstate FPU error trapping
407  #ifdef FE_NOMASK_ENV
408  feenableexcept(oldExcepts);
409  #endif
410 
411  if (debug && changed)
412  {
413  Pout << "Zoltan number to move " << changed << " " << num_exp << endl;
414  }
415 
416  decomp.setSize(points.size());
417  decomp = Pstream::myProcNo();
418 
419  if (changed)
420  {
421  for(int i=0; i<num_exp; i++)
422  {
423  decomp[exp_local_ids[i]] = exp_procs[i];
424  }
425 
426  // Sum the number of cells allocated to each processor
427  labelList nProcCells(Pstream::nProcs(), 0);
428 
429  forAll(decomp, i)
430  {
431  nProcCells[decomp[i]]++;
432  }
433 
434  reduce(nProcCells, ListOp<sumOp<label>>());
435 
436  // If there are no cells allocated to this processor keep the first one
437  // to ensure that all processors have at least one cell
438  if (nProcCells[Pstream::myProcNo()] == 0)
439  {
440  Pout<< " No cells allocated to this processor"
441  ", keeping first cell"
442  << endl;
443  decomp[0] = Pstream::myProcNo();
444  }
445  }
446 
447  // Free memory allocated for load-balancing results by Zoltan library
448 
449  Zoltan_LB_Free_Part
450  (
451  &imp_global_ids,
452  &imp_local_ids,
453  &imp_procs,
454  &imp_to_part
455  );
456 
457  Zoltan_LB_Free_Part
458  (
459  &exp_global_ids,
460  &exp_local_ids,
461  &exp_procs,
462  &exp_to_part
463  );
464 
465  Zoltan_Destroy(&zz);
466 
467  // Free the string storage allocated by strdup
468  for (label i = 0; i < argc; i++)
469  {
470  free(argv[i]);
471  }
472 
473  return 0;
474 }
475 
476 
477 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
478 
480 :
481  decompositionMethod(decompositionDict)
482 {}
483 
484 
485 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
486 
488 (
489  const polyMesh& mesh,
490  const pointField& cellCentres,
491  const scalarField& cellWeights
492 )
493 {
494  if (cellCentres.size() != mesh.nCells())
495  {
497  << "Can use this decomposition method only for the whole mesh"
498  << endl
499  << "and supply one coordinate (cellCentre) for every cell." << endl
500  << "The number of coordinates " << cellCentres.size() << endl
501  << "The number of cells in the mesh " << mesh.nCells()
502  << exit(FatalError);
503  }
504 
505 
506  // Convert mesh.cellCells() into compressed format
507  CompactListList<label> cellCells;
508  calcCellCells
509  (
510  mesh,
511  identityMap(mesh.nCells()),
512  mesh.nCells(),
513  true,
514  cellCells
515  );
516 
517  // Decompose using default weights
518  List<label> decomp;
519  decompose
520  (
521  cellCells,
522  cellCentres,
523  cellWeights,
524  decomp
525  );
526 
527  return decomp;
528 }
529 
530 
532 (
533  const polyMesh& mesh,
534  const labelList& agglom,
535  const pointField& agglomPoints,
536  const scalarField& pointWeights
537 )
538 {
539  if (agglom.size() != mesh.nCells())
540  {
542  << "Size of cell-to-coarse map " << agglom.size()
543  << " differs from number of cells in mesh " << mesh.nCells()
544  << exit(FatalError);
545  }
546 
547  // Convert agglom into compressed format
548  CompactListList<label> cellCells;
549  calcCellCells
550  (
551  mesh,
552  agglom,
553  agglomPoints.size(),
554  true,
555  cellCells
556  );
557 
558  // Decompose using weights
559  List<label> decomp;
560  decompose
561  (
562  cellCells,
563  agglomPoints,
564  pointWeights,
565  decomp
566  );
567 
568  // Rework back into decomposition for original mesh
569  labelList fineDistribution(agglom.size());
570 
571  forAll(fineDistribution, i)
572  {
573  fineDistribution[i] = decomp[agglom[i]];
574  }
575 
576  return fineDistribution;
577 }
578 
579 
581 (
582  const labelListList& globalPointPoints,
583  const pointField& points,
584  const scalarField& pWeights
585 )
586 {
587  if (points.size() != globalPointPoints.size())
588  {
590  << "Inconsistent number of cells (" << globalPointPoints.size()
591  << ") and number of cell centres (" << points.size()
592  << ")." << exit(FatalError);
593  }
594 
595 
596  // Convert globalPointPoints into compressed format
597  CompactListList<label> pointPoints(globalPointPoints);
598 
599  // Decompose using weights
600  List<label> decomp;
601  decompose
602  (
603  pointPoints,
604  points,
605  pWeights,
606  decomp
607  );
608 
609  return decomp;
610 }
611 
612 
613 // ************************************************************************* //
#define forAll(list, i)
Loop across all elements in list.
Definition: UList.H:434
#define forAllConstIter(Container, container, iter)
Iterate across all elements in the container object of type.
Definition: UList.H:477
Macros for easy insertion into run-time selection tables.
A packed storage unstructured matrix of objects of type <T> using an offset table for access.
void size(const label)
Override size to be inconsistent with allocated storage.
Definition: ListI.H:164
A 2-tuple for storing two objects of different types.
Definition: Tuple2.H:66
A 3-tuple for storing three objects of different types.
Definition: Tuple3.H:60
label size() const
Return the primary size, i.e. the number of rows.
labelList sizes() const
Return sizes (to be used e.g. for construction)
static label nProcs(const label communicator=0)
Number of processes in parallel run.
Definition: UPstream.H:411
static int myProcNo(const label communicator=0)
Number of this process (starting from masterNo() = 0)
Definition: UPstream.H:429
static const direction nComponents
Number of components in this vector space.
Definition: VectorSpace.H:105
label size() const
Return the number of arguments.
Definition: argListI.H:90
Abstract base class for decomposition.
label nWeights(const pointField &points, const scalarField &pointWeights) const
Return the number of weights per point.
Zoltan redistribution in parallel.
Definition: zoltan.H:97
virtual labelList decompose(const pointField &points, const scalarField &pointWeights)
Inherit decompose from decompositionMethod.
zoltan(const dictionary &decompositionDict)
Construct given the decomposition dictionary and mesh.
Definition: zoltan.C:479
A list of keyword definitions, which are a keyword followed by any number of values (e....
Definition: dictionary.H:162
const dictionary & subDict(const word &) const
Find and return a sub-dictionary.
Definition: dictionary.C:843
bool readIfPresent(const word &, T &, bool recursive=false, bool patternMatch=true) const
Find an entry if present, and assign to T.
bool found(const word &, bool recursive=false, bool patternMatch=true) const
Search dictionary for given keyword.
Definition: dictionary.C:509
Calculates a unique integer (label so might not have enough room - 2G max) for processor + local inde...
Definition: globalIndex.H:64
label whichProcID(const label i) const
Which processor does global come from? Binary search.
Definition: globalIndexI.H:123
label toGlobal(const label i) const
From local to global.
Definition: globalIndexI.H:82
Mesh consisting of general polyhedral cells.
Definition: polyMesh.H:80
label nCells() const
#define FatalIOErrorInFunction(ios)
Report an error message using Foam::FatalIOError.
Definition: error.H:346
#define FatalErrorInFunction
Report an error message using Foam::FatalError.
Definition: error.H:334
volScalarField scalarField(fieldObject, mesh)
const pointField & points
label nPoints
addToRunTimeSelectionTable(decompositionMethod, metis, decomposer)
Namespace for OpenFOAM.
errorManipArg< error, int > exit(error &err, const int errNo=1)
Definition: errorManip.H:124
pointField vertices(const blockVertexList &bvl)
List< label > labelList
A List of labels.
Definition: labelList.H:56
intWM_LABEL_SIZE_t label
A label is an int32_t or int64_t as specified by the pre-processor macro WM_LABEL_SIZE.
Definition: label.H:59
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:257
word name(const bool)
Return a word representation of a bool.
Definition: boolIO.C:39
messageStream Info
vectorField pointField
pointField is a vectorField.
Definition: pointFieldFwd.H:42
void reduce(const List< UPstream::commsStruct > &comms, T &Value, const BinaryOp &bop, const int tag, const label comm)
IOerror FatalIOError
prefixOSstream Pout(cout, "Pout")
Definition: IOstreams.H:53
error FatalError
List< string > stringList
A List of strings.
Definition: stringList.H:50
labelList identityMap(const label len)
Create identity map (map[i] == i) of given length.
Definition: ListOps.C:104
uint8_t direction
Definition: direction.H:45
Foam::argList args(argc, argv)
volScalarField & p
static int get_number_of_vertices(void *data, int *ierr)
Definition: zoltan.C:62
static void get_num_edges_list(void *data, int nGID, int nLID, int nPoints, ZOLTAN_ID_PTR globalIDs, ZOLTAN_ID_PTR localIDs, int *numEdges, int *ierr)
Definition: zoltan.C:177
static void get_geom_list(void *data, int nGID, int nLID, int nPoints, ZOLTAN_ID_PTR globalIDs, ZOLTAN_ID_PTR localIDs, int nDim, double *vertices, int *ierr)
Definition: zoltan.C:133
static int get_mesh_dim(void *data, int *ierr)
Definition: zoltan.C:126
static void get_vertex_list(void *data, int nGID, int nLID, ZOLTAN_ID_PTR globalIDs, ZOLTAN_ID_PTR localIDs, int wgt_dim, float *obj_wgts, int *ierr)
Definition: zoltan.C:74
static void get_edge_list(void *data, int nGID, int nLID, int nPoints, ZOLTAN_ID_PTR globalIDs, ZOLTAN_ID_PTR localIDs, int *num_edges, ZOLTAN_ID_PTR nborGID, int *nborProc, int wgt_dim, float *ewgts, int *ierr)
Definition: zoltan.C:209