# Copyright 2023, 2024 Johannes Reding, Julius Zimmermann
# SPDX-License-Identifier: GPL-3.0-or-later
import logging
from abc import ABC, abstractmethod
import ngsolve
import numpy as np
_logger = logging.getLogger(__name__)
[docs]class Preconditioner(ABC):
"""Define a preconditioner."""
[docs] @abstractmethod
def to_dictionary(self) -> dict:
"""Prepare dictionary to be passed to NGSolve."""
pass
[docs]class BDDCPreconditioner(Preconditioner):
"""BDDC preconditioner, highly efficient for higher-order FEM."""
def __init__(self, coarsetype: str = "h1amg") -> None:
_logger.debug(f"Initialized BDDC preconditioner with coarsetype: {coarsetype}")
self.type = "bddc"
self.coarsetype = coarsetype
[docs] def to_dictionary(self) -> dict:
"""Prepare dictionary to be passed to NGSolve."""
return {"type": self.type, "coarsetype": self.coarsetype}
[docs]class LocalPreconditioner(Preconditioner):
"""Jacobi preconditioner."""
def __init__(self) -> None:
_logger.debug("Initialized Jacobi preconditioner")
self.type = "local"
[docs] def to_dictionary(self) -> dict:
"""Prepare dictionary to be passed to NGSolve."""
return {"type": self.type}
[docs]class CustomizedLocalPreconditioner(Preconditioner):
"""Custom diagonal Jacobi preconditioner."""
def __init__(self) -> None:
_logger.debug("Initialized customized Jacobi preconditioner")
self.type = "customized_local"
[docs] def to_dictionary(self) -> dict:
"""Prepare dictionary to be passed to the solver."""
return {"type": self.type}
[docs]class JacobiPreconditioner(ngsolve.BaseMatrix):
"""Simple diagonal Jacobi preconditioner as an NGSolve operator."""
def __init__(self, inv_diag: np.ndarray, freedofs, template_vec) -> None:
super().__init__()
self._inv_diag = inv_diag
self._freedofs = np.array(freedofs, dtype=bool)
self._template_vec = template_vec
[docs] def Mult(self, x, y) -> None:
"""Apply the preconditioner to ``x`` and store the result in ``y``."""
x_np = x.FV().NumPy()
y_np = y.FV().NumPy()
y_np[:] = self._inv_diag * x_np
y_np[~self._freedofs] = 0.0
[docs] def CreateColVector(self):
"""Create a compatible column vector."""
return self._template_vec.CreateVector()
[docs] def CreateRowVector(self):
"""Create a compatible row vector."""
return self._template_vec.CreateVector()
@property
def height(self) -> int:
"""Return the operator height."""
return len(self._inv_diag)
@property
def width(self) -> int:
"""Return the operator width."""
return len(self._inv_diag)
[docs]class MultigridPreconditioner(Preconditioner):
"""Geometric multigrid preconditioner."""
def __init__(self) -> None:
_logger.debug("Initialized multigrid preconditioner")
self.type = "multigrid"
[docs] def to_dictionary(self) -> dict:
"""Prepare dictionary to be passed to NGSolve."""
return {"type": self.type}
[docs]class AMGPreconditioner(Preconditioner):
"""Algebraic multigrid preconditioner."""
def __init__(self) -> None:
_logger.debug("Initialized AMG preconditioner")
self.type = "h1amg"
[docs] def to_dictionary(self) -> dict:
"""Prepare dictionary to be passed to NGSolve."""
return {"type": self.type}
[docs]class DirectPreconditioner(Preconditioner):
"""Direct solver."""
def __init__(self, inverse: str = "") -> None:
_logger.debug("Use direct solver as preconditioner")
self.type = "direct"
self.inverse = inverse
[docs] def to_dictionary(self) -> dict:
"""Prepare dictionary to be passed to NGSolve."""
if self.inverse == "":
return {"type": self.type}
return {"type": self.type, "inverse": self.inverse}