Source code for simcardems.newton_solver

import dolfin
import pulse

from . import utils


logger = utils.getLogger(__name__)


[docs] class MechanicsNewtonSolver(dolfin.NewtonSolver): def __init__( self, problem: pulse.NonlinearProblem, state: dolfin.Function, update_cb=None, parameters=None, ): logger.debug(f"Initialize NewtonSolver with parameters: {parameters!r}") dolfin.PETScOptions.clear() self._problem = problem self._state = state self._update_cb = update_cb # Initializing Newton solver (parent class) self.petsc_solver = dolfin.PETScKrylovSolver() super().__init__( self._state.function_space().mesh().mpi_comm(), self.petsc_solver, dolfin.PETScFactory.instance(), ) self._handle_parameters(parameters) def _handle_parameters(self, parameters): # Setting default parameters params = MechanicsNewtonSolver_ODE.default_solver_parameters() params.update(parameters) for k, v in params.items(): if self.parameters.has_parameter(k): self.parameters[k] = v if self.parameters.has_parameter_set(k): for subk, subv in params[k].items(): self.parameters[k][subk] = subv petsc = params.pop("petsc") for k, v in petsc.items(): if v is not None: dolfin.PETScOptions.set(k, v) self.newton_verbose = params.pop("newton_verbose", False) self.ksp_verbose = params.pop("ksp_verbose", False) self.debug = params.pop("debug", False) if self.newton_verbose: dolfin.set_log_level(dolfin.LogLevel.INFO) self.parameters["report"] = True if self.ksp_verbose: self.parameters["lu_solver"]["report"] = True self.parameters["lu_solver"]["verbose"] = True self.parameters["krylov_solver"]["monitor_convergence"] = True dolfin.PETScOptions.set("ksp_monitor_true_residual") self.linear_solver().set_from_options() self._residual_index = 0 self._residuals = []
[docs] def register_datacollector(self, datacollector): self._datacollector = datacollector
[docs] @staticmethod def default_solver_parameters(): return { "petsc": { "ksp_type": "preonly", # "ksp_type": "gmres", "pc_type": "lu", "pc_factor_mat_solver_type": "mumps", "mat_mumps_icntl_33": 0, }, "newton_verbose": False, "ksp_verbose": False, "debug": False, "linear_solver": "mumps", # "linear_solver": "gmres", "error_on_nonconvergence": True, "relative_tolerance": 1e-5, "absolute_tolerance": 1e-5, "maximum_iterations": 20, "report": False, "krylov_solver": { "nonzero_initial_guess": True, "absolute_tolerance": 1e-10, "relative_tolerance": 1e-10, "maximum_iterations": 1000, "monitor_convergence": False, }, "lu_solver": {"report": False, "symmetric": False, "verbose": False}, }
[docs] def converged(self, r, p, i): self._converged_called = True res = r.norm("l2") logger.debug(f"Mechanics solver residual: {res}") if self.debug: if not hasattr(self, "_datacollector"): logger.debug("No datacollector registered with the NewtonSolver") else: self._residuals.append(res) return super().converged(r, p, i)
[docs] def save_residuals(self) -> None: if not self.debug: # Only used in debug mode return if not hasattr(self, "_datacollector"): logger.debug("No datacollector registered with the NewtonSolver") else: self._datacollector.save_residual( self._residuals, index=self._residual_index, ) self._residual_index += 1 self._residuals.clear()
[docs] def solver_setup(self, A, J, p, i): self._solver_setup_called = True super().solver_setup(A, J, p, i)
[docs] def solve(self): logger.debug("Solving mechanics") self._solve_called = True ret = super().solve(self._problem, self._state.vector()) self._state.vector().apply("insert") logger.debug("Done solving mechanics") self.save_residuals() return ret
# DEBUGGING # This is just to check if we are using the overloaded functions
[docs] def check_overloads_called(self): assert getattr(self, "_converged_called", False) assert getattr(self, "_solver_setup_called", False) assert getattr(self, "_update_solution_called", False) assert getattr(self, "_solve_called", False)
[docs] class MechanicsNewtonSolver_ODE(MechanicsNewtonSolver):
[docs] def update_solution(self, x, dx, rp, p, i): self._update_solution_called = True # Update x from the dx obtained from linear solver (Newton iteration) : # x = -rp*dx (rp : relax param) super().update_solution(x, dx, rp, p, i) # Updating form of MechanicsProblem (from current lmbda, zetas, zetaw, ...) self._state.vector().set_local(x) # self._mech_problem._init_forms() # Recompute Zetas, Zetaw, Ta, lmbda # self._mech_problem.material.active.update_prev() self._update_cb()
# Re-init this solver with the new problem (note : done in _init_forms) # self.__init__(self._mech_problem)