GAMGSolverSolve.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) 2011-2026 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 "GAMGSolver.H"
27 #include "diagonalSolver.H"
28 #include "PCG.H"
29 #include "PBiCGStab.H"
30 #include "SubField.H"
31 
32 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
33 
35 (
37  const scalarField& source,
38  const direction cmpt
39 ) const
40 {
41  // Setup class containing solver performance data
42  solverPerformance solverPerf(typeName, fieldName_);
43 
44  // Calculate A.psi used to calculate the initial residual
45  scalarField Apsi(psi.size());
47 
48  // Create the storage for the finestCorrection which may be used as a
49  // temporary in normFactor
50  scalarField finestCorrection(psi.size());
51 
52  // Calculate normalisation factor
53  scalar normFactor = this->normFactor(psi, source, Apsi, finestCorrection);
54 
55  if (debug >= 2)
56  {
57  Pout<< " Normalisation factor = " << normFactor << endl;
58  }
59 
60  // Calculate initial finest-grid residual field
61  scalarField finestResidual(source - Apsi);
62 
63  // Calculate normalised residual for convergence test
64  solverPerf.initialResidual() = gSumMag
65  (
66  finestResidual,
67  matrix().mesh().comm()
68  )/normFactor;
69  solverPerf.finalResidual() = solverPerf.initialResidual();
70 
71 
72  // Check convergence, solve if not converged
73  if
74  (
75  minIter_ > 0
76  || !solverPerf.checkConvergence(tolerance_, relTol_)
77  )
78  {
79  // Create coarse grid correction fields
80  PtrList<scalarField> coarseCorrFields;
81 
82  // Create coarse grid sources
83  PtrList<scalarField> coarseSources;
84 
85  // Create the smoothers for all levels
87 
88  // Scratch fields if processor-agglomerated coarse level meshes
89  // are bigger than original. Usually not needed
90  scalarField scratch1;
91  scalarField scratch2;
92 
93  // Initialise the above data structures
94  initVcycle
95  (
96  coarseCorrFields,
97  coarseSources,
98  smoothers,
99  scratch1,
100  scratch2
101  );
102 
103  do
104  {
105  Vcycle
106  (
107  smoothers,
108  psi,
109  source,
110  Apsi,
111  finestCorrection,
112  finestResidual,
113 
114  (scratch1.size() ? scratch1 : Apsi),
115  (scratch2.size() ? scratch2 : finestCorrection),
116 
117  coarseCorrFields,
118  coarseSources,
119  cmpt
120  );
121 
122  // Calculate finest level residual field
124  finestResidual = source;
125  finestResidual -= Apsi;
126 
127  solverPerf.finalResidual() = gSumMag
128  (
129  finestResidual,
130  matrix().mesh().comm()
131  )/normFactor;
132 
133  if (debug >= 2)
134  {
135  solverPerf.print(Info(matrix().mesh().comm()));
136  }
137  } while
138  (
139  (
140  ++solverPerf.nIterations() < maxIter_
141  && !solverPerf.checkConvergence(tolerance_, relTol_)
142  )
143  || solverPerf.nIterations() < minIter_
144  );
145  }
146 
147  return solverPerf;
148 }
149 
150 
151 void Foam::GAMGSolver::Vcycle
152 (
153  const PtrList<lduMatrix::smoother>& smoothers,
154  scalarField& psi,
155  const scalarField& source,
156  scalarField& Apsi,
157  scalarField& finestCorrection,
158  scalarField& finestResidual,
159 
160  scalarField& scratch1,
161  scalarField& scratch2,
162 
163  PtrList<scalarField>& coarseCorrFields,
164  PtrList<scalarField>& coarseSources,
165  const direction cmpt
166 ) const
167 {
168  // debug = 2;
169 
170  const label coarsestLevel = matrixLevels_.size() - 1;
171 
172  // Restrict finest grid residual for the next level up.
173  agglomeration_.restrictField(coarseSources[0], finestResidual, 0, true);
174 
175  if (debug >= 2 && nPreSweeps_)
176  {
177  Pout<< "Pre-smoothing scaling factors: ";
178  }
179 
180 
181  // Residual restriction (going to coarser levels)
182  for (label leveli = 0; leveli < coarsestLevel; leveli++)
183  {
184  if (coarseSources.set(leveli + 1))
185  {
186  // If the optional pre-smoothing sweeps are selected
187  // smooth the coarse-grid field for the restricted source
188  if (nPreSweeps_)
189  {
190  coarseCorrFields[leveli] = 0.0;
191 
192  smoothers[leveli + 1].smooth
193  (
194  coarseCorrFields[leveli],
195  coarseSources[leveli],
196  cmpt,
197  min
198  (
199  nPreSweeps_ + preSweepsLevelMultiplier_*leveli,
200  maxPreSweeps_
201  )
202  );
203 
205  (
206  scratch1,
207  coarseCorrFields[leveli].size()
208  );
209 
210  // Scale coarse-grid correction field
211  // but not on the coarsest level because it evaluates to 1
212  if (scaleCorrection_ && leveli < coarsestLevel - 1)
213  {
214  scale
215  (
216  coarseCorrFields[leveli],
217  const_cast<scalarField&>
218  (
219  ACf.operator const scalarField&()
220  ),
221  matrixLevels_[leveli],
222  interfaceLevelsBouCoeffs_[leveli],
223  interfaceLevels_[leveli],
224  coarseSources[leveli],
225  cmpt
226  );
227  }
228 
229  // Correct the residual with the new solution
230  matrixLevels_[leveli].Amul
231  (
232  const_cast<scalarField&>
233  (
234  ACf.operator const scalarField&()
235  ),
236  coarseCorrFields[leveli],
237  interfaceLevelsBouCoeffs_[leveli],
238  interfaceLevels_[leveli],
239  cmpt
240  );
241 
242  coarseSources[leveli] -= ACf;
243  }
244 
245  // Residual is equal to source
246  agglomeration_.restrictField
247  (
248  coarseSources[leveli + 1],
249  coarseSources[leveli],
250  leveli + 1,
251  true
252  );
253  }
254  }
255 
256  if (debug >= 2 && nPreSweeps_)
257  {
258  Pout<< endl;
259  }
260 
261 
262  // Solve Coarsest level with either an iterative or direct solver
263  if (coarseCorrFields.set(coarsestLevel))
264  {
265  solveCoarsestLevel
266  (
267  coarseCorrFields[coarsestLevel],
268  coarseSources[coarsestLevel]
269  );
270  }
271 
272  if (debug >= 2)
273  {
274  Pout<< "Post-smoothing scaling factors: ";
275  }
276 
277  // Smoothing and prolongation of the coarse correction fields
278  // (going to finer levels)
279 
280  scalarField dummyField(0);
281 
282  for (label leveli = coarsestLevel - 1; leveli >= 0; leveli--)
283  {
284  if (coarseCorrFields.set(leveli))
285  {
286  // Create a field for the pre-smoothed correction field
287  // as a sub-field of the finestCorrection which is not
288  // currently being used
289  scalarField::subField preSmoothedCoarseCorrField
290  (
291  scratch2,
292  coarseCorrFields[leveli].size()
293  );
294 
295  // Only store the preSmoothedCoarseCorrField if pre-smoothing is
296  // used
297  if (nPreSweeps_)
298  {
299  preSmoothedCoarseCorrField = coarseCorrFields[leveli];
300  }
301 
302  agglomeration_.prolongField
303  (
304  coarseCorrFields[leveli],
305  (
306  coarseCorrFields.set(leveli + 1)
307  ? coarseCorrFields[leveli + 1]
308  : dummyField // dummy value
309  ),
310  leveli + 1,
311  true
312  );
313 
314 
315  // Create A.psi for this coarse level as a sub-field of Apsi
317  (
318  scratch1,
319  coarseCorrFields[leveli].size()
320  );
321  scalarField& ACfRef =
322  const_cast<scalarField&>(ACf.operator const scalarField&());
323 
324  if (interpolateCorrection_) //&& leveli < coarsestLevel - 2)
325  {
326  if (coarseCorrFields.set(leveli+1))
327  {
329  (
330  coarseCorrFields[leveli],
331  ACfRef,
332  matrixLevels_[leveli],
333  interfaceLevelsBouCoeffs_[leveli],
334  interfaceLevels_[leveli],
335  agglomeration_.restrictAddressing(leveli + 1),
336  coarseCorrFields[leveli + 1],
337  cmpt
338  );
339  }
340  else
341  {
343  (
344  coarseCorrFields[leveli],
345  ACfRef,
346  matrixLevels_[leveli],
347  interfaceLevelsBouCoeffs_[leveli],
348  interfaceLevels_[leveli],
349  cmpt
350  );
351  }
352  }
353 
354  // Scale coarse-grid correction field
355  // but not on the coarsest level because it evaluates to 1
356  if
357  (
358  scaleCorrection_
359  && (interpolateCorrection_ || leveli < coarsestLevel - 1)
360  )
361  {
362  scale
363  (
364  coarseCorrFields[leveli],
365  ACfRef,
366  matrixLevels_[leveli],
367  interfaceLevelsBouCoeffs_[leveli],
368  interfaceLevels_[leveli],
369  coarseSources[leveli],
370  cmpt
371  );
372  }
373 
374  // Only add the preSmoothedCoarseCorrField if pre-smoothing is
375  // used
376  if (nPreSweeps_)
377  {
378  coarseCorrFields[leveli] += preSmoothedCoarseCorrField;
379  }
380 
381  smoothers[leveli + 1].smooth
382  (
383  coarseCorrFields[leveli],
384  coarseSources[leveli],
385  cmpt,
386  min
387  (
388  nPostSweeps_ + postSweepsLevelMultiplier_*leveli,
389  maxPostSweeps_
390  )
391  );
392  }
393  }
394 
395  // Prolong the finest level correction
396  agglomeration_.prolongField
397  (
398  finestCorrection,
399  coarseCorrFields[0],
400  0,
401  true
402  );
403 
404  if (interpolateCorrection_)
405  {
407  (
408  finestCorrection,
409  Apsi,
410  matrix_,
411  interfaceBouCoeffs_,
412  interfaces_,
413  agglomeration_.restrictAddressing(0),
414  coarseCorrFields[0],
415  cmpt
416  );
417  }
418 
419  if (scaleCorrection_)
420  {
421  // Scale the finest level correction
422  scale
423  (
424  finestCorrection,
425  Apsi,
426  matrix_,
427  interfaceBouCoeffs_,
428  interfaces_,
429  finestResidual,
430  cmpt
431  );
432  }
433 
434  forAll(psi, i)
435  {
436  psi[i] += finestCorrection[i];
437  }
438 
439  smoothers[0].smooth
440  (
441  psi,
442  source,
443  cmpt,
444  nFinestSweeps_
445  );
446 }
447 
448 
449 void Foam::GAMGSolver::initVcycle
450 (
451  PtrList<scalarField>& coarseCorrFields,
452  PtrList<scalarField>& coarseSources,
453  PtrList<lduMatrix::smoother>& smoothers,
454  scalarField& scratch1,
455  scalarField& scratch2
456 ) const
457 {
458  label maxSize = matrix_.diag().size();
459 
460  coarseCorrFields.setSize(matrixLevels_.size());
461  coarseSources.setSize(matrixLevels_.size());
462  smoothers.setSize(matrixLevels_.size() + 1);
463 
464  // Create the smoother for the finest level
465  smoothers.set
466  (
467  0,
469  (
470  fieldName_,
471  matrix_,
472  interfaceBouCoeffs_,
473  interfaceIntCoeffs_,
474  interfaces_,
475  controlDict_
476  )
477  );
478 
479  forAll(matrixLevels_, leveli)
480  {
481  if (agglomeration_.nCells(leveli) >= 0)
482  {
483  label nCoarseCells = agglomeration_.nCells(leveli);
484 
485  coarseSources.set(leveli, new scalarField(nCoarseCells));
486  }
487 
488  if (matrixLevels_.set(leveli))
489  {
490  const lduMatrix& mat = matrixLevels_[leveli];
491 
492  label nCoarseCells = mat.diag().size();
493 
494  maxSize = max(maxSize, nCoarseCells);
495 
496  coarseCorrFields.set(leveli, new scalarField(nCoarseCells));
497 
498  smoothers.set
499  (
500  leveli + 1,
502  (
503  fieldName_,
504  matrixLevels_[leveli],
505  interfaceLevelsBouCoeffs_[leveli],
506  interfaceLevelsIntCoeffs_[leveli],
507  interfaceLevels_[leveli],
508  controlDict_
509  )
510  );
511  }
512  }
513 
514  if (maxSize > matrix_.diag().size())
515  {
516  // Allocate some scratch storage
517  scratch1.setSize(maxSize);
518  scratch2.setSize(maxSize);
519  }
520 }
521 
522 
523 Foam::dictionary Foam::GAMGSolver::PCGsolverDict
524 (
525  const scalar tol,
526  const scalar relTol
527 ) const
528 {
529  dictionary dict(IStringStream("solver PCG; preconditioner DIC;")());
530  dict.add("tolerance", tol);
531  dict.add("relTol", relTol);
532 
533  return dict;
534 }
535 
536 
537 Foam::dictionary Foam::GAMGSolver::PBiCGStabSolverDict
538 (
539  const scalar tol,
540  const scalar relTol
541 ) const
542 {
543  dictionary dict(IStringStream("solver PBiCGStab; preconditioner DILU;")());
544  dict.add("tolerance", tol);
545  dict.add("relTol", relTol);
546 
547  return dict;
548 }
549 
550 
551 void Foam::GAMGSolver::solveCoarsestLevel
552 (
553  scalarField& coarsestCorrField,
554  const scalarField& coarsestSource
555 ) const
556 {
557  const label coarsestLevel = matrixLevels_.size() - 1;
558 
559  label coarseComm = matrixLevels_[coarsestLevel].mesh().comm();
560 
561  if (directSolveCoarsest_)
562  {
563  coarsestLUMatrixPtr_->solve(coarsestCorrField, coarsestSource);
564  }
565  // else if
566  //(
567  // agglomeration_.processorAgglomerate()
568  // && procMatrixLevels_.set(coarsestLevel)
569  //)
570  //{
571  // // const labelList& agglomProcIDs = agglomeration_.agglomProcIDs
572  // //(
573  // // coarsestLevel
574  // //);
575  // //
576  // // scalarField allSource;
577  // //
578  // // globalIndex cellOffsets;
579  // // if (Pstream::myProcNo(coarseComm) == agglomProcIDs[0])
580  // //{
581  // // cellOffsets.offsets() =
582  // // agglomeration_.cellOffsets(coarsestLevel);
583  // //}
584  // //
585  // // cellOffsets.gather
586  // //(
587  // // coarseComm,
588  // // agglomProcIDs,
589  // // coarsestSource,
590  // // allSource
591  // //);
592  // //
593  // // scalarField allCorrField;
594  // // solverPerformance coarseSolverPerf;
595  //
596  // label solveComm = agglomeration_.procCommunicator(coarsestLevel);
597  //
598  // coarsestCorrField = 0;
599  // solverPerformance coarseSolverPerf;
600  //
601  // if (Pstream::myProcNo(solveComm) != -1)
602  // {
603  // const lduMatrix& allMatrix = procMatrixLevels_[coarsestLevel];
604  //
605  // {
606  // Pout<< "** Master:Solving on comm:" << solveComm
607  // << " with procs:" << UPstream::procID(solveComm) << endl;
608  //
609  // if (allMatrix.asymmetric())
610  // {
611  // coarseSolverPerf = PBiCGStab
612  // (
613  // "coarsestLevelCorr",
614  // allMatrix,
615  // procInterfaceLevelsBouCoeffs_[coarsestLevel],
616  // procInterfaceLevelsIntCoeffs_[coarsestLevel],
617  // procInterfaceLevels_[coarsestLevel],
618  // PBiCGStabSolverDict(tolerance_, relTol_)
619  // ).solve
620  // (
621  // coarsestCorrField,
622  // coarsestSource
623  // );
624  // }
625  // else
626  // {
627  // coarseSolverPerf = PCG
628  // (
629  // "coarsestLevelCorr",
630  // allMatrix,
631  // procInterfaceLevelsBouCoeffs_[coarsestLevel],
632  // procInterfaceLevelsIntCoeffs_[coarsestLevel],
633  // procInterfaceLevels_[coarsestLevel],
634  // PCGsolverDict(tolerance_, relTol_)
635  // ).solve
636  // (
637  // coarsestCorrField,
638  // coarsestSource
639  // );
640  // }
641  // }
642  // }
643  //
644  // Pout<< "done master solve." << endl;
645  //
646  // //// Scatter to all processors
647  // // coarsestCorrField.setSize(coarsestSource.size());
648  // // cellOffsets.scatter
649  // //(
650  // // coarseComm,
651  // // agglomProcIDs,
652  // // allCorrField,
653  // // coarsestCorrField
654  // //);
655  //
656  // if (debug >= 2)
657  // {
658  // coarseSolverPerf.print(Info(coarseComm));
659  // }
660  //
661  // Pout<< "procAgglom: coarsestSource :" << coarsestSource << endl;
662  // Pout<< "procAgglom: coarsestCorrField:" << coarsestCorrField << endl;
663  //}
664  else
665  {
666  coarsestCorrField = 0;
667  solverPerformance coarseSolverPerf;
668 
669  if (matrixLevels_[coarsestLevel].diagonal())
670  {
671  coarseSolverPerf = diagonalSolver
672  (
673  "coarsestLevelCorr",
674  matrixLevels_[coarsestLevel],
675  interfaceLevelsBouCoeffs_[coarsestLevel],
676  interfaceLevelsIntCoeffs_[coarsestLevel],
677  interfaceLevels_[coarsestLevel],
678  PBiCGStabSolverDict(tolerance_, relTol_)
679  ).solve
680  (
681  coarsestCorrField,
682  coarsestSource
683  );
684  }
685  else if (matrixLevels_[coarsestLevel].asymmetric())
686  {
687  coarseSolverPerf = PBiCGStab
688  (
689  "coarsestLevelCorr",
690  matrixLevels_[coarsestLevel],
691  interfaceLevelsBouCoeffs_[coarsestLevel],
692  interfaceLevelsIntCoeffs_[coarsestLevel],
693  interfaceLevels_[coarsestLevel],
694  PBiCGStabSolverDict(tolerance_, relTol_)
695  ).solve
696  (
697  coarsestCorrField,
698  coarsestSource
699  );
700  }
701  else
702  {
703  coarseSolverPerf = PCG
704  (
705  "coarsestLevelCorr",
706  matrixLevels_[coarsestLevel],
707  interfaceLevelsBouCoeffs_[coarsestLevel],
708  interfaceLevelsIntCoeffs_[coarsestLevel],
709  interfaceLevels_[coarsestLevel],
710  PCGsolverDict(tolerance_, relTol_)
711  ).solve
712  (
713  coarsestCorrField,
714  coarsestSource
715  );
716  }
717 
718  if (debug >= 2)
719  {
720  coarseSolverPerf.print(Info(coarseComm));
721  }
722  }
723 }
724 
725 
726 // ************************************************************************* //
#define forAll(list, i)
Loop across all elements in list.
Definition: UList.H:433
SubField< scalar > subField
Declare type of subField.
Definition: Field.H:101
virtual solverPerformance solve(scalarField &psi, const scalarField &source, const direction cmpt=0) const
Solve.
void size(const label)
Override size to be inconsistent with allocated storage.
Definition: ListI.H:164
A templated 1D list of pointers to objects of type <T>, where the size of the array is known and used...
Definition: PtrList.H:75
bool set(const label) const
Is element set.
Definition: PtrListI.H:62
SolverPerformance is the class returned by the LduMatrix solver containing performance statistics.
void print(Ostream &os) const
Print summary of solver performance to the given stream.
const labelType & nIterations() const
Return number of iterations.
const Type & initialResidual() const
Return initial residual.
const Type & finalResidual() const
Return final residual.
bool checkConvergence(const Type &tolerance, const Type &relTolerance)
Check, store and return convergence.
A list of keywords followed by any number of values (e.g. words and numbers) or sub-dictionaries.
Definition: dictionary.H:162
bool add(entry *, bool mergeEntry=false)
Add a new entry.
Definition: dictionary.C:1020
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.
const lduMatrix & matrix_
Definition: lduMatrix.H:100
label maxIter_
Maximum number of iterations in the solver.
Definition: lduMatrix.H:112
scalar tolerance_
Final convergence tolerance.
Definition: lduMatrix.H:118
scalar normFactor(const scalarField &psi, const scalarField &source, const scalarField &Apsi, scalarField &tmpField) const
Return the matrix norm used to normalise the residual for the.
lduInterfaceFieldPtrsList interfaces_
Definition: lduMatrix.H:103
const FieldField< Field, scalar > & interfaceBouCoeffs_
Definition: lduMatrix.H:101
label minIter_
Minimum number of iterations in the solver.
Definition: lduMatrix.H:115
scalar relTol_
Convergence tolerance relative to the initial.
Definition: lduMatrix.H:121
const lduMatrix & matrix() const
Definition: lduMatrix.H:226
void Amul(scalarField &, const tmp< scalarField > &, const FieldField< Field, scalar > &, const lduInterfaceFieldPtrsList &, const direction cmpt) const
Matrix multiplication with updated interfaces.
Foam::fvMesh mesh(Foam::IOobject(regionName, runTime.name(), runTime, Foam::IOobject::MUST_READ), false)
const volScalarField & psi
static tmp< SurfaceField< Type > > interpolate(const VolField< Type > &tvf, const surfaceScalarField &faceFlux, Istream &schemeData)
Interpolate field onto faces using scheme given by Istream.
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
scalar gSumMag(const FieldField< Field, Type > &f)
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:258
messageStream Info
SolverPerformance< scalar > solverPerformance
SolverPerformance instantiated for a scalar.
Field< scalar > scalarField
Specialisation of Field<T> for scalar.
layerAndWeight min(const layerAndWeight &a, const layerAndWeight &b)
layerAndWeight max(const layerAndWeight &a, const layerAndWeight &b)
prefixOSstream Pout(cout, "Pout")
Definition: IOstreams.H:53
uint8_t direction
Definition: direction.H:45
dictionary dict