Loading...
Searching...
No Matches
ptscotchDecomp.C
Go to the documentation of this file.
1/*---------------------------------------------------------------------------*\
2 ========= |
3 \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
4 \\ / O peration |
5 \\ / A nd | www.openfoam.com
6 \\/ M anipulation |
7-------------------------------------------------------------------------------
8 Copyright (C) 2011-2017 OpenFOAM Foundation
9 Copyright (C) 2015-2024 OpenCFD Ltd.
10-------------------------------------------------------------------------------
11License
12 This file is part of OpenFOAM.
13
14 OpenFOAM is free software: you can redistribute it and/or modify it
15 under the terms of the GNU General Public License as published by
16 the Free Software Foundation, either version 3 of the License, or
17 (at your option) any later version.
18
19 OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
20 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
21 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
22 for more details.
23
24 You should have received a copy of the GNU General Public License
25 along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
26
27\*---------------------------------------------------------------------------*/
28
29#include "ptscotchDecomp.H"
31#include "floatScalar.H"
32#include "globalMeshData.H"
33#include "Time.H"
35#include "OFstream.H"
36#include <cstdio>
37#include <limits>
38
39// Include MPI without any C++ bindings
40#ifndef MPICH_SKIP_MPICXX
41#define MPICH_SKIP_MPICXX
42#endif
43#ifndef OMPI_SKIP_MPICXX
44#define OMPI_SKIP_MPICXX
45#endif
46
47// Avoid too many warnings from mpi.h
48#pragma GCC diagnostic ignored "-Wold-style-cast"
49
50#include <mpi.h>
51#include "ptscotch.h"
52
53// Hack: scotch generates floating point errors so need to switch off error
54// trapping!
55#ifdef __GLIBC__
56 #ifndef _GNU_SOURCE
57 #define _GNU_SOURCE
58 #endif
59 #include <fenv.h>
60#endif
61
62// Error if we attempt narrowing
63static_assert
64(
65 sizeof(Foam::label) <= sizeof(SCOTCH_Num),
66 "SCOTCH_Num is too small for Foam::label, check your scotch headers"
67);
68
69// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
70
71namespace Foam
72{
75 (
79 );
80}
81
83// * * * * * * * * * * * * * * * Local Functions * * * * * * * * * * * * * * //
84
85namespace Foam
86{
87
88// Check and print error message
89static inline void check(const int retVal, const char* what)
90{
91 if (retVal)
92 {
94 << "Call to scotch routine " << what
95 << " failed (" << retVal << ")\n"
96 << exit(FatalError);
97 }
98}
99
100// The mesh-relative graph path/name (without extension)
101static inline Foam::fileName getGraphPathBase(const polyMesh& mesh)
102{
103 return mesh.time().path()/mesh.name();
104}
105
106
107} // End namespace Foam
108
109
110// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
111
112Foam::label Foam::ptscotchDecomp::decompose
113(
114 const labelList& adjncy,
115 const labelList& xadj,
116 const List<scalar>& cWeights,
117 labelList& decomp
118) const
119{
120 const SCOTCH_Num numCells = Foam::max(0, (xadj.size()-1));
121
122 // Addressing
123 ConstPrecisionAdaptor<SCOTCH_Num, label, List> adjncy_param(adjncy);
124 ConstPrecisionAdaptor<SCOTCH_Num, label, List> xadj_param(xadj);
125
126 // Output: cell -> processor addressing
127 decomp.resize_nocopy(numCells);
128 decomp = 0;
129 PrecisionAdaptor<SCOTCH_Num, label, List> decomp_param(decomp, false);
130
131 // Avoid potential nullptr issues with zero-sized arrays
132 labelList adjncy_dummy, xadj_dummy, decomp_dummy;
133 if (!numCells)
134 {
135 adjncy_dummy.resize(1, 0);
136 adjncy_param.set(adjncy_dummy);
137
138 xadj_dummy.resize(2, 0);
139 xadj_param.set(xadj_dummy);
140
141 decomp_dummy.resize(1, 0);
142 decomp_param.clear(); // Avoid propagating spurious values
143 decomp_param.set(decomp_dummy);
144 }
145
146
147 if (debug & 2)
148 {
149 Pout<< "ptscotchDecomp : " << numCells << " cells" << endl;
150 }
151
152 // Dump graph
153 if (coeffsDict_.getOrDefault("writeGraph", false))
154 {
155 OFstream str
156 (
157 graphPath_ + "_" + Foam::name(Pstream::myProcNo()) + ".dgr"
158 );
159
160 Pout<< "Dumping Scotch graph file to " << str.name() << endl
161 << "Use this in combination with dgpart." << endl;
162
163 const label numConnect = adjncy.size();
164 const label nTotCells = returnReduce(numCells, sumOp<label>());
165 const label nTotConnect = returnReduce(numConnect, sumOp<label>());
166
167 // Version 2 = Distributed graph file (.dgrf)
168 str << "2" << nl;
169
170 // Number of files (procglbnbr), my file number (procloc)
171 str << Pstream::nProcs() << ' ' << Pstream::myProcNo() << nl;
172
173 // Total number of vertices (vertglbnbr),
174 // Total number of connections (edgeglbnbr)
175 str << nTotCells << ' ' << nTotConnect << nl;
176
177 // Local number of vertices (vertlocnbr),
178 // Local number of connections (edgelocnbr)
179 str << numCells << ' ' << numConnect << nl;
180
181 // Numbering starts from 0
182 // 100*hasVertlabels+10*hasEdgeWeights+1*hasVertWeights
183 str << "0 000" << nl;
184
185 for (label celli = 0; celli < numCells; ++celli)
186 {
187 const label beg = xadj[celli];
188 const label end = xadj[celli+1];
189
190 str << (end-beg); // size
191
192 for (label i = beg; i < end; ++i)
193 {
194 str << ' ' << adjncy[i];
195 }
196 str << nl;
197 }
198 }
199
200
201 // Make repeatable
202 SCOTCH_randomReset();
203
204 // Strategy
205 // ~~~~~~~~
206
207 // Default.
208 SCOTCH_Strat stradat;
209 check
210 (
211 SCOTCH_stratInit(&stradat),
212 "SCOTCH_stratInit"
213 );
214
215 string strategy;
216 if (coeffsDict_.readIfPresent("strategy", strategy))
217 {
219 << "ptscotchDecomp : Using strategy " << strategy << endl;
220
221 SCOTCH_stratDgraphMap(&stradat, strategy.c_str());
222 //fprintf(stdout, "S\tStrat=");
223 //SCOTCH_stratSave(&stradat, stdout);
224 //fprintf(stdout, "\n");
225 }
226
227
228 // Graph
229 // ~~~~~
230
231 // Check for externally provided cellweights and if so initialise weights
232
233 bool hasWeights = returnReduceOr(cWeights.size());
234
235 const scalar minWeights = hasWeights ? gMin(cWeights) : scalar(1);
236
237 if (minWeights <= 0)
238 {
239 hasWeights = false;
241 << "Illegal minimum weight " << minWeights
242 << " ... ignoring"
243 << endl;
244 }
245 else if (hasWeights && (cWeights.size() != numCells))
246 {
247 FatalErrorInFunction
248 << "Number of cell weights " << cWeights.size()
249 << " does not equal number of cells " << numCells
250 << exit(FatalError);
251 }
252
253
254 List<SCOTCH_Num> velotab;
255
256 if (hasWeights)
257 {
258 scalar rangeScale(1);
259
260 const scalar velotabSum = gSum(cWeights)/minWeights;
261
262 const scalar upperRange = static_cast<scalar>
263 (
264 std::numeric_limits<SCOTCH_Num>::max()-1
265 );
266
267 if (velotabSum > upperRange)
268 {
269 // 0.9 factor of safety to avoid floating point round-off in
270 // rangeScale tipping the subsequent sum over the integer limit.
271 rangeScale = 0.9*upperRange/velotabSum;
272
274 << "Sum of weights overflows SCOTCH_Num: " << velotabSum
275 << ", compressing by factor " << rangeScale << endl;
276 }
277
278 if (cWeights.size())
279 {
280 // Convert to integers.
281 velotab.resize(cWeights.size());
282
283 forAll(velotab, i)
284 {
285 velotab[i] = static_cast<SCOTCH_Num>
286 (
287 ((cWeights[i]/minWeights - 1)*rangeScale) + 1
288 );
289 }
290 }
291 else
292 {
293 // Locally zero cells but not globally.
294 // Provide some size to avoid null pointer.
295 velotab.resize(1, 1);
296 }
297 }
298 else
299 {
300 // HACK: specify uniform weights
301 // - seems that scotch takes different code paths internally
302 // if weights are not specified (issue #3063)
303
304 if (numCells > 0)
305 {
306 velotab.resize(numCells);
307 }
308 else
309 {
310 // Locally zero cells but not globally.
311 // Provide some size to avoid null pointer.
312 velotab.resize(1);
313 }
314
315 velotab = static_cast<SCOTCH_Num>(1);
316 }
317
318
319 //
320 // Decomposition graph
321 //
322
323 if (debug & 2)
324 {
325 Pout<< "SCOTCH_dgraphInit" << endl;
326 }
327 SCOTCH_Dgraph grafdat;
328 check
329 (
330 SCOTCH_dgraphInit(&grafdat, MPI_COMM_WORLD),
331 "SCOTCH_dgraphInit"
332 );
333
334 if (debug & 2)
335 {
336 Pout<< "SCOTCH_dgraphBuild with:" << nl
337 << "numCells : " << numCells << nl
338 << "xadj : " << name(xadj_param().cdata()) << nl
339 << "velotab : " << name(velotab.cdata()) << nl
340 << "adjncySize : " << adjncy_param().size() << nl
341 << "adjncy : " << name(adjncy_param().cdata()) << nl
342 << endl;
343 }
344
345 check
346 (
347 SCOTCH_dgraphBuild
348 (
349 &grafdat, // Graph to build
350 0, // Base for indexing (C-style)
351
352 numCells, // vertlocnbr [== nCells]
353 numCells, // vertlocmax
354
355 xadj_param.constCast().data(),
356 // vertloctab, start index per cell into
357 // adjncy
358 (xadj_param.constCast().data()+1),
359 // vendloctab, end index ,,
360
361 velotab.data(), // veloloctab, vtx weights
362 nullptr, // vlblloctab
363
364 adjncy.size(), // edgelocnbr, number of arcs
365 adjncy.size(), // edgelocsiz
366 adjncy_param.constCast().data(), // edgeloctab
367 nullptr, // edgegsttab
368 nullptr // edlotab, edge weights
369 ),
370 "SCOTCH_dgraphBuild"
371 );
372
373 if (debug & 2)
374 {
375 Pout<< "SCOTCH_dgraphCheck" << endl;
376 }
377 check
378 (
379 SCOTCH_dgraphCheck(&grafdat),
380 "SCOTCH_dgraphCheck"
381 );
382
383
384 // Architecture
385 // ~~~~~~~~~~~~
386 // (fully connected network topology since using switch)
387
388 if (debug & 2)
389 {
390 Pout<< "SCOTCH_archInit" << endl;
391 }
392 SCOTCH_Arch archdat;
393 check
394 (
395 SCOTCH_archInit(&archdat),
396 "SCOTCH_archInit"
397 );
398
399 List<SCOTCH_Num> procWeights;
400 if
401 (
402 coeffsDict_.readIfPresent("processorWeights", procWeights)
403 && !procWeights.empty()
404 )
405 {
406 if (procWeights.size() != nDomains_)
407 {
408 FatalIOErrorInFunction(coeffsDict_)
409 << "processorWeights (" << procWeights.size()
410 << ") != number of domains (" << nDomains_ << ")" << nl
411 << exit(FatalIOError);
412 }
413
415 << "ptscotchDecomp : Using procesor weights "
416 << procWeights << endl;
417
418 check
419 (
420 SCOTCH_archCmpltw(&archdat, nDomains_, procWeights.cdata()),
421 "SCOTCH_archCmpltw"
422 );
423 }
424 else
425 {
426 // Check for optional domains/weights
427 List<SCOTCH_Num> domains;
428 List<scalar> dWeights;
429 if
430 (
431 coeffsDict_.readIfPresent
432 (
433 "domains",
434 domains,
436 )
437 && coeffsDict_.readIfPresent
438 (
439 "domainWeights",
440 dWeights,
442 )
443 )
444 {
446 << "Ignoring multi-level decomposition since"
447 << " not supported by ptscotch."
448 << " It is supported by scotch" << endl;
449 }
450
451 if (debug & 2)
452 {
453 Pout<< "SCOTCH_archCmplt" << endl;
454 }
455 check
456 (
457 SCOTCH_archCmplt(&archdat, nDomains_),
458 "SCOTCH_archCmplt"
459 );
460 }
461
462
463 // Hack:switch off fpu error trapping
464 #ifdef FE_NOMASK_ENV
465 int oldExcepts = fedisableexcept
466 (
467 FE_DIVBYZERO
468 | FE_INVALID
469 | FE_OVERFLOW
470 );
471 #endif
472
473 if (debug & 2)
474 {
475 Pout<< "SCOTCH_dgraphMap" << endl;
476 }
477 check
478 (
479 SCOTCH_dgraphMap
480 (
481 &grafdat,
482 &archdat,
483 &stradat, // const SCOTCH_Strat *
484 decomp_param.ref().data() // parttab
485 ),
486 "SCOTCH_graphMap"
487 );
488
489 #ifdef FE_NOMASK_ENV
490 feenableexcept(oldExcepts);
491 #endif
492
493 //check
494 //(
495 // SCOTCH_dgraphPart
496 // (
497 // &grafdat,
498 // nDomains_, // partnbr
499 // &stradat, // const SCOTCH_Strat *
500 // decomp_param.ref().data() // parttab
501 // ),
502 // "SCOTCH_graphPart"
503 //);
504
505 if (debug & 2)
506 {
507 Pout<< "SCOTCH_dgraphExit" << endl;
508 }
509
510 SCOTCH_dgraphExit(&grafdat); // Release storage for graph
511 SCOTCH_stratExit(&stradat); // Release storage for strategy
512 SCOTCH_archExit(&archdat); // Release storage for network topology
514 return 0;
515}
516
517
518// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
519
521:
522 decompositionMethod(numDomains),
523 coeffsDict_(dictionary::null)
524{}
525
526
528(
529 const dictionary& decompDict,
530 const word& regionName
531)
532:
534 coeffsDict_(findCoeffsDict("scotchCoeffs", selectionType::NULL_DICT))
535{}
536
537
538// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
539
540Foam::labelList Foam::ptscotchDecomp::decompose
541(
542 const polyMesh& mesh,
543 const pointField& points,
544 const scalarField& pointWeights
545) const
546{
547 // Where to write graph
548 graphPath_ = getGraphPathBase(mesh);
549
550 if (!points.empty() && (points.size() != mesh.nCells()))
551 {
553 << "Number of cell centres (" << points.size()
554 << ") != number of cells (" << mesh.nCells() << ")"
555 << exit(FatalError);
556 }
557
558 // Make Metis CSR (Compressed Storage Format) storage
559 // adjncy : contains neighbours (= edges in graph)
560 // xadj(celli) : start of information in adjncy for celli
561
562 // Global mesh connectivity
563 CompactListList<label> cellCells;
564 globalMeshData::calcCellCells(mesh, cellCells, true);
565
566 labelList decomp;
567 decompose
568 (
569 cellCells.values(),
570 cellCells.offsets(),
571 pointWeights,
572 decomp
573 );
574
575 return decomp;
576}
577
578
579Foam::labelList Foam::ptscotchDecomp::decompose
580(
581 const polyMesh& mesh,
582 const labelList& agglom,
583 const pointField& agglomPoints,
584 const scalarField& agglomWeights
585) const
586{
587 // Where to write graph
588 graphPath_ = getGraphPathBase(mesh);
589
590 if (agglom.size() != mesh.nCells())
591 {
593 << "Agglomeration size (" << agglom.size()
594 << ") != number of cells (" << mesh.nCells() << ")"
595 << exit(FatalError);
596 }
597
598
599 // Make Metis CSR (Compressed Storage Format) storage
600 // adjncy : contains neighbours (= edges in graph)
601 // xadj(celli) : start of information in adjncy for celli
602
603 CompactListList<label> cellCells;
605 (
606 mesh,
607 agglom,
608 agglomPoints.size(),
609 true, // Global mesh connectivity
610 cellCells
611 );
612
613 // Decompose using weights
614 labelList decomp;
615 decompose
616 (
617 cellCells.values(),
618 cellCells.offsets(),
619 agglomWeights,
620 decomp
621 );
622
623 // From coarse back to fine for original mesh
624 return labelList(decomp, agglom);
625}
626
627
628Foam::labelList Foam::ptscotchDecomp::decompose
629(
630 const CompactListList<label>& globalCellCells,
631 const pointField& cellCentres,
632 const scalarField& cWeights
633) const
634{
635 // Where to write graph
636 graphPath_ = "ptscotch";
637
638 if (!cellCentres.empty() && (cellCentres.size() != globalCellCells.size()))
639 {
641 << "Number of cell centres (" << cellCentres.size()
642 << ") != number of cells (" << globalCellCells.size() << ")"
643 << exit(FatalError);
644 }
645
646 // CompactListList is already
647 // Metis CSR (Compressed Storage Format) storage
648 // adjncy : contains neighbours (= edges in graph)
649 // xadj(celli) : start of information in adjncy for celli
650
651 labelList decomp;
652 decompose
653 (
654 globalCellCells.values(),
655 globalCellCells.offsets(),
656 cWeights,
657 decomp
658 );
659
660 return decomp;
661}
662
663
664Foam::labelList Foam::ptscotchDecomp::decompose
665(
666 const labelListList& globalCellCells,
667 const pointField& cellCentres,
668 const scalarField& cWeights
669) const
670{
671 // Where to write graph
672 graphPath_ = "ptscotch";
673
674 if (!cellCentres.empty() && (cellCentres.size() != globalCellCells.size()))
675 {
677 << "Number of cell centres (" << cellCentres.size()
678 << ") != number of cells (" << globalCellCells.size() << ")"
679 << exit(FatalError);
680 }
681
682 // Make Metis CSR (Compressed Storage Format) storage
683 // adjncy : contains neighbours (= edges in graph)
684 // xadj(celli) : start of information in adjncy for celli
685
686 auto cellCells(CompactListList<label>::pack(globalCellCells));
687
688 labelList decomp;
689 decompose
690 (
691 cellCells.values(),
692 cellCells.offsets(),
693 cWeights,
694 decomp
695 );
696
697 return decomp;
698}
699
700
701// ************************************************************************* //
if(maxValue - minValue< SMALL)
Macros for easy insertion into run-time selection tables.
#define addToRunTimeSelectionTable(baseType, thisType, argNames)
Add to construction table with typeName as the key.
A packed storage of objects of type <T> using an offset table for access.
const labelList & offsets() const noexcept
Return the offset table (= size()+1).
const List< T > & values() const noexcept
Return the packed values.
static CompactListList< T > pack(const UList< SubListType > &lists, const bool checkOverflow=false)
Construct by packing together the list of lists.
label size() const noexcept
The primary size (the number of rows/sublists).
virtual const fileName & name() const override
Get the name of the output serial stream. (eg, the name of the Fstream file name).
Definition OSstream.H:134
fileName path() const
The path for the case = rootPath/caseName.
Definition TimePathsI.H:102
bool empty() const noexcept
True if List is empty (ie, size() is zero).
Definition UList.H:701
void size(const label n)
Older name for setAddressableSize.
Definition UList.H:118
static int myProcNo(const label communicator=worldComm)
Rank of this process in the communicator (starting from masterNo()). Negative if the process is not a...
Definition UPstream.H:1706
static label nProcs(const label communicator=worldComm)
Number of ranks in parallel run (for given communicator). It is 1 for serial run.
Definition UPstream.H:1697
Abstract base class for domain decomposition.
selectionType
Selection type when handling the coefficients dictionary.
label nDomains_
Number of domains for the decomposition.
decompositionMethod(const label numDomains)
Construct with specified number of domains, no coefficients or constraints.
static FOAM_NO_DANGLING_REFERENCE const dictionary & findCoeffsDict(const dictionary &dict, const word &coeffsName, int select=selectionType::DEFAULT)
Locate coeffsName dictionary or the fallback "coeffs" dictionary within an enclosing dictionary.
A list of keyword definitions, which are a keyword followed by a number of values (eg,...
Definition dictionary.H:133
A class for handling file names.
Definition fileName.H:75
const Time & time() const
Return the top-level database.
Definition fvMesh.H:360
const word & name() const
Return reference to name.
Definition fvMesh.H:387
static void calcCellCells(const polyMesh &mesh, const labelUList &agglom, const label nLocalCoarse, const bool parallel, CompactListList< label > &cellCells)
Determine (local or global) cellCells from mesh agglomeration.
@ LITERAL
String literal.
Definition keyType.H:82
Mesh consisting of general polyhedral cells.
Definition polyMesh.H:79
PTScotch domain decomposition.
ptscotchDecomp(const ptscotchDecomp &)=delete
No copy construct.
A class for handling words, derived from Foam::string.
Definition word.H:66
#define defineTypeNameAndDebug(Type, DebugSwitch)
Define the typeName and debug information.
Definition className.H:142
dynamicFvMesh & mesh
Foam::word regionName(args.getOrDefault< word >("region", Foam::polyMesh::defaultRegion))
#define FatalIOErrorInFunction(ios)
Report an error message using Foam::FatalIOError.
Definition error.H:629
#define FatalErrorInFunction
Report an error message using Foam::FatalError.
Definition error.H:600
auto & name
const pointField & points
#define DebugInfo
Report an information message using Foam::Info.
#define WarningInFunction
Report a warning using Foam::Warning.
const char * end
Definition SVGTools.H:223
Namespace for OpenFOAM.
bool returnReduceOr(const bool value, const int communicator=UPstream::worldComm)
Perform logical (or) MPI Allreduce on a copy. Uses UPstream::reduceOr.
label max(const labelHashSet &set, label maxValue=labelMin)
Find the max value in labelHashSet, optionally limited by second argument.
Definition hashSets.C:40
Type gSum(const FieldField< Field, Type > &f)
List< labelList > labelListList
List of labelList.
Definition labelList.H:38
List< label > labelList
A List of labels.
Definition List.H:62
static void check(const int retVal, const char *what)
Field< scalar > scalarField
Specialisation of Field<T> for scalar.
T returnReduce(const T &value, BinaryOp bop, const int tag=UPstream::msgType(), const int communicator=UPstream::worldComm)
Perform reduction on a copy, using specified binary operation.
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition Ostream.H:519
IOerror FatalIOError
Error stream (stdout output on all processes), with additional 'FOAM FATAL IO ERROR' header text and ...
static Foam::fileName getGraphPathBase(const polyMesh &mesh)
error FatalError
Error stream (stdout output on all processes), with additional 'FOAM FATAL ERROR' header text and sta...
Type gMin(const FieldField< Field, Type > &f)
word name(const expressions::valueTypeCode typeCode)
A word representation of a valueTypeCode. Empty for expressions::valueTypeCode::INVALID.
Definition exprTraits.C:127
prefixOSstream Pout
OSstream wrapped stdout (std::cout) with parallel prefix.
vectorField pointField
pointField is a vectorField.
errorManipArg< error, int > exit(error &err, const int errNo=1)
Definition errorManip.H:125
constexpr char nl
The newline '\n' character (0x0a).
Definition Ostream.H:50
#define forAll(list, i)
Loop across all elements in list.
Definition stdFoam.H:299