import copy
import numpy
from .labeling import as_form
from .nystrom_stepper import StageDerivativeNystromTimeStepper
from .tableaux.ButcherTableaux import ButcherTableau, CollocationButcherTableau
from .scheme import GalerkinCollocationScheme, DiscontinuousGalerkinCollocationScheme
from .discontinuous_galerkin_stepper import getElement
from .galerkin_stepper import getTestElement
from firedrake import AuxiliaryOperatorPC, derivative
from firedrake.dmhooks import get_appctx
# Oddly, we can't turn pivoting off in scipy?
[docs]
def ldu(A):
m = A.shape[0]
assert m == A.shape[1]
L = numpy.eye(m)
U = numpy.copy(A)
D = numpy.zeros((m, m))
for k in range(m):
for i in range(k+1, m):
alpha = U[i, k] / U[k, k]
U[i, :] -= alpha * U[k, :]
L[i, k] = alpha
assert numpy.allclose(L @ U, A)
for k in range(m):
D[k, k] = U[k, k]
U[k, k:] /= D[k, k]
assert numpy.allclose(L @ D @ U, A)
return L, D, U
[docs]
def as_butcher_tableau(scheme):
"""Convert a scheme to its ButcherTableau equivalent."""
if isinstance(scheme, ButcherTableau):
return scheme
if isinstance(scheme, GalerkinCollocationScheme):
basis_type = scheme.basis_type
if isinstance(basis_type, tuple):
basis_type = basis_type[1]
element = getTestElement(basis_type, scheme.order-1)
elif isinstance(scheme, DiscontinuousGalerkinCollocationScheme):
element = getElement(scheme.basis_type, scheme.order)
else:
raise TypeError(f"Cannot convert a {type(scheme).__name__} into a ButcherTableau.")
return CollocationButcherTableau(element, scheme.order)
[docs]
def RanaLDScheme(scheme):
"""ButcherTableau for preconditioning with Atilde = LD where A=LDU."""
butcher = as_butcher_tableau(scheme)
L, D, U = ldu(butcher.A)
return butcher.reconstruct(A=L @ D)
[docs]
def RanaDUScheme(scheme):
"""ButcherTableau for preconditioning with Atilde = DU where A=LDU."""
butcher = as_butcher_tableau(scheme)
L, D, U = ldu(butcher.A)
return butcher.reconstruct(A=D @ U)
[docs]
class IRKAuxiliaryOperatorPC(AuxiliaryOperatorPC):
"""Base class that inherits from Firedrake's AuxiliaryOperatorPC class and
provides the preconditioning bilinear form associated with an auxiliary
Form and/or approximate Butcher matrix (which are provided by subclasses).
"""
[docs]
def getAtilde(self, A):
"""Derived classes produce a typically structured
approximation to A."""
raise NotImplementedError
[docs]
class RanaBase(IRKAuxiliaryOperatorPC):
"""Base class for methods out of Rana, Howle, Long, Meek, & Milestone."""
pass
[docs]
class RanaLD(RanaBase):
"""Implements Rana-type preconditioner using Atilde = LD where A=LDU."""
[docs]
def getAtilde(self, A):
L, D, U = ldu(A)
return L @ D
[docs]
class RanaDU(RanaBase):
"""Implements Rana-type preconditioner using Atilde = DU where A=LDU."""
[docs]
def getAtilde(self, A):
L, D, U = ldu(A)
return D @ U
[docs]
class NystromAuxiliaryOperatorPC(AuxiliaryOperatorPC):
"""Base class that inherits from Firedrake's AuxiliaryOperatorPC class and
provides the preconditioning bilinear form associated with an auxiliary
Form and/or approximate Nystrom matrices (which are provided by subclasses).
"""
[docs]
def getAtildes(self, A, Abar):
"""Derived classes produce a typically structured
approximation to A and Abar."""
raise NotImplementedError
[docs]
class ClinesBase(NystromAuxiliaryOperatorPC):
"""Base class for methods out of Clines/Howle/Long."""
pass
[docs]
class ClinesLD(ClinesBase):
"""Implements Clines-type preconditioner using Atilde = LD where A=LDU."""
[docs]
def getAtildes(self, A, Abar):
L, D, _ = ldu(A)
Atilde = L @ D
try:
Lbar, Dbar, _ = ldu(Abar)
except AssertionError:
raise ValueError(
"ClinesLD preconditioner failed for for this tableau. Please try again with GaussLegendre or RadauIIA methods")
Abartilde = Lbar @ Dbar
return Atilde, Abartilde