Dear PETSc team,

We are solving the Stokes equations using PETSc (via Firedrake) on a highly 
anisotropic 3D domain (L_x=1, L_y=0.01, L_z=0.1).

In this setup, standard Schur complement preconditioners using a mass inverse 
for pressure struggle to converge. We could solve the problem with the LSC 
preconditioner (solver parameters are shown in the script).

We have the following questions:

 *   Why standard preconditioners struggle in such domains?
 *   Why is the preconditioned residual norm for the Schur complement system 
much higher than the true residual norm?
 *   Would you recommend alternative or more robust preconditioners for such 
geometries?

Thank you for your help.

Best regards,
Hardik




HARDIK KOTHARI

hardik.koth...@corintis.com<mailto:hardik.koth...@corintis.com>

Corintis SA
EPFL Innovation Park Building C
1015 Lausanne



[https://urldefense.us/v3/__https://storcor.s3.eu-central-1.amazonaws.com/logos/Logo-black.png__;!!G_uCfscf7eWS!coZIkzX5gHWdyGx54LSoh7iP6KZ7AV-0Oa7FTFrMVAzIYrBCsVJ0vqY0hLvcIqcrdQtxcmcdpr25Hhsper7CRJwNrHrjfDsq$
 ]
Here at Corintis we care for your privacy. That is why we have taken 
appropriate measures to ensure that the data you have provided to us is always 
secure
import firedrake as fd
from firedrake import inner, grad, div, dx
from firedrake.petsc import PETSc


simulation_case = "elongated"
# simulation_case = "cube"

if simulation_case == "elongated":
    Lx = 1
    Ly = 0.01
    Lz = 0.1
    nx = 100
    ny = int(nx * Ly / Lx * 5)
    nz = int(nx * Lz / Lx * 5)
else:
    Lx = 1
    Ly = 1
    Lz = 1
    nx = 20
    ny = nx
    nz = nx
mesh = fd.BoxMesh(nx, ny, nz, Lx, Ly, Lz)

V = fd.VectorFunctionSpace(mesh, "CG", 2)
W = fd.FunctionSpace(mesh, "CG", 1)
Z = V * W
PETSc.Sys.Print(f"DOFs: {Z.dim()}")

u, p = fd.TrialFunctions(Z)
v, q = fd.TestFunctions(Z)


def a(u, v, p, q):
    return (inner(grad(u), grad(v)) - inner(p, div(v)) + inner(div(u), q)) * dx


L = inner(fd.Constant((0, 0, 0)), v) * dx

max_vel = 16
x, y, z = fd.SpatialCoordinate(mesh)
u_inlet = fd.as_vector(
    [
        max_vel * (1 - y / Ly) * y / Ly * z / Lz * (1 - z / Lz),
        0,
        0,
    ]
)


bcs = [
    fd.DirichletBC(Z.sub(0), u_inlet, (1,)),
    fd.DirichletBC(Z.sub(0), fd.Constant((0, 0, 0)), (3, 4, 5, 6)),
]

up = fd.Function(Z)
a_form = a(u, v, p, q)

direct_solver_settings = {
    "ksp_type": "preonly",
    "ksp_monitor_true_residual": None,
    "ksp_monitor": None,
    "pc_type": "lu",
    "pc_factor_mat_solver_type": "mumps",
    "mat_mumps_icntl_14": 200,
    "mat_mumps_icntl_24": 1,
}


class MassInvPC(fd.AssembledPC):

    _prefix = "Mp_"

    def form(self, pc, test, trial):
        _, bcs = super(MassInvPC, self).form(pc)

        appctx = self.get_appctx(pc)
        mu = appctx.get("mu", 1.0)
        a = inner((1 / mu) * trial, test) * dx
        return a, bcs

    def set_nullspaces(self, pc):
        # the mass matrix does not have a nullspace
        pass


assembled_hypre_settings = {
    "ksp_type": "cg",
    # "ksp_converged_reason": None,
    "ksp_max_it": 100,
    "ksp_atol": 1e-15,
    "ksp_rtol": 1e-15,
    "pc_type": "python",
    "pc_python_type": "firedrake.AssembledPC",
    "assembled_pc_type": "hypre",
    "assembled_pc_hypre_type": "boomeramg",
    "assembled_pc_hypre_boomeramg_coarsen_type": "PMIS",
    "assembled_pc_hypre_boomeramg_interp_type": "ext+i",
    "assembled_pc_hypre_boomeramg_strong_threshold": 0.1,
    "assembled_pc_hypre_boomeramg_agg_nl": 1,
    "assembled_pc_hypre_boomeramg_relax_type_all": "l1-Gauss-Seidel",
    "assembled_pc_hypre_boomeramg_smooth_type": "ILU",
    "assembled_pc_hypre_boomeramg_truncfactor": 0.3,
    "assembled_pc_hypre_boomeramg_max_levels": 25,
    "assembled_pc_hypre_boomeramg_max_iter": 1,
}

mass_inv_settings = {
    "ksp_type": "gmres",
    "ksp_converged_reason": None,
    "ksp_monitor_true_residual": None,
    "ksp_max_it": 100,
    "ksp_atol": 1e-12,
    "ksp_rtol": 1e-12,
    "pc_type": "python",
    "pc_python_type": "__main__.MassInvPC",
    "Mp_pc_type": "lu",
}

split1_lsc_iterative = {
    "ksp_type": "gmres",
    "ksp_monitor_true_residual": None,
    "ksp_rtol": 1e-4,
    "ksp_gmres_modifiedgramschmidt": None,
    "ksp_converged_reason": None,
    "pc_type": "lsc",
    "lsc_pc_type": "cholesky",
    "pc_lsc_scale_diag": True,
}

solver_parameters = {
    "ksp_type": "fgmres",
    "ksp_atol": 1e-7,
    "ksp_rtol": 1e-6,
    "ksp_monitor_true_residual": None,
    "pc_type": "fieldsplit",
    "pc_fieldsplit_type": "schur",
    "pc_fieldsplit_schur_fact_type": "full",
    "pc_fieldsplit_schur_precondition": "selfp",
    "fieldsplit_0": assembled_hypre_settings,
    "fieldsplit_1": split1_lsc_iterative,
}

up.assign(0)

fd.solve(a_form == L, up, bcs=bcs, solver_parameters=solver_parameters)

u, p = up.subfunctions
u.rename("Velocity")
p.rename("Pressure")
fd.VTKFile("slimStokes.pvd").write(u, p)

Reply via email to