/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | www.openfoam.com
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2011-2017 OpenFOAM Foundation
Copyright (C) 2016-2021,2023 OpenCFD Ltd.
Copyright (C) 2023 Huawei (Yu Ankun)
Copyright (C) 2023 OpenCFD Ltd.
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
OpenFOAM is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
for more details.
You should have received a copy of the GNU General Public License
along with OpenFOAM. If not, see .
\*---------------------------------------------------------------------------*/
#include "GAMGSolver.H"
#include "SubField.H"
#include "PrecisionAdaptor.H"
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
Foam::solverPerformance Foam::GAMGSolver::solve
(
scalarField& psi_s,
const scalarField& source,
const direction cmpt
) const
{
PrecisionAdaptor tpsi(psi_s);
solveScalarField& psi = tpsi.ref();
ConstPrecisionAdaptor tsource(source);
// Setup class containing solver performance data
solverPerformance solverPerf(typeName, fieldName_);
// Calculate A.psi used to calculate the initial residual
solveScalarField Apsi(psi.size());
matrix_.Amul(Apsi, psi, interfaceBouCoeffs_, interfaces_, cmpt);
// Create the storage for the finestCorrection which may be used as a
// temporary in normFactor
solveScalarField finestCorrection(psi.size());
// Calculate normalisation factor
solveScalar normFactor =
this->normFactor(psi, tsource(), Apsi, finestCorrection);
if ((log_ >= 2) || (debug >= 2))
{
Pout<< " Normalisation factor = " << normFactor << endl;
}
// Calculate initial finest-grid residual field
solveScalarField finestResidual(tsource() - Apsi);
matrix().setResidualField
(
ConstPrecisionAdaptor(finestResidual)(),
fieldName_,
true
);
// Calculate normalised residual for convergence test
solverPerf.initialResidual() = gSumMag
(
finestResidual,
matrix().mesh().comm()
)/normFactor;
solverPerf.finalResidual() = solverPerf.initialResidual();
// Check convergence, solve if not converged
if
(
minIter_ > 0
|| !solverPerf.checkConvergence(tolerance_, relTol_, log_)
)
{
// Create coarse grid correction fields
PtrList coarseCorrFields;
// Create coarse grid sources
PtrList coarseSources;
// Create the smoothers for all levels
PtrList smoothers;
// Scratch fields if processor-agglomerated coarse level meshes
// are bigger than original. Usually not needed
solveScalarField scratch1;
solveScalarField scratch2;
// Initialise the above data structures
initVcycle
(
coarseCorrFields,
coarseSources,
smoothers,
scratch1,
scratch2
);
do
{
Vcycle
(
smoothers,
psi,
source,
Apsi,
finestCorrection,
finestResidual,
(scratch1.size() ? scratch1 : Apsi),
(scratch2.size() ? scratch2 : finestCorrection),
coarseCorrFields,
coarseSources,
cmpt
);
// Calculate finest level residual field
matrix_.Amul(Apsi, psi, interfaceBouCoeffs_, interfaces_, cmpt);
finestResidual = tsource();
finestResidual -= Apsi;
solverPerf.finalResidual() = gSumMag
(
finestResidual,
matrix().mesh().comm()
)/normFactor;
if ((log_ >= 2) || (debug >= 2))
{
solverPerf.print(Info.masterStream(matrix().mesh().comm()));
}
} while
(
(
++solverPerf.nIterations() < maxIter_
&& !solverPerf.checkConvergence(tolerance_, relTol_, log_)
)
|| solverPerf.nIterations() < minIter_
);
}
matrix().setResidualField
(
ConstPrecisionAdaptor(finestResidual)(),
fieldName_,
false
);
return solverPerf;
}
void Foam::GAMGSolver::Vcycle
(
const PtrList& smoothers,
solveScalarField& psi,
const scalarField& source,
solveScalarField& Apsi,
solveScalarField& finestCorrection,
solveScalarField& finestResidual,
solveScalarField& scratch1,
solveScalarField& scratch2,
PtrList& coarseCorrFields,
PtrList& coarseSources,
const direction cmpt
) const
{
//debug = 2;
const label coarsestLevel = matrixLevels_.size() - 1;
// Restrict finest grid residual for the next level up.
agglomeration_.restrictField(coarseSources[0], finestResidual, 0, true);
if (nPreSweeps_ && ((log_ >= 2) || (debug >= 2)))
{
Pout<< "Pre-smoothing scaling factors: ";
}
// Residual restriction (going to coarser levels)
for (label leveli = 0; leveli < coarsestLevel; leveli++)
{
if (coarseSources.set(leveli + 1))
{
// If the optional pre-smoothing sweeps are selected
// smooth the coarse-grid field for the restricted source
if (nPreSweeps_)
{
coarseCorrFields[leveli] = 0.0;
smoothers[leveli + 1].scalarSmooth
(
coarseCorrFields[leveli],
coarseSources[leveli], //coarseSource,
cmpt,
min
(
nPreSweeps_ + preSweepsLevelMultiplier_*leveli,
maxPreSweeps_
)
);
// Scale coarse-grid correction field
// but not on the coarsest level because it evaluates to 1
if (scaleCorrection_ && leveli < coarsestLevel - 1)
{
solveScalarField::subField ACf
(
scratch1,
coarseCorrFields[leveli].size()
);
scale
(
coarseCorrFields[leveli],
const_cast
(
static_cast(ACf)
),
matrixLevels_[leveli],
interfaceLevelsBouCoeffs_[leveli],
interfaceLevels_[leveli],
coarseSources[leveli],
cmpt
);
}
// Correct the residual with the new solution
// residual can be used by fusing Amul with b-Amul
matrixLevels_[leveli].residual
(
coarseSources[leveli],
coarseCorrFields[leveli],
ConstPrecisionAdaptor
(
coarseSources[leveli]
)(),
interfaceLevelsBouCoeffs_[leveli],
interfaceLevels_[leveli],
cmpt
);
}
// Residual is equal to source
agglomeration_.restrictField
(
coarseSources[leveli + 1],
coarseSources[leveli],
leveli + 1,
true
);
}
}
if (nPreSweeps_ && ((log_ >= 2) || (debug >= 2)))
{
Pout<< endl;
}
// Solve Coarsest level with either an iterative or direct solver
if (coarseCorrFields.set(coarsestLevel))
{
solveCoarsestLevel
(
coarseCorrFields[coarsestLevel],
coarseSources[coarsestLevel]
);
}
if ((log_ >= 2) || (debug >= 2))
{
Pout<< "Post-smoothing scaling factors: ";
}
// Smoothing and prolongation of the coarse correction fields
// (going to finer levels)
solveScalarField dummyField(0);
// Work storage for prolongation
solveScalarField work;
for (label leveli = coarsestLevel - 1; leveli >= 0; leveli--)
{
if (coarseCorrFields.set(leveli))
{
// Create a field for the pre-smoothed correction field
// as a sub-field of the finestCorrection which is not
// currently being used
solveScalarField::subField preSmoothedCoarseCorrField
(
scratch2,
coarseCorrFields[leveli].size()
);
// Only store the preSmoothedCoarseCorrField if pre-smoothing is
// used
if (nPreSweeps_)
{
preSmoothedCoarseCorrField = coarseCorrFields[leveli];
}
// Prolong correction to leveli
const auto& cf = agglomeration_.prolongField
(
coarseCorrFields[leveli], // current level
work,
(
coarseCorrFields.set(leveli + 1)
? coarseCorrFields[leveli + 1]
: dummyField // dummy value
),
leveli + 1
);
// Create A.psi for this coarse level as a sub-field of Apsi
solveScalarField::subField ACf
(
scratch1,
coarseCorrFields[leveli].size()
);
auto& ACfRef = const_cast
(
static_cast(ACf)
);
if (interpolateCorrection_)
{
// Normal operation : have both coarse level and fine
// level. No processor agglomeration
interpolate
(
coarseCorrFields[leveli],
ACfRef,
matrixLevels_[leveli],
interfaceLevelsBouCoeffs_[leveli],
interfaceLevels_[leveli],
agglomeration_.restrictAddressing(leveli + 1),
cf,
cmpt
);
}
// Scale coarse-grid correction field
// but not on the coarsest level because it evaluates to 1
if
(
scaleCorrection_
&& (interpolateCorrection_ || leveli < coarsestLevel - 1)
)
{
scale
(
coarseCorrFields[leveli],
ACfRef,
matrixLevels_[leveli],
interfaceLevelsBouCoeffs_[leveli],
interfaceLevels_[leveli],
coarseSources[leveli],
cmpt
);
}
// Only add the preSmoothedCoarseCorrField if pre-smoothing is
// used
if (nPreSweeps_)
{
coarseCorrFields[leveli] += preSmoothedCoarseCorrField;
}
smoothers[leveli + 1].scalarSmooth
(
coarseCorrFields[leveli],
coarseSources[leveli], //coarseSource,
cmpt,
min
(
nPostSweeps_ + postSweepsLevelMultiplier_*leveli,
maxPostSweeps_
)
);
}
}
// Prolong the finest level correction
agglomeration_.prolongField
(
finestCorrection,
coarseCorrFields[0],
0,
true
);
if (interpolateCorrection_)
{
interpolate
(
finestCorrection,
Apsi,
matrix_,
interfaceBouCoeffs_,
interfaces_,
agglomeration_.restrictAddressing(0),
coarseCorrFields[0],
cmpt
);
}
if (scaleCorrection_)
{
// Scale the finest level correction
scale
(
finestCorrection,
Apsi,
matrix_,
interfaceBouCoeffs_,
interfaces_,
finestResidual,
cmpt
);
}
forAll(psi, i)
{
psi[i] += finestCorrection[i];
}
smoothers[0].smooth
(
psi,
source,
cmpt,
nFinestSweeps_
);
}
void Foam::GAMGSolver::initVcycle
(
PtrList& coarseCorrFields,
PtrList& coarseSources,
PtrList& smoothers,
solveScalarField& scratch1,
solveScalarField& scratch2
) const
{
label maxSize = matrix_.diag().size();
coarseCorrFields.setSize(matrixLevels_.size());
coarseSources.setSize(matrixLevels_.size());
smoothers.setSize(matrixLevels_.size() + 1);
// Create the smoother for the finest level
smoothers.set
(
0,
lduMatrix::smoother::New
(
fieldName_,
matrix_,
interfaceBouCoeffs_,
interfaceIntCoeffs_,
interfaces_,
controlDict_
)
);
forAll(matrixLevels_, leveli)
{
if (agglomeration_.nCells(leveli) >= 0)
{
label nCoarseCells = agglomeration_.nCells(leveli);
coarseSources.set(leveli, new solveScalarField(nCoarseCells));
}
if (matrixLevels_.set(leveli))
{
const lduMatrix& mat = matrixLevels_[leveli];
label nCoarseCells = mat.diag().size();
maxSize = max(maxSize, nCoarseCells);
coarseCorrFields.set(leveli, new solveScalarField(nCoarseCells));
smoothers.set
(
leveli + 1,
lduMatrix::smoother::New
(
fieldName_,
matrixLevels_[leveli],
interfaceLevelsBouCoeffs_[leveli],
interfaceLevelsIntCoeffs_[leveli],
interfaceLevels_[leveli],
controlDict_
)
);
}
}
if (maxSize > matrix_.diag().size())
{
// Allocate some scratch storage
scratch1.setSize(maxSize);
scratch2.setSize(maxSize);
}
}
Foam::dictionary Foam::GAMGSolver::PCGsolverDict
(
const scalar tol,
const scalar relTol
) const
{
dictionary dict(IStringStream("solver PCG; preconditioner DIC;")());
dict.add("tolerance", tol);
dict.add("relTol", relTol);
return dict;
}
Foam::dictionary Foam::GAMGSolver::PBiCGStabSolverDict
(
const scalar tol,
const scalar relTol
) const
{
dictionary dict(IStringStream("solver PBiCGStab; preconditioner DILU;")());
dict.add("tolerance", tol);
dict.add("relTol", relTol);
return dict;
}
void Foam::GAMGSolver::solveCoarsestLevel
(
solveScalarField& coarsestCorrField,
const solveScalarField& coarsestSource
) const
{
const label coarsestLevel = matrixLevels_.size() - 1;
const label coarseComm = matrixLevels_[coarsestLevel].mesh().comm();
if (directSolveCoarsest_)
{
PrecisionAdaptor tcorrField(coarsestCorrField);
coarsestLUMatrixPtr_->solve
(
tcorrField.ref(),
ConstPrecisionAdaptor(coarsestSource)()
);
}
//else if
//(
// agglomeration_.processorAgglomerate()
// && procMatrixLevels_.set(coarsestLevel)
//)
//{
// //const labelList& agglomProcIDs = agglomeration_.agglomProcIDs
// //(
// // coarsestLevel
// //);
// //
// //scalarField allSource;
// //
// //globalIndex cellOffsets;
// //if (Pstream::myProcNo(coarseComm) == agglomProcIDs[0])
// //{
// // cellOffsets.offsets() =
// // agglomeration_.cellOffsets(coarsestLevel);
// //}
// //
// //cellOffsets.gather
// //(
// // coarseComm,
// // agglomProcIDs,
// // coarsestSource,
// // allSource
// //);
// //
// //scalarField allCorrField;
// //solverPerformance coarseSolverPerf;
//
// label solveComm = agglomeration_.procCommunicator(coarsestLevel);
//
// coarsestCorrField = 0;
// solverPerformance coarseSolverPerf;
//
// if (Pstream::myProcNo(solveComm) != -1)
// {
// const lduMatrix& allMatrix = procMatrixLevels_[coarsestLevel];
//
// {
// Pout<< "** Master:Solving on comm:" << solveComm
// << " with procs:" << UPstream::procID(solveComm) << endl;
//
// if (allMatrix.asymmetric())
// {
// coarseSolverPerf = PBiCGStab
// (
// "coarsestLevelCorr",
// allMatrix,
// procInterfaceLevelsBouCoeffs_[coarsestLevel],
// procInterfaceLevelsIntCoeffs_[coarsestLevel],
// procInterfaceLevels_[coarsestLevel],
// PBiCGStabSolverDict(tolerance_, relTol_)
// ).solve
// (
// coarsestCorrField,
// coarsestSource
// );
// }
// else
// {
// coarseSolverPerf = PCG
// (
// "coarsestLevelCorr",
// allMatrix,
// procInterfaceLevelsBouCoeffs_[coarsestLevel],
// procInterfaceLevelsIntCoeffs_[coarsestLevel],
// procInterfaceLevels_[coarsestLevel],
// PCGsolverDict(tolerance_, relTol_)
// ).solve
// (
// coarsestCorrField,
// coarsestSource
// );
// }
// }
// }
//
// Pout<< "done master solve." << endl;
//
// //// Scatter to all processors
// //coarsestCorrField.setSize(coarsestSource.size());
// //cellOffsets.scatter
// //(
// // coarseComm,
// // agglomProcIDs,
// // allCorrField,
// // coarsestCorrField
// //);
//
// if (debug >= 2)
// {
// coarseSolverPerf.print(Info.masterStream(coarseComm));
// }
//
// Pout<< "procAgglom: coarsestSource :" << coarsestSource << endl;
// Pout<< "procAgglom: coarsestCorrField:" << coarsestCorrField << endl;
//}
else
{
coarsestCorrField = 0;
const solverPerformance coarseSolverPerf
(
coarsestSolverPtr_->scalarSolve
(
coarsestCorrField,
coarsestSource
)
);
if ((log_ >= 2) || debug)
{
coarseSolverPerf.print(Info.masterStream(coarseComm));
}
}
}
// ************************************************************************* //