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-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 "GAMGSolver.H"
27 #include "SubField.H"
28 
29 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
30 
32 (
34  const scalarField& source,
35  const direction cmpt
36 ) const
37 {
38  // Setup class containing solver performance data
40 
41  // Calculate A.psi used to calculate the initial residual
42  scalarField Apsi(psi.size());
44 
45  // Create the storage for the finestCorrection which may be used as a
46  // temporary in normFactor
47  scalarField finestCorrection(psi.size());
48 
49  // Calculate normalisation factor
50  scalar normFactor = this->normFactor(psi, source, Apsi, finestCorrection);
51 
52  if (debug >= 2)
53  {
54  Pout<< " Normalisation factor = " << normFactor << endl;
55  }
56 
57  // Calculate initial finest-grid residual field
58  scalarField finestResidual(source - Apsi);
59 
60  // Calculate normalised residual for convergence test
61  solverPerf.initialResidual() = gSumMag
62  (
63  finestResidual,
64  matrix().mesh().comm()
65  )/normFactor;
66  solverPerf.finalResidual() = solverPerf.initialResidual();
67 
68 
69  // Check convergence, solve if not converged
70  if
71  (
72  minIter_ > 0
73  || !solverPerf.checkConvergence(tolerance_, relTol_)
74  )
75  {
76  // Create coarse grid correction fields
77  PtrList<scalarField> coarseCorrFields;
78 
79  // Create coarse grid sources
80  PtrList<scalarField> coarseSources;
81 
82  // Create the smoothers for all levels
84 
85  // Scratch fields if processor-agglomerated coarse level meshes
86  // are bigger than original. Usually not needed
87  scalarField scratch1;
88  scalarField scratch2;
89 
90  // Initialise the above data structures
91  initVcycle
92  (
93  coarseCorrFields,
94  coarseSources,
95  smoothers,
96  scratch1,
97  scratch2
98  );
99 
100  do
101  {
102  Vcycle
103  (
104  smoothers,
105  psi,
106  source,
107  Apsi,
108  finestCorrection,
109  finestResidual,
110 
111  (scratch1.size() ? scratch1 : Apsi),
112  (scratch2.size() ? scratch2 : finestCorrection),
113 
114  coarseCorrFields,
115  coarseSources,
116  cmpt
117  );
118 
119  // Calculate finest level residual field
121  finestResidual = source;
122  finestResidual -= Apsi;
123 
124  solverPerf.finalResidual() = gSumMag
125  (
126  finestResidual,
127  matrix().mesh().comm()
128  )/normFactor;
129 
130  if (debug >= 2)
131  {
132  solverPerf.print(Info(matrix().mesh().comm()));
133  }
134  } while
135  (
136  (
137  ++solverPerf.nIterations() < maxIter_
138  && !solverPerf.checkConvergence(tolerance_, relTol_)
139  )
140  || solverPerf.nIterations() < minIter_
141  );
142  }
143 
144  return solverPerf;
145 }
146 
147 
148 void Foam::GAMGSolver::Vcycle
149 (
150  const PtrList<lduMatrix::smoother>& smoothers,
151  scalarField& psi,
152  const scalarField& source,
153  scalarField& Apsi,
154  scalarField& finestCorrection,
155  scalarField& finestResidual,
156 
157  scalarField& scratch1,
158  scalarField& scratch2,
159 
160  PtrList<scalarField>& coarseCorrFields,
161  PtrList<scalarField>& coarseSources,
162  const direction cmpt
163 ) const
164 {
165  // debug = 2;
166 
167  const label coarsestLevel = matrixLevels_.size() - 1;
168 
169  // Restrict finest grid residual for the next level up.
170  agglomeration_.restrictField(coarseSources[0], finestResidual, 0, true);
171 
172  if (debug >= 2 && nPreSweeps_)
173  {
174  Pout<< "Pre-smoothing scaling factors: ";
175  }
176 
177 
178  // Residual restriction (going to coarser levels)
179  for (label leveli = 0; leveli < coarsestLevel; leveli++)
180  {
181  if (coarseSources.set(leveli + 1))
182  {
183  // If the optional pre-smoothing sweeps are selected
184  // smooth the coarse-grid field for the restricted source
185  if (nPreSweeps_)
186  {
187  coarseCorrFields[leveli] = 0.0;
188 
189  smoothers[leveli + 1].smooth
190  (
191  coarseCorrFields[leveli],
192  coarseSources[leveli],
193  cmpt,
194  min
195  (
196  nPreSweeps_ + preSweepsLevelMultiplier_*leveli,
197  maxPreSweeps_
198  )
199  );
200 
202  (
203  scratch1,
204  coarseCorrFields[leveli].size()
205  );
206 
207  // Scale coarse-grid correction field
208  // but not on the coarsest level because it evaluates to 1
209  if (scaleCorrection_ && leveli < coarsestLevel - 1)
210  {
211  scale
212  (
213  coarseCorrFields[leveli],
214  const_cast<scalarField&>
215  (
216  ACf.operator const scalarField&()
217  ),
218  matrixLevels_[leveli],
219  interfaceLevelsBouCoeffs_[leveli],
220  interfaceLevels_[leveli],
221  coarseSources[leveli],
222  cmpt
223  );
224  }
225 
226  // Correct the residual with the new solution
227  matrixLevels_[leveli].Amul
228  (
229  const_cast<scalarField&>
230  (
231  ACf.operator const scalarField&()
232  ),
233  coarseCorrFields[leveli],
234  interfaceLevelsBouCoeffs_[leveli],
235  interfaceLevels_[leveli],
236  cmpt
237  );
238 
239  coarseSources[leveli] -= ACf;
240  }
241 
242  // Residual is equal to source
243  agglomeration_.restrictField
244  (
245  coarseSources[leveli + 1],
246  coarseSources[leveli],
247  leveli + 1,
248  true
249  );
250  }
251  }
252 
253  if (debug >= 2 && nPreSweeps_)
254  {
255  Pout<< endl;
256  }
257 
258 
259  // Solve Coarsest level with either an iterative or direct solver
260  if (coarseCorrFields.set(coarsestLevel))
261  {
262  solveCoarsestLevel
263  (
264  coarseCorrFields[coarsestLevel],
265  coarseSources[coarsestLevel]
266  );
267  }
268 
269  if (debug >= 2)
270  {
271  Pout<< "Post-smoothing scaling factors: ";
272  }
273 
274  // Smoothing and prolongation of the coarse correction fields
275  // (going to finer levels)
276 
277  scalarField dummyField(0);
278 
279  for (label leveli = coarsestLevel - 1; leveli >= 0; leveli--)
280  {
281  if (coarseCorrFields.set(leveli))
282  {
283  // Create a field for the pre-smoothed correction field
284  // as a sub-field of the finestCorrection which is not
285  // currently being used
286  scalarField::subField preSmoothedCoarseCorrField
287  (
288  scratch2,
289  coarseCorrFields[leveli].size()
290  );
291 
292  // Only store the preSmoothedCoarseCorrField if pre-smoothing is
293  // used
294  if (nPreSweeps_)
295  {
296  preSmoothedCoarseCorrField = coarseCorrFields[leveli];
297  }
298 
299  agglomeration_.prolongField
300  (
301  coarseCorrFields[leveli],
302  (
303  coarseCorrFields.set(leveli + 1)
304  ? coarseCorrFields[leveli + 1]
305  : dummyField // dummy value
306  ),
307  leveli + 1,
308  true
309  );
310 
311 
312  // Create A.psi for this coarse level as a sub-field of Apsi
314  (
315  scratch1,
316  coarseCorrFields[leveli].size()
317  );
318  scalarField& ACfRef =
319  const_cast<scalarField&>(ACf.operator const scalarField&());
320 
321  if (interpolateCorrection_) //&& leveli < coarsestLevel - 2)
322  {
323  if (coarseCorrFields.set(leveli+1))
324  {
326  (
327  coarseCorrFields[leveli],
328  ACfRef,
329  matrixLevels_[leveli],
330  interfaceLevelsBouCoeffs_[leveli],
331  interfaceLevels_[leveli],
332  agglomeration_.restrictAddressing(leveli + 1),
333  coarseCorrFields[leveli + 1],
334  cmpt
335  );
336  }
337  else
338  {
340  (
341  coarseCorrFields[leveli],
342  ACfRef,
343  matrixLevels_[leveli],
344  interfaceLevelsBouCoeffs_[leveli],
345  interfaceLevels_[leveli],
346  cmpt
347  );
348  }
349  }
350 
351  // Scale coarse-grid correction field
352  // but not on the coarsest level because it evaluates to 1
353  if
354  (
355  scaleCorrection_
356  && (interpolateCorrection_ || leveli < coarsestLevel - 1)
357  )
358  {
359  scale
360  (
361  coarseCorrFields[leveli],
362  ACfRef,
363  matrixLevels_[leveli],
364  interfaceLevelsBouCoeffs_[leveli],
365  interfaceLevels_[leveli],
366  coarseSources[leveli],
367  cmpt
368  );
369  }
370 
371  // Only add the preSmoothedCoarseCorrField if pre-smoothing is
372  // used
373  if (nPreSweeps_)
374  {
375  coarseCorrFields[leveli] += preSmoothedCoarseCorrField;
376  }
377 
378  smoothers[leveli + 1].smooth
379  (
380  coarseCorrFields[leveli],
381  coarseSources[leveli],
382  cmpt,
383  min
384  (
385  nPostSweeps_ + postSweepsLevelMultiplier_*leveli,
386  maxPostSweeps_
387  )
388  );
389  }
390  }
391 
392  // Prolong the finest level correction
393  agglomeration_.prolongField
394  (
395  finestCorrection,
396  coarseCorrFields[0],
397  0,
398  true
399  );
400 
401  if (interpolateCorrection_)
402  {
404  (
405  finestCorrection,
406  Apsi,
407  matrix_,
408  interfaceBouCoeffs_,
409  interfaces_,
410  agglomeration_.restrictAddressing(0),
411  coarseCorrFields[0],
412  cmpt
413  );
414  }
415 
416  if (scaleCorrection_)
417  {
418  // Scale the finest level correction
419  scale
420  (
421  finestCorrection,
422  Apsi,
423  matrix_,
424  interfaceBouCoeffs_,
425  interfaces_,
426  finestResidual,
427  cmpt
428  );
429  }
430 
431  forAll(psi, i)
432  {
433  psi[i] += finestCorrection[i];
434  }
435 
436  smoothers[0].smooth
437  (
438  psi,
439  source,
440  cmpt,
441  nFinestSweeps_
442  );
443 }
444 
445 
446 void Foam::GAMGSolver::initVcycle
447 (
448  PtrList<scalarField>& coarseCorrFields,
449  PtrList<scalarField>& coarseSources,
450  PtrList<lduMatrix::smoother>& smoothers,
451  scalarField& scratch1,
452  scalarField& scratch2
453 ) const
454 {
455  label maxSize = matrix_.diag().size();
456 
457  coarseCorrFields.setSize(matrixLevels_.size());
458  coarseSources.setSize(matrixLevels_.size());
459  smoothers.setSize(matrixLevels_.size() + 1);
460 
461  // Create the smoother for the finest level
462  smoothers.set
463  (
464  0,
466  (
467  fieldName_,
468  matrix_,
469  interfaceBouCoeffs_,
470  interfaceIntCoeffs_,
471  interfaces_,
472  controlDict_
473  )
474  );
475 
476  forAll(matrixLevels_, leveli)
477  {
478  if (agglomeration_.nCells(leveli) >= 0)
479  {
480  label nCoarseCells = agglomeration_.nCells(leveli);
481 
482  coarseSources.set(leveli, new scalarField(nCoarseCells));
483  }
484 
485  if (matrixLevels_.set(leveli))
486  {
487  const lduMatrix& mat = matrixLevels_[leveli];
488 
489  label nCoarseCells = mat.diag().size();
490 
491  maxSize = max(maxSize, nCoarseCells);
492 
493  coarseCorrFields.set(leveli, new scalarField(nCoarseCells));
494 
495  smoothers.set
496  (
497  leveli + 1,
499  (
500  fieldName_,
501  matrixLevels_[leveli],
502  interfaceLevelsBouCoeffs_[leveli],
503  interfaceLevelsIntCoeffs_[leveli],
504  interfaceLevels_[leveli],
505  controlDict_
506  )
507  );
508  }
509  }
510 
511  if (maxSize > matrix_.diag().size())
512  {
513  // Allocate some scratch storage
514  scratch1.setSize(maxSize);
515  scratch2.setSize(maxSize);
516  }
517 }
518 
519 
520 void Foam::GAMGSolver::solveCoarsestLevel
521 (
522  scalarField& coarsestCorrField,
523  const scalarField& coarsestSource
524 ) const
525 {
526  const label coarsestLevel = matrixLevels_.size() - 1;
527 
528  label coarseComm = matrixLevels_[coarsestLevel].mesh().comm();
529 
530  if (directSolveCoarsest_)
531  {
532  coarsestLUMatrixPtr_->solve
533  (
534  coarsestCorrField,
535  coarsestSource
536  );
537  }
538  else
539  {
540  coarsestCorrField = 0;
541  solverPerformance coarseSolverPerf = coarsestSolverPtr_->solve
542  (
543  coarsestCorrField,
544  coarsestSource
545  );
546 
547  if (debug >= 2)
548  {
549  coarseSolverPerf.print(Info(coarseComm));
550  }
551  }
552 }
553 
554 
555 // ************************************************************************* //
#define forAll(list, i)
Loop across all elements in list.
Definition: UList.H:449
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.
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.
Template function which returns the un-mangled name of a given type. Useful for types which do not ha...
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
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:288
messageStream Info
SolverPerformance< scalar > solverPerformance
SolverPerformance instantiated for a scalar.
Field< scalar > scalarField
Specialisation of Field<T> for scalar.
scalar gSumMag(const UList< Type > &f, const label comm)
dimensioned< Type > min(const DimensionedField< Type, GeoMesh, PrimitiveField > &df)
prefixOSstream Pout(cout, "Pout")
Definition: IOstreams.H:53
uint8_t direction
Definition: direction.H:45
dimensioned< Type > max(const DimensionedField< Type, GeoMesh, PrimitiveField > &df)