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-2025 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  List<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.begin(), &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 (!methodDict_.empty())
310  {
311  methodDict_.readIfPresent("lb_method", lb_method);
312 
313  forAllConstIter(IDLList<entry>, methodDict_, iter)
314  {
315  if (!iter().isDict())
316  {
317  const word& key = iter().keyword();
318  const word value(iter().stream());
319 
320  if (debug)
321  {
322  Info<< typeName << " : setting parameter " << key
323  << " to " << value << endl;
324  }
325 
326  Zoltan_Set_Param(zz, key.c_str(), value.c_str());
327  }
328  }
329  }
330 
331  if (nWeights > 1 && lb_method != "rcb")
332  {
333  FatalIOErrorInFunction(methodDict_)
334  << "Multiple constraints specified for lb_method " << lb_method
335  << " but is only supported by the rcb method"
336  << exit(FatalIOError);
337  }
338 
339  globalIndex globalMap(points.size());
340 
341  void* pointsPtr = &const_cast<pointField&>(points);
342  Zoltan_Set_Num_Obj_Fn(zz, get_number_of_vertices, pointsPtr);
343 
344  Tuple3<const pointField&, const scalarField&, const globalIndex&> vertexData
345  (
346  points,
347  pWeights,
348  globalMap
349  );
350  Zoltan_Set_Obj_List_Fn(zz, get_vertex_list, &vertexData);
351 
352  // Callbacks for geometry
353  Zoltan_Set_Num_Geom_Fn(zz, get_mesh_dim, pointsPtr);
354  Zoltan_Set_Geom_Multi_Fn(zz, get_geom_list, pointsPtr);
355 
356  // Callbacks for connectivity
357  void* adjacencyPtr = &const_cast<CompactListList<label>&>(adjacency);
358  Zoltan_Set_Num_Edges_Multi_Fn(zz, get_num_edges_list, adjacencyPtr);
359 
360  Tuple2<const CompactListList<label>&, const globalIndex&> edgeData
361  (
362  adjacency,
363  globalMap
364  );
365  Zoltan_Set_Edge_List_Multi_Fn(zz, get_edge_list, &edgeData);
366 
367  int changed, num_imp, num_exp, *imp_procs, *exp_procs;
368  int *imp_to_part, *exp_to_part;
369  int num_gid_entries, num_lid_entries;
370  ZOLTAN_ID_PTR imp_global_ids, exp_global_ids;
371  ZOLTAN_ID_PTR imp_local_ids, exp_local_ids;
372 
373  // Switch off FPU error trapping to work around divide-by-zero bug
374  // in the Zoltan graph and hypergraph methods
375  #ifdef FE_NOMASK_ENV
376  int oldExcepts = fedisableexcept
377  (
378  FE_DIVBYZERO
379  | FE_INVALID
380  | FE_OVERFLOW
381  );
382  #endif
383 
384  // Perform load balancing
385  Zoltan_LB_Partition
386  (
387  zz,
388  &changed,
389  &num_gid_entries,
390  &num_lid_entries,
391  &num_imp,
392  &imp_global_ids,
393  &imp_local_ids,
394  &imp_procs,
395  &imp_to_part,
396  &num_exp,
397  &exp_global_ids,
398  &exp_local_ids,
399  &exp_procs,
400  &exp_to_part
401  );
402 
403  // Reinstate FPU error trapping
404  #ifdef FE_NOMASK_ENV
405  feenableexcept(oldExcepts);
406  #endif
407 
408  if (debug && changed)
409  {
410  Pout << "Zoltan number to move " << changed << " " << num_exp << endl;
411  }
412 
413  decomp.setSize(points.size());
414  decomp = Pstream::myProcNo();
415 
416  if (changed)
417  {
418  for(int i=0; i<num_exp; i++)
419  {
420  decomp[exp_local_ids[i]] = exp_procs[i];
421  }
422 
423  // Sum the number of cells allocated to each processor
424  labelList nProcCells(Pstream::nProcs(), 0);
425 
426  forAll(decomp, i)
427  {
428  nProcCells[decomp[i]]++;
429  }
430 
431  reduce(nProcCells, ListOp<sumOp<label>>());
432 
433  // If there are no cells allocated to this processor keep the first one
434  // to ensure that all processors have at least one cell
435  if (nProcCells[Pstream::myProcNo()] == 0)
436  {
437  Pout<< " No cells allocated to this processor"
438  ", keeping first cell"
439  << endl;
440  decomp[0] = Pstream::myProcNo();
441  }
442  }
443 
444  // Free memory allocated for load-balancing results by Zoltan library
445 
446  Zoltan_LB_Free_Part
447  (
448  &imp_global_ids,
449  &imp_local_ids,
450  &imp_procs,
451  &imp_to_part
452  );
453 
454  Zoltan_LB_Free_Part
455  (
456  &exp_global_ids,
457  &exp_local_ids,
458  &exp_procs,
459  &exp_to_part
460  );
461 
462  Zoltan_Destroy(&zz);
463 
464  // Free the string storage allocated by strdup
465  for (label i = 0; i < argc; i++)
466  {
467  free(argv[i]);
468  }
469 
470  return 0;
471 }
472 
473 
474 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
475 
477 (
478  const dictionary& decompositionDict,
479  const dictionary& methodDict
480 )
481 :
482  decompositionMethod(decompositionDict),
483  methodDict_(methodDict)
484 {}
485 
486 
487 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
488 
490 (
491  const polyMesh& mesh,
492  const pointField& cellCentres,
493  const scalarField& cellWeights
494 )
495 {
496  if (cellCentres.size() != mesh.nCells())
497  {
499  << "Can use this decomposition method only for the whole mesh"
500  << endl
501  << "and supply one coordinate (cellCentre) for every cell." << endl
502  << "The number of coordinates " << cellCentres.size() << endl
503  << "The number of cells in the mesh " << mesh.nCells()
504  << exit(FatalError);
505  }
506 
507 
508  // Convert mesh.cellCells() into compressed format
509  CompactListList<label> cellCells;
510  calcCellCells
511  (
512  mesh,
514  mesh.nCells(),
515  true,
516  cellCells
517  );
518 
519  // Decompose using default weights
520  List<label> decomp;
521  decompose
522  (
523  cellCells,
524  cellCentres,
525  cellWeights,
526  decomp
527  );
528 
529  return decomp;
530 }
531 
532 
534 (
535  const polyMesh& mesh,
536  const labelList& agglom,
537  const pointField& agglomPoints,
538  const scalarField& pointWeights
539 )
540 {
541  if (agglom.size() != mesh.nCells())
542  {
544  << "Size of cell-to-coarse map " << agglom.size()
545  << " differs from number of cells in mesh " << mesh.nCells()
546  << exit(FatalError);
547  }
548 
549  // Convert agglom into compressed format
550  CompactListList<label> cellCells;
551  calcCellCells
552  (
553  mesh,
554  agglom,
555  agglomPoints.size(),
556  true,
557  cellCells
558  );
559 
560  // Decompose using weights
561  List<label> decomp;
562  decompose
563  (
564  cellCells,
565  agglomPoints,
566  pointWeights,
567  decomp
568  );
569 
570  // Rework back into decomposition for original mesh
571  labelList fineDistribution(agglom.size());
572 
573  forAll(fineDistribution, i)
574  {
575  fineDistribution[i] = decomp[agglom[i]];
576  }
577 
578  return fineDistribution;
579 }
580 
581 
583 (
584  const labelListList& globalPointPoints,
585  const pointField& points,
586  const scalarField& pWeights
587 )
588 {
589  if (points.size() != globalPointPoints.size())
590  {
592  << "Inconsistent number of cells (" << globalPointPoints.size()
593  << ") and number of cell centres (" << points.size()
594  << ")." << exit(FatalError);
595  }
596 
597 
598  // Convert globalPointPoints into compressed format
599  CompactListList<label> pointPoints(globalPointPoints);
600 
601  // Decompose using weights
602  List<label> decomp;
603  decompose
604  (
605  pointPoints,
606  points,
607  pWeights,
608  decomp
609  );
610 
611  return decomp;
612 }
613 
614 
615 // ************************************************************************* //
#define forAll(list, i)
Loop across all elements in list.
Definition: UList.H:433
#define forAllConstIter(Container, container, iter)
Iterate across all elements in the container object of type.
Definition: UList.H:476
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, const dictionary &methodDict)
Construct given the decomposition dictionary and mesh.
Definition: zoltan.C:477
A list of keywords followed by any number of values (e.g. words and numbers) or sub-dictionaries.
Definition: dictionary.H:162
bool readIfPresent(const word &, T &, bool recursive=false, bool patternMatch=true) const
Find an entry if present, and assign to T.
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
Foam::fvMesh mesh(Foam::IOobject(regionName, runTime.name(), runTime, Foam::IOobject::MUST_READ), false)
#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:258
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
word name(const LagrangianState state)
Return a string representation of a Lagrangian state enumeration.
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