Loading...
Searching...
No Matches
scotchDecomp.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-2023 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 "scotchDecomp.H"
31#include "floatScalar.H"
32#include "Time.H"
34#include "OFstream.H"
35#include <cstdio>
36#include <limits>
37
38// Probably not needed, but in case we pickup a ptscotch.h ...
39#ifndef MPICH_SKIP_MPICXX
40#define MPICH_SKIP_MPICXX
41#endif
42#ifndef OMPI_SKIP_MPICXX
43#define OMPI_SKIP_MPICXX
44#endif
45
46#include "scotch.h"
47
48// Hack: scotch generates floating point errors so need to switch off error
49// trapping!
50#ifdef __GLIBC__
51 #ifndef _GNU_SOURCE
52 #define _GNU_SOURCE
53 #endif
54 #include <fenv.h>
55#endif
56
57// Error if we attempt narrowing
58static_assert
59(
60 sizeof(Foam::label) <= sizeof(SCOTCH_Num),
61 "SCOTCH_Num is too small for Foam::label, check your scotch headers"
62);
63
64// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
65
66namespace Foam
67{
70 (
74 );
75}
76
78// * * * * * * * * * * * * * * * Local Functions * * * * * * * * * * * * * * //
79
80namespace Foam
81{
82
83// Check and print error message
84static inline void check(const int retVal, const char* what)
85{
86 if (retVal)
87 {
89 << "Call to scotch routine " << what
90 << " failed (" << retVal << ")\n"
91 << exit(FatalError);
92 }
93}
94
95
96// The mesh-relative graph path/name (without extension)
97static inline Foam::fileName getGraphPathBase(const polyMesh& mesh)
98{
99 return mesh.time().path()/mesh.name();
100}
102
103} // End namespace Foam
104
105
106// * * * * * * * * * * * * Protected Member Functions * * * * * * * * * * * //
107
109(
110 const labelList& adjncy,
111 const labelList& xadj,
112 const List<scalar>& cWeights,
113 labelList& decomp
114) const
115{
116 const SCOTCH_Num numCells = Foam::max(0, (xadj.size()-1));
117
118 // Addressing
121
122 // Output: cell -> processor addressing
123 decomp.resize_nocopy(numCells);
124 decomp = 0;
125 PrecisionAdaptor<SCOTCH_Num, label, List> decomp_param(decomp, false);
126
127 // Avoid potential nullptr issues with zero-sized arrays
128 labelList adjncy_dummy, xadj_dummy, decomp_dummy;
129 if (!numCells)
130 {
131 adjncy_dummy.resize(1, 0);
132 adjncy_param.set(adjncy_dummy);
133
134 xadj_dummy.resize(2, 0);
135 xadj_param.set(xadj_dummy);
136
137 decomp_dummy.resize(1, 0);
138 decomp_param.clear(); // Avoid propagating spurious values
139 decomp_param.set(decomp_dummy);
140 }
141
142
143 // Dump graph
144 if (coeffsDict_.getOrDefault("writeGraph", false))
145 {
146 OFstream str(graphPath_ + ".grf");
147
148 Info<< "Dumping Scotch graph file to " << str.name() << nl
149 << "Use this in combination with gpart." << endl;
150
151 const label numConnect = adjncy.size();
152
153 // Version 0 = Graph file (.grf)
154 str << "0" << nl;
155
156 // Number of vertices,
157 // number of edges (connections)
158 str << numCells << ' ' << numConnect << nl;
159
160 // Numbering starts from 0
161 // 100*hasVertlabels+10*hasEdgeWeights+1*hasVertWeights
162 str << "0 000" << nl;
163
164 for (label celli = 0; celli < numCells; ++celli)
165 {
166 const label beg = xadj[celli];
167 const label end = xadj[celli+1];
168
169 str << (end-beg); // size
170
171 for (label i = beg; i < end; ++i)
172 {
173 str << ' ' << adjncy[i];
174 }
175 str << nl;
176 }
177 }
178
179
180 // Make repeatable
181 SCOTCH_randomReset();
182
183 // Strategy
184 // ~~~~~~~~
185
186 // Default.
187 SCOTCH_Strat stradat;
188 check
189 (
190 SCOTCH_stratInit(&stradat),
191 "SCOTCH_stratInit"
192 );
193
194 string strategy;
195 if (coeffsDict_.readIfPresent("strategy", strategy))
196 {
197 DebugInfo << "scotchDecomp : Using strategy " << strategy << endl;
198
199 SCOTCH_stratGraphMap(&stradat, strategy.c_str());
200 //fprintf(stdout, "S\tStrat=");
201 //SCOTCH_stratSave(&stradat, stdout);
202 //fprintf(stdout, "\n");
203 }
204
205
206 // Graph
207 // ~~~~~
208
209 // Check for externally provided cellweights and if so initialise weights
210
211 bool hasWeights = !cWeights.empty();
212
213 // Note: min, not gMin since routine runs on master only.
214 const scalar minWeights = hasWeights ? min(cWeights) : scalar(1);
215
216 if (minWeights <= 0)
217 {
218 hasWeights = false;
220 << "Illegal minimum weight " << minWeights
221 << " ... ignoring"
222 << endl;
223 }
224 else if (hasWeights && (cWeights.size() != numCells))
225 {
226 FatalErrorInFunction
227 << "Number of cell weights " << cWeights.size()
228 << " does not equal number of cells " << numCells
229 << exit(FatalError);
230 }
231
232
233 List<SCOTCH_Num> velotab;
234
235 if (hasWeights)
236 {
237 scalar rangeScale(1);
238
239 const scalar velotabSum = sum(cWeights)/minWeights;
240
241 const scalar upperRange = static_cast<scalar>
242 (
243 std::numeric_limits<SCOTCH_Num>::max()-1
244 );
245
246 if (velotabSum > upperRange)
247 {
248 // 0.9 factor of safety to avoid floating point round-off in
249 // rangeScale tipping the subsequent sum over the integer limit.
250 rangeScale = 0.9*upperRange/velotabSum;
251
253 << "Sum of weights overflows SCOTCH_Num: " << velotabSum
254 << ", compressing by factor " << rangeScale << endl;
255 }
256
257 {
258 // Convert to integers.
259 velotab.resize(cWeights.size());
260
261 forAll(velotab, i)
262 {
263 velotab[i] = static_cast<SCOTCH_Num>
264 (
265 ((cWeights[i]/minWeights - 1)*rangeScale) + 1
266 );
267 }
268 }
269 }
270 else
271 {
272 // HACK: specify uniform weights
273 // - seems that scotch takes different code paths internally
274 // if weights are not specified (issue #3063)
275
276 velotab.resize(numCells);
277 velotab = static_cast<SCOTCH_Num>(1);
278 }
279
280
281 //
282 // Decomposition graph
283 //
284
285 SCOTCH_Graph grafdat;
286 check
287 (
288 SCOTCH_graphInit(&grafdat),
289 "SCOTCH_graphInit"
290 );
291 check
292 (
293 SCOTCH_graphBuild
294 (
295 &grafdat, // Graph to build
296 0, // Base for indexing (C-style)
297
298 numCells, // Number of vertices [== nCells]
299 xadj_param().cdata(), // verttab, start index per cell into adjncy
300 nullptr, // vendtab, end index (nullptr == automatic)
301
302 velotab.cdata(), // velotab, vertex weights
303 nullptr, // Vertex labels (nullptr == ignore)
304
305 adjncy.size(), // Number of graph edges
306 adjncy_param().cdata(), // Edge array
307 nullptr // Edge weights (nullptr == ignore)
308 ),
309 "SCOTCH_graphBuild"
310 );
311 check
312 (
313 SCOTCH_graphCheck(&grafdat),
314 "SCOTCH_graphCheck"
315 );
316
317
318 // Architecture
319 // ~~~~~~~~~~~~
320 // (fully connected network topology since using switch)
321
322 SCOTCH_Arch archdat;
323 check
324 (
325 SCOTCH_archInit(&archdat),
326 "SCOTCH_archInit"
327 );
328
329 List<SCOTCH_Num> procWeights;
330
331 // Do optional tree-like/multi-level decomposition by specifying
332 // domains/weights
333 List<SCOTCH_Num> domains;
334 List<scalar> dWeights;
335
336 if
337 (
338 coeffsDict_.readIfPresent("processorWeights", procWeights)
339 && !procWeights.empty()
340 )
341 {
342 if (procWeights.size() != nDomains_)
343 {
345 << "processorWeights (" << procWeights.size()
346 << ") != number of domains (" << nDomains_ << ")" << nl
347 << exit(FatalIOError);
348 }
349
351 << "scotchDecomp : Using procesor weights "
352 << procWeights << endl;
353
354 check
355 (
356 SCOTCH_archCmpltw(&archdat, nDomains_, procWeights.cdata()),
357 "SCOTCH_archCmpltw"
358 );
359 }
360 else if
361 (
362 coeffsDict_.readIfPresent("domains", domains, keyType::LITERAL)
363 && coeffsDict_.readIfPresent("domainWeights", dWeights, keyType::LITERAL)
364 )
365 {
366 // multi-level
367
368 label nTotal = 1;
369 for (const label n : domains)
370 {
371 nTotal *= n;
372 }
373
374 if (nTotal < nDomains())
375 {
376 const label sz = domains.size();
377 domains.setSize(sz+1);
378 dWeights.setSize(sz+1);
379 for (label i = sz-1; i >= 0; i--)
380 {
381 domains[i+1] = domains[i];
382 dWeights[i+1] = dWeights[i];
383 }
384
385 if (nDomains() % nTotal)
386 {
388 << "Top level decomposition specifies " << nDomains()
389 << " domains which is not equal to the product of"
390 << " all sub domains " << nTotal
391 << exit(FatalError);
392 }
393
394 domains[0] = nDomains() / nTotal;
395 dWeights[0] = scalar(1);
396 }
397
398
399 // Note: min, not gMin since routine runs on master only.
400 const scalar minWeights = min(dWeights);
401
402 // Convert to integers.
403 List<SCOTCH_Num> weights(dWeights.size());
404
405 forAll(weights, i)
406 {
407 weights[i] = static_cast<SCOTCH_Num>
408 (
409 (dWeights[i]/minWeights - 1) + 1
410 );
411 }
412
413 check
414 (
415 SCOTCH_archTleaf
416 (
417 &archdat,
418 SCOTCH_Num(domains.size()),
419 domains.cdata(),
420 weights.cdata()
421 ),
422 "SCOTCH_archTleaf"
423 );
424 }
425 else
426 {
427 check
428 (
429 SCOTCH_archCmplt(&archdat, nDomains_),
430 "SCOTCH_archCmplt"
431 );
432
433
434 //- Hack to test clustering. Note that decomp is non-compact
435 // numbers!
436 //
438 //check
439 //(
440 // SCOTCH_archVcmplt(&archdat),
441 // "SCOTCH_archVcmplt"
442 //);
443 //
445 //SCOTCH_Num straval = 0;
448 //
451 //SCOTCH_Num agglomSize = 3;
452 //
454 //check
455 //(
456 // SCOTCH_stratGraphClusterBuild
457 // (
458 // &stradat, // strategy to build
459 // straval, // strategy flags
460 // agglomSize, // cells per cluster
461 // 1.0, // weight?
462 // 0.01 // max load imbalance
463 // ),
464 // "SCOTCH_stratGraphClusterBuild"
465 //);
466 }
467
468
469 //SCOTCH_Mapping mapdat;
470 //SCOTCH_graphMapInit(&grafdat, &mapdat, &archdat, nullptr);
471 //SCOTCH_graphMapCompute(&grafdat, &mapdat, &stradat); /* Perform mapping */
472 //SCOTCH_graphMapExit(&grafdat, &mapdat);
473
474
475 // Hack:switch off fpu error trapping
476 #ifdef FE_NOMASK_ENV
477 int oldExcepts = fedisableexcept
478 (
479 FE_DIVBYZERO
480 | FE_INVALID
481 | FE_OVERFLOW
482 );
483 #endif
484
485 check
486 (
487 SCOTCH_graphMap
488 (
489 &grafdat,
490 &archdat,
491 &stradat, // const SCOTCH_Strat *
492 decomp_param.ref().data() // parttab
493 ),
494 "SCOTCH_graphMap"
495 );
496
497 #ifdef FE_NOMASK_ENV
498 feenableexcept(oldExcepts);
499 #endif
500
501 //decomp.resize(numCells);
502 //check
503 //(
504 // SCOTCH_graphPart
505 // (
506 // &grafdat,
507 // nDomains_, // partnbr
508 // &stradat, // const SCOTCH_Strat *
509 // decomp_param.ref().data() // parttab
510 // ),
511 // "SCOTCH_graphPart"
512 //);
513
514 SCOTCH_graphExit(&grafdat); // Release storage for graph
515 SCOTCH_stratExit(&stradat); // Release storage for strategy
516 SCOTCH_archExit(&archdat); // Release storage for network topology
517
518 return 0;
519}
521
522// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
523
524Foam::scotchDecomp::scotchDecomp(const label numDomains)
525:
526 metisLikeDecomp(numDomains)
527{}
528
529
531(
532 const dictionary& decompDict,
533 const word& regionName
534)
535:
536 metisLikeDecomp(typeName, decompDict, regionName, selectionType::NULL_DICT)
537{}
539
540// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
541
543(
544 const polyMesh& mesh,
545 const pointField& points,
546 const scalarField& pointWeights
547) const
548{
549 // Where to write graph
550 graphPath_ = getGraphPathBase(mesh);
551
553 (
554 mesh,
555 points,
556 pointWeights
557 );
558}
559
560
562(
563 const polyMesh& mesh,
564 const labelList& agglom,
565 const pointField& agglomPoints,
566 const scalarField& agglomWeights
567) const
568{
569 // Where to write graph
570 graphPath_ = getGraphPathBase(mesh);
571
573 (
574 mesh,
575 agglom,
576 agglomPoints,
577 agglomWeights
578 );
579}
580
581
583(
584 const CompactListList<label>& globalCellCells,
585 const pointField& cellCentres,
586 const scalarField& cWeights
587) const
588{
589 // Where to write graph
590 graphPath_ = "scotch.grf";
591
593 (
594 globalCellCells,
595 cellCentres,
596 cWeights
597 );
598}
599
600
602(
603 const labelListList& globalCellCells,
604 const pointField& cellCentres,
605 const scalarField& cWeights
606) const
607{
608 // Where to write graph
609 graphPath_ = "scotch.grf";
610
612 (
613 globalCellCells,
614 cellCentres,
615 cWeights
616 );
617}
618
619
620// ************************************************************************* //
if(maxValue - minValue< SMALL)
label n
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.
A const Field/List wrapper with possible data conversion.
void set(const Container< InputType > &input)
Set adaptor for different input, copying input if required.
A 1D array of objects of type <T>, where the size of the vector is known and used for subscript bound...
Definition List.H:72
void resize_nocopy(const label len)
Adjust allocated size of list without necessarily.
Definition ListI.H:171
void setSize(label n)
Alias for resize().
Definition List.H:536
void resize(const label len)
Adjust allocated size of list.
Definition ListI.H:153
Output to file stream as an OSstream, normally using std::ofstream for the actual output.
Definition OFstream.H:75
virtual const fileName & name() const override
Read/write access to the name of the stream.
Definition OSstream.H:134
A non-const Field/List wrapper with possible data conversion.
void set(Container< InputType > &input, const bool doCopy=true)
Set adaptor for different input, copying input as required.
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
const T * cdata() const noexcept
Return pointer to the underlying array serving as data storage.
Definition UListI.H:267
void size(const label n)
Older name for setAddressableSize.
Definition UList.H:118
Abstract base class for domain decomposition.
selectionType
Selection type when handling the coefficients dictionary.
label nDomains_
Number of domains for the decomposition.
label nDomains() const noexcept
Number of domains.
A list of keyword definitions, which are a keyword followed by a number of values (eg,...
Definition dictionary.H:133
T getOrDefault(const word &keyword, const T &deflt, enum keyType::option matchOpt=keyType::REGEX) const
Find and return a T, or return the given default value. FatalIOError if it is found and the number of...
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
@ LITERAL
String literal.
Definition keyType.H:82
Domain decomposition using METIS-like data structures.
const dictionary & coeffsDict_
Coefficients for all derived methods.
metisLikeDecomp(const metisLikeDecomp &)=delete
No copy construct.
virtual labelList decompose(const polyMesh &mesh, const pointField &points=pointField::null(), const scalarField &pointWeights=scalarField::null()) const
Return for every coordinate the wanted processor number.
Mesh consisting of general polyhedral cells.
Definition polyMesh.H:79
void clear() const noexcept
If object pointer points to valid object: delete object and set pointer to nullptr.
Definition refPtrI.H:303
T & ref() const
Return non-const reference to the contents of a non-null managed pointer.
Definition refPtrI.H:230
Scotch domain decomposition.
virtual label decomposeSerial(const labelList &adjncy, const labelList &xadj, const List< scalar > &cWeights, labelList &decomp) const
Decompose non-parallel.
virtual labelList decompose(const polyMesh &mesh, const pointField &points=pointField::null(), const scalarField &pointWeights=scalarField::null()) const
Return for every coordinate the wanted processor number.
scotchDecomp(const scotchDecomp &)=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
const pointField & points
#define DebugInfo
Report an information message using Foam::Info.
#define WarningInFunction
Report a warning using Foam::Warning.
Namespace for OpenFOAM.
label max(const labelHashSet &set, label maxValue=labelMin)
Find the max value in labelHashSet, optionally limited by second argument.
Definition hashSets.C:40
List< labelList > labelListList
List of labelList.
Definition labelList.H:38
List< label > labelList
A List of labels.
Definition List.H:62
messageStream Info
Information stream (stdout output on master, null elsewhere).
static void check(const int retVal, const char *what)
Field< scalar > scalarField
Specialisation of Field<T> for scalar.
const word GlobalIOList< Tuple2< scalar, vector > >::typeName("scalarVectorTable")
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition Ostream.H:519
label min(const labelHashSet &set, label minValue=labelMax)
Find the min value in labelHashSet, optionally limited by second argument.
Definition hashSets.C:26
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)
dimensioned< Type > sum(const DimensionedField< Type, GeoMesh > &f1, const label comm)
error FatalError
Error stream (stdout output on all processes), with additional 'FOAM FATAL ERROR' header text and sta...
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