amgcl: Stokes problem gives NaN by AMG but GMRES works fine

I’m trying to use AMGCL to solve a simple Stokes problem using finite element method. The linear system is similar to Naiver-Stokes one:

[K G * {u = {b_u D S] p} b_p}

2nd Order FEM System size: 4080 x 4080 u is the unknows for velocity from 1 - 3600 p is the unkonws for pressure from 3601-4080

Tets=48 (I tried larger mesh which still gives me the similar error)

G and D are non-square. S has zero-diagonal. This is a typical saddle-point problem matrix for Stokes. image

Download the linear system here Ab_mm.zip

Based on the benchmark problem, I trying to solve the system using following solver

  1. GMRES + ILU0, GOOD
  2. AMG + ILU0 and, NAN
  3. Schur Pressure AMG. Crash

But only 1) GMRES + ILU0 wokrs, any AMG solver return nan or Crash.

Can you check why AMGCL is not working here?

Thanks, Bin

output info:

Solver
======
Type:             FGMRES(30)
Unknowns:         4080
Memory footprint: 1.94 M

Preconditioner
==============
Number of levels:    2
Operator complexity: 1.01
Grid complexity:     1.02
Memory footprint:    6.88 M

level     unknowns       nonzeros      memory
---------------------------------------------
    0         4080         201072      6.85 M (98.77%)
    1           71           2503     37.69 K ( 1.23%)

#Iters=1000 Error=nan

My solver set up are:

typedef amgcl::backend::builtin<double> Backend;

//Simple iterative solver GMRES + ILU0
typedef amgcl::make_solver<
	relaxation::as_preconditioner<Backend, relaxation::ilu0>,
	solver::fgmres<Backend>
> Solver1;

//AMG + ILU0
typedef make_solver<
	amg<Backend, coarsening::smoothed_aggregation, relaxation::ilu0>,
	solver::fgmres<Backend>
> Solver2;

//Parameter setup
Solver::params prm;
double tol = 1e-6;
prm.solver.tol = tol;
prm.solver.maxiter = 1000;

//Solve the system
Solver solve(A,prm);
std::cout << solve << std::endl;

int iters;
double error;
std::tie(iters, error) = solve(b, X);
printf("#Iters=%d Error=%lf\n", iters, error);

Specially my schur_pressure_correction are:

typedef make_solver<
	preconditioner::schur_pressure_correction<
	make_solver<
	relaxation::as_preconditioner<Backend, relaxation::damped_jacobi>,
	solver::bicgstab<Backend>
	>,
	make_solver<
	amg<
	Backend,
	coarsening::smoothed_aggregation,
	relaxation::ilu0
	>,
	solver::fgmres<Backend>
	>
	>,
	solver::fgmres<Backend>
> Solver;

//Parameter setup
Solver::params prm;
double tol = 1.0e-6;
prm.solver.tol = tol;
prm.precond.usolver.solver.tol = tol * 10;
prm.precond.psolver.solver.tol = tol * 10;
prm.precond.psolver.solver.maxiter = 1000;
prm.precond.usolver.solver.maxiter = 1000;

for(size_t i=0;i<rows;++i)
	if(i>3600) prm.precond.pmask[i] = 1;

//Solve the system
Solver solve(A,prm);
std::cout << solve << std::endl;

int iters;
double error;
std::tie(iters, error) = solve(b, X);
printf("#Iters=%d Error=%lf\n", iters, error);

About this issue

  • Original URL
  • State: closed
  • Created 4 years ago
  • Comments: 61 (40 by maintainers)

Most upvoted comments

Sorry about the confusion. The system is a “bad” system where the boundary condtions are not propertely setup. The V5 works fine now after fixing the boundary condition. I will post my latest benchmark soon.

I see. Well, I am out of ideas then. The approach that works (SchurPC with dummy preconditioner for the pressure part) is not strictly multigrid, so I guess the increase in the number of iterations is not unexpected. You can try to increase the number of iterations for the pressure solver (since the unpreconditioned solver is not a very effective one). It somewhat helps with the matrix you provided:

./schur_pressure_correction \
    -A problems/issue-144/condensed/A.mm \
    -f problems/issue-144/condensed/b.mm \
    -m '>15552' -p \
    solver.type=fgmres \
    precond.usolver.solver.maxiter=1 \
    precond.psolver.precond.class=dummy \
    precond.usolver.precond.{class=relaxation,type=ilu0} \
    precond.psolver.solver.maxiter=5
Schur complement (two-stage preconditioner)
  unknowns: 19392(3840)
  nonzeros: 1102992

Iterations: 39
Error:      7.89528e-09

[Profile:                2.831 s] (100.00%)
[  reading:              2.174 s] ( 76.78%)
[  schur_complement:     0.657 s] ( 23.21%)
[    setup:              0.057 s] (  2.03%)
[    solve:              0.599 s] ( 21.15%)

Another thing to try is to disable the use of approximate schur complement:

./schur_pressure_correction \
    -A problems/issue-144/condensed/A.mm \
    -f problems/issue-144/condensed/b.mm \
    -m '>15552' -p \
    solver.type=fgmres \
    precond.usolver.solver.maxiter=1 \
    precond.psolver.precond.class=dummy \
    precond.usolver.precond.{class=relaxation,type=ilu0} \
    precond.psolver.solver.maxiter=5 \
    precond.approx_schur=0
Schur complement (two-stage preconditioner)
  unknowns: 19392(3840)
  nonzeros: 1102992

Iterations: 20
Error:      9.33415e-09

[Profile:                3.460 s] (100.00%)
[  reading:              2.162 s] ( 62.48%)
[  schur_complement:     1.298 s] ( 37.52%)
[    setup:              0.057 s] (  1.64%)
[    solve:              1.240 s] ( 35.85%)

Finally, since the pressure part in the condensed matrix does have a nonzero diagonal, you can replace dummy preconditioner with some simple single level one like spai0 or damped_jacobi:

./schur_pressure_correction \
    -A problems/issue-144/condensed/A.mm \
    -f problems/issue-144/condensed/b.mm \
    -m '>15552' -p \
    solver.type=fgmres \
    precond.usolver.solver.maxiter=1 \
    precond.psolver.precond.{class=relaxation,type=damped_jacobi} \
    precond.usolver.precond.{class=relaxation,type=ilu0} \
    precond.psolver.solver.maxiter=5 \
    precond.approx_schur=0
Schur complement (two-stage preconditioner)
  unknowns: 19392(3840)
  nonzeros: 1102992

Iterations: 24
Error:      2.14533e-09

[Profile:                3.910 s] (100.00%)
[  reading:              2.377 s] ( 60.79%)
[  schur_complement:     1.533 s] ( 39.21%)
[    setup:              0.055 s] (  1.41%)
[    solve:              1.477 s] ( 37.78%)

Not sure how any of these will scale though.

Also, the USolver and PSolver template parameters for the schur_pressure_correction are in the wrong order. The following works for me (I’ve changed the inner iterative solvers to bicgstab as that is what was used in my example above):

#include <iostream>
#include <string>

#ifdef _OPENMP
#include <omp.h>
#endif

#include <amgcl/adapter/crs_tuple.hpp>
#include <amgcl/backend/builtin.hpp>
#include <amgcl/make_solver.hpp>
#include <amgcl/preconditioner/dummy.hpp>
#include <amgcl/preconditioner/schur_pressure_correction.hpp>
#include <amgcl/solver/fgmres.hpp>
#include <amgcl/solver/bicgstab.hpp>
#include <amgcl/coarsening/smoothed_aggregation.hpp>
#include <amgcl/coarsening/aggregation.hpp>
#include <amgcl/coarsening/ruge_stuben.hpp>
#include <amgcl/relaxation/as_preconditioner.hpp>
#include <amgcl/relaxation/damped_jacobi.hpp>
#include <amgcl/relaxation/ilu0.hpp>
#include <amgcl/relaxation/spai0.hpp>
#include <amgcl/amg.hpp>
#include <amgcl/io/binary.hpp>
#include <amgcl/io/mm.hpp>
#include <amgcl/profiler.hpp>

typedef amgcl::backend::builtin<double> Backend;


int main(int argc, char* argv[]) {
    using std::string;
    using std::vector;

    using namespace amgcl;
    namespace io = amgcl::io;

    size_t rows;
    vector<ptrdiff_t> ptr, col;
    vector<double> val, rhs;
    std::vector<char> pm;


    size_t cols;
    std::tie(rows, cols) = io::mm_reader("A.mm")(ptr, col, val);

    size_t n, m;
    std::tie(n, m) = io::mm_reader("b.mm")(rhs);

    typedef
        make_solver<
            preconditioner::schur_pressure_correction<
                make_solver<
                    amg<
                        Backend,
                        coarsening::smoothed_aggregation,
                        relaxation::ilu0
                        >,
                    solver::bicgstab<Backend>
                    >,
                make_solver<
                    preconditioner::dummy<Backend>,
                    solver::bicgstab<Backend>
                    >
                >,
            solver::fgmres<Backend>
        > Solver;

    //Parameter setup
    Solver::params prm;
    double tol = 1.0e-8;
    prm.solver.tol = tol;

    prm.precond.usolver.solver.tol = tol * 10;
    prm.precond.psolver.solver.tol = tol * 10;
    prm.precond.psolver.solver.maxiter = 1;
    prm.precond.usolver.solver.maxiter = 1;

    prm.precond.pmask.resize(n, 0);
    for (size_t i = 0; i < rows; ++i)
        if (i >= 3600) prm.precond.pmask[i] = 1;


    //Solve the system
    Solver solve(std::tie(rows, ptr, col, val), prm);
    std::cout << solve << std::endl;
    std::vector<double> x(n, 0.0);

    int iters;
    double error;
    std::tie(iters, error) = solve(rhs, x);
    printf("#Iters=%d Error=%lf\n", iters, error);
}

The result is:

Solver
======
Type:             FGMRES(30)
Unknowns:         4080
Memory footprint: 1.94 M

Preconditioner
==============
Schur complement (two-stage preconditioner)
  unknowns: 4080(480)
  nonzeros: 201072

#Iters=41 Error=0.000000