File naulin_laplace.hxx#
Iterative solver to handle non-constant-in-z coefficients
Scheme suggested by Volker Naulin: solve
using standard FFT-based solver, iterating to include other terms by evaluating them on rhs using \(\phi\) from previous iteration. DC part (i.e. Field2D part) of \(C_1 D\), \(C_2\) and \(A,D\) is kept in the FFT inversion to improve convergence by including as much as possible in the direct solve and so that all Neumann boundary conditions can be used at least when \(DC(A/D)!=0\)
Explanation of the procedure
A way to invert the equation \(\Omega^D = \nabla\cdot(n\nabla_\perp \phi)\) invented by Naulin, V. In an orthogonal system, we have that:
Rearranging gives
In fact we allow for the slightly more general form
The iteration can be under-relaxed to help it converge. Amount of under-relaxation is set by the parameter ‘underrelax_factor’. 0<underrelax_factor<=1, with underrelax_factor=1 corresponding to no under-relaxation. The amount of under-relaxation is temporarily increased if the iteration starts diverging, the starting value uof underrelax_factor can be set with the initial_underrelax_factor option.
The iteration now works as follows:
Get the vorticity from
where phiCur is phi of the current iteration [and DC(f) is the constant-in-z component of f]vort = (vortD/n) - grad_perp(ln_n)*grad_perp(phiCur) [Delp2(phiNext) + 1/DC(C2*D)*grad_perp(DC(C2))*grad_perp(phiNext) + DC(A/D)*phiNext = b(phiCur) = (rhs/D) - (1/C1/D*grad_perp(C2)*grad_perp(phiCur) - 1/DC(C2*D)*grad_perp(DC(C2))*grad_perp(phiCur)) - (A/D - DC(A/D))*phiCur]
Invert \(phi\) to find the voricity using
phiNext = invert_laplace_perp(vort)
[set Acoef of laplace_perp solver to DC(A/D)
and C1coef of laplace_perp solver to DC(C1*D)
and C2coef of laplace_perp solver to DC(C2)
then phiNext = invert_laplace_perp(underrelax_factor*b(phiCur) - (1-underrelax_factor)*b(phiPrev))]
where b(phiPrev) is the previous rhs value, which (up to rounding errors) is
the same as the lhs of the direct solver applied to phiCur.
Calculate the error at phi=phiNext
error3D = Delp2(phiNext) + 1/C1*grad_perp(C2)*grad_perp(phiNext) + A/D*phiNext - rhs/D = b(phiCur) - b(phiNext) as b(phiCur) = Delp2(phiNext) + 1/DC(C2*D)*grad_perp(DC(C2))*grad_perp(phiNext) + DC(A/D)*phiNext up to rounding errors
Calculate the infinity norms of the error
EAbsLInf = max(error3D) ERelLInf = EAbsLInf/sqrt( max((rhs/D)^2) )
Check whether
EAbsLInf > atol
If yes
Check whether
ERelLInf > rtol
If yes
Check whether
EAbsLInf > EAbsLInf(previous step)
If yes
Restart iterationunderrelax_factor *= 0.9
If no
- increase curCount and start from step 1
phiCur = phiNext
If number of iteration is above maxit, throw exception
If no
Stop: Function returns phiNext
if no
Stop: Function returns phiNext
-
class LaplaceNaulin : public Laplacian#
Solves the 2D Laplacian equation.
Public Functions
-
LaplaceNaulin(Options *opt = NULL, const CELL_LOC loc = CELL_CENTRE, Mesh *mesh_in = nullptr, Solver *solver = nullptr)#
-
~LaplaceNaulin() = default#
-
inline virtual void setCoefA(const Field2D &val) override#
Set coefficients for inversion. Re-builds matrices if necessary.
-
inline virtual bool uses3DCoefs() const override#
Does this solver use Field3D coefficients (true) or only their DC component (false)
-
inline virtual void setGlobalFlags(int f) override#
-
inline virtual void setInnerBoundaryFlags(int f) override#
-
inline virtual void setOuterBoundaryFlags(int f) override#
-
inline void resetMeanIterations()#
-
void setCoefA(const Field2D &val) = 0
Set coefficients for inversion. Re-builds matrices if necessary.
-
inline void setCoefA(const Field3D &val)
-
void setCoefC(const Field2D &val) = 0
-
inline void setCoefC(const Field3D &val)
-
inline void setCoefC1(const Field2D &val)
-
inline void setCoefC1(const Field3D &val)
-
inline void setCoefC2(const Field2D &val)
-
inline void setCoefC2(const Field3D &val)
-
void setCoefD(const Field2D &val) = 0
-
inline void setCoefD(const Field3D &val)
-
void setCoefEx(const Field2D &val) = 0
-
void setCoefEz(const Field2D &val) = 0
Private Functions
-
LaplaceNaulin(const LaplaceNaulin&)#
-
LaplaceNaulin &operator=(const LaplaceNaulin&)#
Private Members
-
std::unique_ptr<Laplacian> delp2solver = {nullptr}#
Laplacian solver used to solve the equation with constant-in-z coefficients.
-
int maxits#
Maximum number of iterations.
-
BoutReal initial_underrelax_factor = {1.}#
Initial choice for under-relaxation factor, should be greater than 0 and less than or equal to 1. Value of 1 means no underrelaxation
-
BoutReal underrelax_threshold = {1.5}#
Factor by which the error may increase since the previous iteration before triggering a sub-cycle with decreasing underrelax_factor to try and prevent divergence.
-
BoutReal underrelax_decrease_factor = {0.9}#
Factor by which to decrease underrelax_factor at each stage of the sub-loop triggered if underrelax_threshold is crossed.
-
int underrelax_decrease_maxits = {10}#
Maximum number of iterations in the decreasing-underrelax_factor subcycle before trying to continue the main iteration loop.
-
BoutReal underrelax_recovery = {1.1}#
If underrelax_factor has been decreased, increase it again by this factor at the end of a successful iteration to try and speed up subsequent convergence.
-
BoutReal naulinsolver_mean_underrelax_counts = {0.}#
Mean number of times the underrelaxation factor is reduced.
-
int ncalls#
Counter for the number of times the solver has been called.
-
LaplaceNaulin(Options *opt = NULL, const CELL_LOC loc = CELL_CENTRE, Mesh *mesh_in = nullptr, Solver *solver = nullptr)#