Skip to content
4 changes: 2 additions & 2 deletions docs/source/userguide/troubleshooting.rst
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ We are simply solving a set of equations using the finite element method, and ar
Solver doesn't converge
^^^^^^^^^^^^^^^^^^^^^^^

The first thing to check is the details of the Newton solver iterations.
To do so, you must set the ``log_level`` to ``20`` (default is ``40``).
The first thing to check is the details of the SNES Newton solver iterations.
To do so, you must set the ``log_level`` to ``INFO`` or ``DEBUG``.
This will provide more information during the solving stage.

.. testcode::
Expand Down
3 changes: 3 additions & 0 deletions src/festim/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,13 @@
from .exports.xdmf import XDMFExport
from .heat_transfer_problem import HeatTransferProblem
from .helpers import (
KSPMonitor,
SnesMonitor,
Value,
as_fenics_constant,
as_fenics_interp_expr_and_function,
as_mapped_function,
convergenceTest,
get_interpolation_points,
)
from .hydrogen_transport_problem import (
Expand Down
55 changes: 55 additions & 0 deletions src/festim/helpers.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
from collections.abc import Callable

from mpi4py import MPI

import dolfinx
import numpy as np
import ufl
Expand Down Expand Up @@ -341,3 +343,56 @@ def is_it_time_to_export(
return True

return False


_residual0 = 0
_prev_xnorm = 0


def convergenceTest(snes, it, norms):
global _residual0
xnorm, gnorm, f = norms # ||x_k||, ||x_k-x_k-1||, ||F(x_k)||

rtol, atol, stol, max_its = snes.getTolerances()

if it == 0:
_residual0 = f
if it > max_its:
return snes.ConvergedReason.DIVERGED_MAX_IT
elif f < atol:
# elif f < atol and it > 0:
return snes.ConvergedReason.CONVERGED_FNORM_ABS
elif f / _residual0 < rtol:
return snes.ConvergedReason.CONVERGED_FNORM_RELATIVE
elif gnorm < stol and it > 0:
return snes.ConvergedReason.CONVERGED_SNORM_RELATIVE
else:
return snes.ConvergedReason.ITERATING


def SnesMonitor(snes, iter, rnorm):
global _prev_xnorm
if MPI.COMM_WORLD.rank == 0:
rtol, atol, stol, max_its = snes.getTolerances()
x = snes.getSolution()
xnorm = x.norm()

stepsize_rel = abs(xnorm - _prev_xnorm) / xnorm if iter > 0 else float("inf")
if iter == 0:
relative_residual = float("inf")
else:
relative_residual = rnorm / _residual0

dolfinx.log.log(
dolfinx.log.LogLevel.INFO,
f"SNES {iter=} ; {rnorm=:.5e} ({atol=}) ; {relative_residual=:.5e} ({rtol=}) ; {stepsize_rel=:.5e} ({stol=:.5e})",
)

# Update previous xnorm
_prev_xnorm = xnorm


def KSPMonitor(ksp, iter, rnorm):
dolfinx.log.log(dolfinx.log.LogLevel.DEBUG, f"KSP {iter=}, {_residual0=:.5e}")
if MPI.COMM_WORLD.rank == 0:
dolfinx.log.log(dolfinx.log.LogLevel.DEBUG, f"KSP {iter=} {rnorm=:.5e}")
11 changes: 9 additions & 2 deletions src/festim/hydrogen_transport_problem.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@
as_fenics_constant,
get_interpolation_points,
is_it_time_to_export,
SnesMonitor,
KSPMonitor,
convergenceTest,
nmm_interpolate,
)
from festim.material import SolubilityLaw
Expand Down Expand Up @@ -1696,9 +1699,9 @@ def create_solver(self):
"snes_divergence_tolerance": "PETSC_UNLIMITED",
"ksp_type": "preonly",
"pc_type": "lu",
"snes_monitor": None,
"ksp_monitor": None,
"pc_factor_mat_solver_type": linear_solver,
"snes_error_if_not_converged": True,
"ksp_error_if_not_converged": True,
}
else:
petsc_options = self.petsc_options
Expand All @@ -1712,6 +1715,10 @@ def create_solver(self):
petsc_options_prefix="festim_solver",
)

self.solver.solver.setMonitor(SnesMonitor)
self.solver.solver.getKSP().setMonitor(KSPMonitor)
self.solver.solver.setConvergenceTest(convergenceTest)

# Delete PETSc options post setting them, ref:
# https://gitlab.com/petsc/petsc/-/issues/1201
snes = self.solver.solver
Expand Down
6 changes: 6 additions & 0 deletions src/festim/problem.py
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,8 @@ def create_solver(self):
"ksp_type": "preonly",
"pc_type": "lu",
"pc_factor_mat_solver_type": linear_solver,
"snes_error_if_not_converged": True,
"ksp_error_if_not_converged": True,
}
else:
petsc_options = self.petsc_options
Expand All @@ -196,6 +198,10 @@ def create_solver(self):
petsc_options=petsc_options,
petsc_options_prefix="festim_solver",
)

self.solver.solver.setMonitor(F.helpers.SnesMonitor)
self.solver.solver.getKSP().setMonitor(F.helpers.KSPMonitor)
self.solver.solver.setConvergenceTest(F.helpers.convergenceTest)
# Delete PETSc options post setting them, ref:
# https://gitlab.com/petsc/petsc/-/issues/1201
snes = self.solver.solver
Expand Down
1 change: 0 additions & 1 deletion test/benchmark.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
from petsc4py import PETSc

import basix
import dolfinx
import numpy as np
import tqdm.autonotebook
from dolfinx.fem import (
Expand Down
16 changes: 3 additions & 13 deletions test/test_multispecies_problem.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,12 +84,12 @@ def test_multispecies_permeation_problem():
),
]
my_model.settings = F.Settings(
atol=1e10,
atol=1e2,
rtol=1e-10,
max_iterations=30,
final_time=10,
)
my_model.settings.stepsize = F.Stepsize(initial_value=1 / 20)
my_model.settings.stepsize = F.Stepsize(initial_value=0.08)

outgassing_flux_spe_1 = F.SurfaceFlux(
field=spe_1,
Expand Down Expand Up @@ -124,17 +124,7 @@ def test_multispecies_permeation_problem():
opts[f"{option_prefix}pc_type"] = "gamg"
opts[f"{option_prefix}pc_factor_mat_solver_type"] = "mumps"
ksp.setFromOptions()
elif Version(dolfinx.__version__) > Version("0.9.0"):
snes = my_model.solver.solver
opts = PETSc.Options()
option_prefix = snes.getOptionsPrefix()
opts[f"{option_prefix}snes_atol"] = 0
opts[f"{option_prefix}snes_rtol"] = 0
opts[f"{option_prefix}snes_stol"] = 1e-8
opts[f"{option_prefix}ksp_type"] = "cg"
opts[f"{option_prefix}pc_type"] = "gamg"
opts[f"{option_prefix}pc_factor_mat_solver_type"] = "mumps"
snes.setFromOptions()

my_model.run()

times = outgassing_flux_spe_1.t
Expand Down
30 changes: 4 additions & 26 deletions test/test_permeation_problem.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,13 +83,13 @@ def test_permeation_problem(mesh_size=1001):
]

my_model.settings = F.Settings(
atol=1e10,
atol=1e2,
rtol=1e-10,
max_iterations=30,
final_time=50,
)

my_model.settings.stepsize = F.Stepsize(initial_value=1 / 20)
my_model.settings.stepsize = F.Stepsize(initial_value=0.3)

my_model.initialise()

Expand All @@ -101,17 +101,6 @@ def test_permeation_problem(mesh_size=1001):
opts[f"{option_prefix}ksp_type"] = "cg"
opts[f"{option_prefix}pc_type"] = "gamg"
ksp.setFromOptions()
elif Version(dolfinx.__version__) > Version("0.9.0"):
snes = my_model.solver.solver
opts = PETSc.Options()
option_prefix = snes.getOptionsPrefix()
opts[f"{option_prefix}snes_atol"] = 0
opts[f"{option_prefix}snes_rtol"] = 0
opts[f"{option_prefix}snes_stol"] = 1e-8
opts[f"{option_prefix}snes_max_it"] = 30
opts[f"{option_prefix}ksp_type"] = "cg"
opts[f"{option_prefix}pc_type"] = "gamg"
snes.setFromOptions()

my_model.run()

Expand Down Expand Up @@ -196,13 +185,13 @@ def test_permeation_problem_multi_volume(tmp_path):
]

my_model.settings = F.Settings(
atol=1e10,
atol=1e2,
rtol=1e-10,
max_iterations=30,
final_time=50,
)

my_model.settings.stepsize = F.Stepsize(initial_value=1 / 20)
my_model.settings.stepsize = F.Stepsize(initial_value=0.4)

my_model.initialise()

Expand All @@ -215,17 +204,6 @@ def test_permeation_problem_multi_volume(tmp_path):
opts[f"{option_prefix}pc_type"] = "gamg"
opts[f"{option_prefix}pc_factor_mat_solver_type"] = "mumps"
ksp.setFromOptions()
elif Version(dolfinx.__version__) > Version("0.9.0"):
snes = my_model.solver.solver
opts = PETSc.Options()
option_prefix = snes.getOptionsPrefix()
opts[f"{option_prefix}snes_atol"] = 0
opts[f"{option_prefix}snes_rtol"] = 0
opts[f"{option_prefix}snes_stol"] = 1e-8
opts[f"{option_prefix}ksp_type"] = "cg"
opts[f"{option_prefix}pc_type"] = "gamg"
opts[f"{option_prefix}pc_factor_mat_solver_type"] = "mumps"
snes.setFromOptions()

my_model.run()

Expand Down
2 changes: 0 additions & 2 deletions test/test_species.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
from mpi4py import MPI

import dolfinx
import numpy as np
import pytest
import ufl
from dolfinx.fem import Function, functionspace
from dolfinx.mesh import create_unit_cube
from ufl.indexed import Indexed

import festim as F

Expand Down
Loading