GAMGSolverSolve.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) 2016-2021,2023 OpenCFD Ltd.
10  Copyright (C) 2023 Huawei (Yu Ankun)
11 -------------------------------------------------------------------------------
12 License
13  This file is part of OpenFOAM.
14 
15  OpenFOAM is free software: you can redistribute it and/or modify it
16  under the terms of the GNU General Public License as published by
17  the Free Software Foundation, either version 3 of the License, or
18  (at your option) any later version.
19 
20  OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
21  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
22  FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
23  for more details.
24 
25  You should have received a copy of the GNU General Public License
26  along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
27 
28 \*---------------------------------------------------------------------------*/
29 
30 #include "GAMGSolver.H"
31 #include "SubField.H"
32 #include "PrecisionAdaptor.H"
33 
34 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
35 
37 (
38  scalarField& psi_s,
39  const scalarField& source,
40  const direction cmpt
41 ) const
42 {
44  solveScalarField& psi = tpsi.ref();
45 
47 
48  // Setup class containing solver performance data
49  solverPerformance solverPerf(typeName, fieldName_);
50 
51  // Calculate A.psi used to calculate the initial residual
52  solveScalarField Apsi(psi.size());
54 
55  // Create the storage for the finestCorrection which may be used as a
56  // temporary in normFactor
57  solveScalarField finestCorrection(psi.size());
58 
59  // Calculate normalisation factor
60  solveScalar normFactor =
61  this->normFactor(psi, tsource(), Apsi, finestCorrection);
62 
63  if ((log_ >= 2) || (debug >= 2))
64  {
65  Pout<< " Normalisation factor = " << normFactor << endl;
66  }
67 
68  // Calculate initial finest-grid residual field
69  solveScalarField finestResidual(tsource() - Apsi);
70 
72  (
74  fieldName_,
75  true
76  );
77 
78  // Calculate normalised residual for convergence test
79  solverPerf.initialResidual() = gSumMag
80  (
81  finestResidual,
82  matrix().mesh().comm()
83  )/normFactor;
84  solverPerf.finalResidual() = solverPerf.initialResidual();
85 
86 
87  // Check convergence, solve if not converged
88  if
89  (
90  minIter_ > 0
91  || !solverPerf.checkConvergence(tolerance_, relTol_, log_)
92  )
93  {
94  // Create coarse grid correction fields
95  PtrList<solveScalarField> coarseCorrFields;
96 
97  // Create coarse grid sources
98  PtrList<solveScalarField> coarseSources;
99 
100  // Create the smoothers for all levels
102 
103  // Scratch fields if processor-agglomerated coarse level meshes
104  // are bigger than original. Usually not needed
105  solveScalarField scratch1;
106  solveScalarField scratch2;
107 
108  // Initialise the above data structures
109  initVcycle
110  (
111  coarseCorrFields,
112  coarseSources,
113  smoothers,
114  scratch1,
115  scratch2
116  );
117 
118  do
119  {
120  Vcycle
121  (
122  smoothers,
123  psi,
124  source,
125  Apsi,
126  finestCorrection,
127  finestResidual,
128 
129  (scratch1.size() ? scratch1 : Apsi),
130  (scratch2.size() ? scratch2 : finestCorrection),
131 
132  coarseCorrFields,
133  coarseSources,
134  cmpt
135  );
136 
137  // Calculate finest level residual field
139  finestResidual = tsource();
140  finestResidual -= Apsi;
141 
142  solverPerf.finalResidual() = gSumMag
143  (
144  finestResidual,
145  matrix().mesh().comm()
146  )/normFactor;
147 
148  if ((log_ >= 2) || (debug >= 2))
149  {
150  solverPerf.print(Info.masterStream(matrix().mesh().comm()));
151  }
152  } while
153  (
154  (
155  ++solverPerf.nIterations() < maxIter_
156  && !solverPerf.checkConvergence(tolerance_, relTol_, log_)
157  )
158  || solverPerf.nIterations() < minIter_
159  );
160  }
161 
163  (
165  fieldName_,
166  false
167  );
168 
169  return solverPerf;
170 }
171 
172 
173 void Foam::GAMGSolver::Vcycle
174 (
175  const PtrList<lduMatrix::smoother>& smoothers,
177  const scalarField& source,
178  solveScalarField& Apsi,
179  solveScalarField& finestCorrection,
180  solveScalarField& finestResidual,
181 
182  solveScalarField& scratch1,
183  solveScalarField& scratch2,
184 
185  PtrList<solveScalarField>& coarseCorrFields,
186  PtrList<solveScalarField>& coarseSources,
187  const direction cmpt
188 ) const
189 {
190  //debug = 2;
191 
192  const label coarsestLevel = matrixLevels_.size() - 1;
193 
194  // Restrict finest grid residual for the next level up.
195  agglomeration_.restrictField(coarseSources[0], finestResidual, 0, true);
196 
197  if (nPreSweeps_ && ((log_ >= 2) || (debug >= 2)))
198  {
199  Pout<< "Pre-smoothing scaling factors: ";
200  }
201 
202 
203  // Residual restriction (going to coarser levels)
204  for (label leveli = 0; leveli < coarsestLevel; leveli++)
205  {
206  if (coarseSources.set(leveli + 1))
207  {
208  // If the optional pre-smoothing sweeps are selected
209  // smooth the coarse-grid field for the restricted source
210  if (nPreSweeps_)
211  {
212  coarseCorrFields[leveli] = 0.0;
213 
214  smoothers[leveli + 1].scalarSmooth
215  (
216  coarseCorrFields[leveli],
217  coarseSources[leveli], //coarseSource,
218  cmpt,
219  min
220  (
221  nPreSweeps_ + preSweepsLevelMultiplier_*leveli,
222  maxPreSweeps_
223  )
224  );
225 
226  // Scale coarse-grid correction field
227  // but not on the coarsest level because it evaluates to 1
228  if (scaleCorrection_ && leveli < coarsestLevel - 1)
229  {
231  (
232  scratch1,
233  coarseCorrFields[leveli].size()
234  );
235 
236  scale
237  (
238  coarseCorrFields[leveli],
239  const_cast<solveScalarField&>
240  (
241  ACf.operator const solveScalarField&()
242  ),
243  matrixLevels_[leveli],
244  interfaceLevelsBouCoeffs_[leveli],
245  interfaceLevels_[leveli],
246  coarseSources[leveli],
247  cmpt
248  );
249  }
250 
251  // Correct the residual with the new solution
252  // residual can be used by fusing Amul with b-Amul
253  matrixLevels_[leveli].residual
254  (
255  coarseSources[leveli],
256  coarseCorrFields[leveli],
257  ConstPrecisionAdaptor<scalar, solveScalar>
258  (
259  coarseSources[leveli]
260  )(),
261  interfaceLevelsBouCoeffs_[leveli],
262  interfaceLevels_[leveli],
263  cmpt
264  );
265  }
266 
267  // Residual is equal to source
268  agglomeration_.restrictField
269  (
270  coarseSources[leveli + 1],
271  coarseSources[leveli],
272  leveli + 1,
273  true
274  );
275  }
276  }
277 
278  if (nPreSweeps_ && ((log_ >= 2) || (debug >= 2)))
279  {
280  Pout<< endl;
281  }
282 
283 
284  // Solve Coarsest level with either an iterative or direct solver
285  if (coarseCorrFields.set(coarsestLevel))
286  {
287  solveCoarsestLevel
288  (
289  coarseCorrFields[coarsestLevel],
290  coarseSources[coarsestLevel]
291  );
292  }
293 
294  if ((log_ >= 2) || (debug >= 2))
295  {
296  Pout<< "Post-smoothing scaling factors: ";
297  }
298 
299  // Smoothing and prolongation of the coarse correction fields
300  // (going to finer levels)
301 
302  solveScalarField dummyField(0);
303 
304  for (label leveli = coarsestLevel - 1; leveli >= 0; leveli--)
305  {
306  if (coarseCorrFields.set(leveli))
307  {
308  // Create a field for the pre-smoothed correction field
309  // as a sub-field of the finestCorrection which is not
310  // currently being used
311  solveScalarField::subField preSmoothedCoarseCorrField
312  (
313  scratch2,
314  coarseCorrFields[leveli].size()
315  );
316 
317  // Only store the preSmoothedCoarseCorrField if pre-smoothing is
318  // used
319  if (nPreSweeps_)
320  {
321  preSmoothedCoarseCorrField = coarseCorrFields[leveli];
322  }
323 
324  agglomeration_.prolongField
325  (
326  coarseCorrFields[leveli],
327  (
328  coarseCorrFields.set(leveli + 1)
329  ? coarseCorrFields[leveli + 1]
330  : dummyField // dummy value
331  ),
332  leveli + 1,
333  true
334  );
335 
336 
337  // Create A.psi for this coarse level as a sub-field of Apsi
339  (
340  scratch1,
341  coarseCorrFields[leveli].size()
342  );
343  solveScalarField& ACfRef =
344  const_cast<solveScalarField&>
345  (
346  ACf.operator const solveScalarField&()
347  );
348 
349  if (interpolateCorrection_) //&& leveli < coarsestLevel - 2)
350  {
351  if
352  (
353  coarseCorrFields.set(leveli+1)
354  && (
355  matrixLevels_[leveli].mesh().comm()
356  == matrixLevels_[leveli+1].mesh().comm()
357  )
358  )
359  {
360  // Normal operation : have both coarse level and fine
361  // level. No processor agglomeration
363  (
364  coarseCorrFields[leveli],
365  ACfRef,
366  matrixLevels_[leveli],
367  interfaceLevelsBouCoeffs_[leveli],
368  interfaceLevels_[leveli],
369  agglomeration_.restrictAddressing(leveli + 1),
370  coarseCorrFields[leveli + 1],
371  cmpt
372  );
373  }
374  else
375  {
377  (
378  coarseCorrFields[leveli],
379  ACfRef,
380  matrixLevels_[leveli],
381  interfaceLevelsBouCoeffs_[leveli],
382  interfaceLevels_[leveli],
383  cmpt
384  );
385  }
386  }
387 
388  // Scale coarse-grid correction field
389  // but not on the coarsest level because it evaluates to 1
390  if
391  (
392  scaleCorrection_
393  && (interpolateCorrection_ || leveli < coarsestLevel - 1)
394  )
395  {
396  scale
397  (
398  coarseCorrFields[leveli],
399  ACfRef,
400  matrixLevels_[leveli],
401  interfaceLevelsBouCoeffs_[leveli],
402  interfaceLevels_[leveli],
403  coarseSources[leveli],
404  cmpt
405  );
406  }
407 
408  // Only add the preSmoothedCoarseCorrField if pre-smoothing is
409  // used
410  if (nPreSweeps_)
411  {
412  coarseCorrFields[leveli] += preSmoothedCoarseCorrField;
413  }
414 
415  smoothers[leveli + 1].scalarSmooth
416  (
417  coarseCorrFields[leveli],
418  coarseSources[leveli], //coarseSource,
419  cmpt,
420  min
421  (
422  nPostSweeps_ + postSweepsLevelMultiplier_*leveli,
423  maxPostSweeps_
424  )
425  );
426  }
427  }
428 
429  // Prolong the finest level correction
430  agglomeration_.prolongField
431  (
432  finestCorrection,
433  coarseCorrFields[0],
434  0,
435  true
436  );
437 
438  if (interpolateCorrection_)
439  {
441  (
442  finestCorrection,
443  Apsi,
444  matrix_,
445  interfaceBouCoeffs_,
446  interfaces_,
447  agglomeration_.restrictAddressing(0),
448  coarseCorrFields[0],
449  cmpt
450  );
451  }
452 
453  if (scaleCorrection_)
454  {
455  // Scale the finest level correction
456  scale
457  (
458  finestCorrection,
459  Apsi,
460  matrix_,
461  interfaceBouCoeffs_,
462  interfaces_,
463  finestResidual,
464  cmpt
465  );
466  }
467 
468  forAll(psi, i)
469  {
470  psi[i] += finestCorrection[i];
471  }
472 
473  smoothers[0].smooth
474  (
475  psi,
476  source,
477  cmpt,
478  nFinestSweeps_
479  );
480 }
481 
482 
483 void Foam::GAMGSolver::initVcycle
484 (
485  PtrList<solveScalarField>& coarseCorrFields,
486  PtrList<solveScalarField>& coarseSources,
487  PtrList<lduMatrix::smoother>& smoothers,
488  solveScalarField& scratch1,
489  solveScalarField& scratch2
490 ) const
491 {
492  label maxSize = matrix_.diag().size();
493 
494  coarseCorrFields.setSize(matrixLevels_.size());
495  coarseSources.setSize(matrixLevels_.size());
496  smoothers.setSize(matrixLevels_.size() + 1);
497 
498  // Create the smoother for the finest level
499  smoothers.set
500  (
501  0,
503  (
504  fieldName_,
505  matrix_,
506  interfaceBouCoeffs_,
507  interfaceIntCoeffs_,
508  interfaces_,
509  controlDict_
510  )
511  );
512 
513  forAll(matrixLevels_, leveli)
514  {
515  if (agglomeration_.nCells(leveli) >= 0)
516  {
517  label nCoarseCells = agglomeration_.nCells(leveli);
518 
519  coarseSources.set(leveli, new solveScalarField(nCoarseCells));
520  }
521 
522  if (matrixLevels_.set(leveli))
523  {
524  const lduMatrix& mat = matrixLevels_[leveli];
525 
526  label nCoarseCells = mat.diag().size();
527 
528  maxSize = max(maxSize, nCoarseCells);
529 
530  coarseCorrFields.set(leveli, new solveScalarField(nCoarseCells));
531 
532  smoothers.set
533  (
534  leveli + 1,
536  (
537  fieldName_,
538  matrixLevels_[leveli],
539  interfaceLevelsBouCoeffs_[leveli],
540  interfaceLevelsIntCoeffs_[leveli],
541  interfaceLevels_[leveli],
542  controlDict_
543  )
544  );
545  }
546  }
547 
548  if (maxSize > matrix_.diag().size())
549  {
550  // Allocate some scratch storage
551  scratch1.setSize(maxSize);
552  scratch2.setSize(maxSize);
553  }
554 }
555 
556 
557 Foam::dictionary Foam::GAMGSolver::PCGsolverDict
558 (
559  const scalar tol,
560  const scalar relTol
561 ) const
562 {
563  dictionary dict(IStringStream("solver PCG; preconditioner DIC;")());
564  dict.add("tolerance", tol);
565  dict.add("relTol", relTol);
566 
567  return dict;
568 }
569 
570 
571 Foam::dictionary Foam::GAMGSolver::PBiCGStabSolverDict
572 (
573  const scalar tol,
574  const scalar relTol
575 ) const
576 {
577  dictionary dict(IStringStream("solver PBiCGStab; preconditioner DILU;")());
578  dict.add("tolerance", tol);
579  dict.add("relTol", relTol);
580 
581  return dict;
582 }
583 
584 
585 void Foam::GAMGSolver::solveCoarsestLevel
586 (
587  solveScalarField& coarsestCorrField,
588  const solveScalarField& coarsestSource
589 ) const
590 {
591  const label coarsestLevel = matrixLevels_.size() - 1;
592 
593  const label coarseComm = matrixLevels_[coarsestLevel].mesh().comm();
594 
595  if (directSolveCoarsest_)
596  {
597  PrecisionAdaptor<scalar, solveScalar> tcorrField(coarsestCorrField);
598 
599  coarsestLUMatrixPtr_->solve
600  (
601  tcorrField.ref(),
602  ConstPrecisionAdaptor<scalar, solveScalar>(coarsestSource)()
603  );
604  }
605  //else if
606  //(
607  // agglomeration_.processorAgglomerate()
608  // && procMatrixLevels_.set(coarsestLevel)
609  //)
610  //{
611  // //const labelList& agglomProcIDs = agglomeration_.agglomProcIDs
612  // //(
613  // // coarsestLevel
614  // //);
615  // //
616  // //scalarField allSource;
617  // //
618  // //globalIndex cellOffsets;
619  // //if (Pstream::myProcNo(coarseComm) == agglomProcIDs[0])
620  // //{
621  // // cellOffsets.offsets() =
622  // // agglomeration_.cellOffsets(coarsestLevel);
623  // //}
624  // //
625  // //cellOffsets.gather
626  // //(
627  // // coarseComm,
628  // // agglomProcIDs,
629  // // coarsestSource,
630  // // allSource
631  // //);
632  // //
633  // //scalarField allCorrField;
634  // //solverPerformance coarseSolverPerf;
635  //
636  // label solveComm = agglomeration_.procCommunicator(coarsestLevel);
637  //
638  // coarsestCorrField = 0;
639  // solverPerformance coarseSolverPerf;
640  //
641  // if (Pstream::myProcNo(solveComm) != -1)
642  // {
643  // const lduMatrix& allMatrix = procMatrixLevels_[coarsestLevel];
644  //
645  // {
646  // Pout<< "** Master:Solving on comm:" << solveComm
647  // << " with procs:" << UPstream::procID(solveComm) << endl;
648  //
649  // if (allMatrix.asymmetric())
650  // {
651  // coarseSolverPerf = PBiCGStab
652  // (
653  // "coarsestLevelCorr",
654  // allMatrix,
655  // procInterfaceLevelsBouCoeffs_[coarsestLevel],
656  // procInterfaceLevelsIntCoeffs_[coarsestLevel],
657  // procInterfaceLevels_[coarsestLevel],
658  // PBiCGStabSolverDict(tolerance_, relTol_)
659  // ).solve
660  // (
661  // coarsestCorrField,
662  // coarsestSource
663  // );
664  // }
665  // else
666  // {
667  // coarseSolverPerf = PCG
668  // (
669  // "coarsestLevelCorr",
670  // allMatrix,
671  // procInterfaceLevelsBouCoeffs_[coarsestLevel],
672  // procInterfaceLevelsIntCoeffs_[coarsestLevel],
673  // procInterfaceLevels_[coarsestLevel],
674  // PCGsolverDict(tolerance_, relTol_)
675  // ).solve
676  // (
677  // coarsestCorrField,
678  // coarsestSource
679  // );
680  // }
681  // }
682  // }
683  //
684  // Pout<< "done master solve." << endl;
685  //
686  // //// Scatter to all processors
687  // //coarsestCorrField.setSize(coarsestSource.size());
688  // //cellOffsets.scatter
689  // //(
690  // // coarseComm,
691  // // agglomProcIDs,
692  // // allCorrField,
693  // // coarsestCorrField
694  // //);
695  //
696  // if (debug >= 2)
697  // {
698  // coarseSolverPerf.print(Info.masterStream(coarseComm));
699  // }
700  //
701  // Pout<< "procAgglom: coarsestSource :" << coarsestSource << endl;
702  // Pout<< "procAgglom: coarsestCorrField:" << coarsestCorrField << endl;
703  //}
704  else
705  {
706  coarsestCorrField = 0;
707  const solverPerformance coarseSolverPerf
708  (
709  coarsestSolverPtr_->scalarSolve
710  (
711  coarsestCorrField,
712  coarsestSource
713  )
714  );
715 
716  if ((log_ >= 2) || debug)
717  {
718  coarseSolverPerf.print(Info.masterStream(coarseComm));
719  }
720  }
721 }
722 
723 
724 // ************************************************************************* //
dictionary dict
void size(const label n)
Older name for setAddressableSize.
Definition: UList.H:116
uint8_t direction
Definition: direction.H:48
Field< solveScalar > solveScalarField
A list of keyword definitions, which are a keyword followed by a number of values (eg...
Definition: dictionary.H:120
bool interpolate(const vector &p1, const vector &p2, const vector &o, vector &n, scalar l)
Definition: curveTools.C:75
void setResidualField(const scalarField &residual, const word &fieldName, const bool initial) const
Set the residual field using an IOField on the object registry if it exists.
Definition: lduMatrix.C:326
label max(const labelHashSet &set, label maxValue=labelMin)
Find the max value in labelHashSet, optionally limited by second argument.
Definition: hashSets.C:40
const FieldField< Field, scalar > & interfaceBouCoeffs_
Definition: lduMatrix.H:137
const lduMatrix & matrix() const noexcept
Definition: lduMatrix.H:306
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:487
entry * add(entry *entryPtr, bool mergeEntry=false)
Add a new entry.
Definition: dictionary.C:637
virtual solverPerformance solve(scalarField &psi, const scalarField &source, const direction cmpt=0) const
Solve.
A non-const Field/List wrapper with possible data conversion.
bool checkConvergence(const Type &tolerance, const Type &relTolerance, const int logLevel=0)
Check, store and return convergence.
int log_
Verbosity level for solver output statements.
Definition: lduMatrix.H:149
label minIter_
Minimum number of iterations in the solver.
Definition: lduMatrix.H:154
#define forAll(list, i)
Loop across all elements in list.
Definition: stdFoam.H:414
dynamicFvMesh & mesh
scalar tolerance_
Final convergence tolerance.
Definition: lduMatrix.H:169
const labelType & nIterations() const noexcept
Return number of iterations.
void Amul(solveScalarField &, const tmp< solveScalarField > &, const FieldField< Field, scalar > &, const lduInterfaceFieldPtrsList &, const direction cmpt) const
Matrix multiplication with updated interfaces.
SolverPerformance is the class returned by the LduMatrix solver containing performance statistics...
label maxIter_
Maximum number of iterations in the solver.
Definition: lduMatrix.H:159
A const Field/List wrapper with possible data conversion.
label min(const labelHashSet &set, label minValue=labelMax)
Find the min value in labelHashSet, optionally limited by second argument.
Definition: hashSets.C:26
const T * set(const label i) const
Return const pointer to element (can be nullptr), or nullptr for out-of-range access (ie...
Definition: PtrList.H:159
const Type & finalResidual() const noexcept
Return final residual.
const lduMatrix & matrix_
Definition: lduMatrix.H:136
int debug
Static debugging option.
Container< Type > & ref() const
Return non-const reference to the contents of a non-null managed pointer.
Definition: refPtrI.H:230
lduInterfaceFieldPtrsList interfaces_
Definition: lduMatrix.H:139
solveScalarField::cmptType normFactor(const solveScalarField &psi, const solveScalarField &source, const solveScalarField &Apsi, solveScalarField &tmpField, const lduMatrix::normTypes normType) const
Return the matrix norm using the specified norm method.
typeOfMag< Type >::type gSumMag(const FieldField< Field, Type > &f)
void print(Ostream &os) const
Print summary of solver performance to the given stream.
OSstream & masterStream(const label communicator)
Return OSstream for output operations on the master process only, Snull on other processes.
A list of pointers to objects of type <T>, with allocation/deallocation management of the pointers...
Definition: List.H:55
static autoPtr< smoother > New(const word &fieldName, const lduMatrix &matrix, const FieldField< Field, scalar > &interfaceBouCoeffs, const FieldField< Field, scalar > &interfaceIntCoeffs, const lduInterfaceFieldPtrsList &interfaces, const dictionary &solverControls)
Return a new smoother.
messageStream Info
Information stream (stdout output on master, null elsewhere)
SubField< solveScalar > subField
Declare type of subField.
Definition: Field.H:128
const volScalarField & psi
prefixOSstream Pout
OSstream wrapped stdout (std::cout) with parallel prefix.
SolverPerformance< scalar > solverPerformance
SolverPerformance instantiated for a scalar.
scalar relTol_
Convergence tolerance relative to the initial.
Definition: lduMatrix.H:174
const Type & initialResidual() const noexcept
Return initial residual.