Source code for implementations.problem_classes.GeneralizedFisher_1D_FD_implicit

import numpy as np
import scipy.sparse as sp
from scipy.sparse.linalg import spsolve

from pySDC.core.errors import ParameterError, ProblemError
from pySDC.core.problem import Problem
from pySDC.helpers import problem_helper
from pySDC.implementations.datatype_classes.mesh import mesh


# noinspection PyUnusedLocal
[docs] class generalized_fisher(Problem): r""" The following one-dimensional problem is an example of a reaction-diffusion equation with traveling waves, and can be seen as a generalized Fisher equation. This class implements a special case of the Kolmogorov-Petrovskii-Piskunov problem [1]_ .. math:: \frac{\partial u}{\partial t} = \Delta u + \lambda_0^2 u (1 - u^\nu) with initial condition .. math:: u(x, 0) = \left[ 1 + \left(2^{\nu / 2} - 1\right) \exp\left(-(\nu / 2)\delta x\right) \right]^{2 / \nu} for :math:`x \in \mathbb{R}`. For .. math:: \delta = \lambda_1 - \sqrt{\lambda_1^2 - \lambda_0^2}, .. math:: \lambda_1 = \frac{\lambda_0}{2} \left[ \left(1 + \frac{\nu}{2}\right)^{1/2} + \left(1 + \frac{\nu}{2}\right)^{-1/2} \right], the exact solution is .. math:: u(x, t) = \left( 1 + \left(2^{\nu / 2} - 1\right) \exp\left(-\frac{\nu}{2}\delta (x + 2 \lambda_1 t)\right) \right)^{-2 / n}. Spatial discretization is done by centered finite differences. Parameters ---------- nvars : int, optional Spatial resolution, i.e., number of degrees of freedom in space. nu : float, optional Problem parameter :math:`\nu`. lambda0 : float, optional Problem parameter :math:`\lambda_0`. newton_maxiter : int, optional Maximum number of Newton iterations to solve the nonlinear system. newton_tol : float, optional Tolerance for Newton to terminate. interval : tuple, optional Defines the spatial domain. stop_at_nan : bool, optional Indicates if the nonlinear solver should stop if ``nan`` values arise. Attributes ---------- A : sparse matrix (CSC) Second-order FD discretization of the 1D Laplace operator. dx : float Distance between two spatial nodes. References ---------- .. [1] Z. Feng. Traveling wave behavior for a generalized fisher equation. Chaos Solitons Fractals 38(2), 481–488 (2008). """ dtype_u = mesh dtype_f = mesh def __init__( self, nvars=127, nu=1.0, lambda0=2.0, newton_maxiter=100, newton_tol=1e-12, interval=(-5, 5), stop_at_nan=True ): """Initialization routine""" # we assert that nvars looks very particular here.. this will be necessary for coarsening in space later on if (nvars + 1) % 2 != 0: raise ProblemError('setup requires nvars = 2^p - 1') # invoke super init, passing number of dofs, dtype_u and dtype_f super().__init__((nvars, None, np.dtype('float64'))) self._makeAttributeAndRegister( 'nvars', 'nu', 'lambda0', 'newton_maxiter', 'newton_tol', 'interval', 'stop_at_nan', localVars=locals(), readOnly=True, ) # compute dx and get discretization matrix A self.dx = (self.interval[1] - self.interval[0]) / (self.nvars + 1) self.A, _ = problem_helper.get_finite_difference_matrix( derivative=2, order=2, stencil_type='center', dx=self.dx, size=self.nvars + 2, dim=1, bc='dirichlet-zero', ) # noinspection PyTypeChecker
[docs] def solve_system(self, rhs, factor, u0, t): """ Simple Newton solver. Parameters ---------- rhs : dtype_f Right-hand side for the nonlinear system. factor : float Abbrev. for the node-to-node stepsize (or any other factor required). u0 : dtype_u Initial guess for the iterative solver. t : float urrent time (required here for the BC). Returns ------- u : dtype_u Solution. """ u = self.dtype_u(u0) nu = self.nu lambda0 = self.lambda0 # set up boundary values to embed inner points lam1 = lambda0 / 2.0 * ((nu / 2.0 + 1) ** 0.5 + (nu / 2.0 + 1) ** (-0.5)) sig1 = lam1 - np.sqrt(lam1**2 - lambda0**2) ul = (1 + (2 ** (nu / 2.0) - 1) * np.exp(-nu / 2.0 * sig1 * (self.interval[0] + 2 * lam1 * t))) ** (-2.0 / nu) ur = (1 + (2 ** (nu / 2.0) - 1) * np.exp(-nu / 2.0 * sig1 * (self.interval[1] + 2 * lam1 * t))) ** (-2.0 / nu) # start newton iteration n = 0 res = 99 while n < self.newton_maxiter: # form the function g with g(u) = 0 uext = np.concatenate(([ul], u, [ur])) g = u - factor * (self.A.dot(uext)[1:-1] + lambda0**2 * u * (1 - u**nu)) - rhs # if g is close to 0, then we are done res = np.linalg.norm(g, np.inf) if res < self.newton_tol: break # assemble dg dg = sp.eye(self.nvars) - factor * ( self.A[1:-1, 1:-1] + sp.diags(lambda0**2 - lambda0**2 * (nu + 1) * u**nu, offsets=0) ) # newton update: u1 = u0 - g/dg u -= spsolve(dg, g) # increase iteration count n += 1 if np.isnan(res) and self.stop_at_nan: raise ProblemError('Newton got nan after %i iterations, aborting...' % n) elif np.isnan(res): self.logger.warning('Newton got nan after %i iterations...' % n) if n == self.newton_maxiter: self.logger.warning('Newton did not converge after %i iterations, error is %s' % (n, res)) return u
[docs] def eval_f(self, u, t): """ Routine to evaluate the right-hand side of the problem. Parameters ---------- u : dtype_u Current values of the numerical solution. t : float Current time of the numerical solution is computed. Returns ------- f : dtype_f The right-hand side of the problem. """ # set up boundary values to embed inner points lam1 = self.lambda0 / 2.0 * ((self.nu / 2.0 + 1) ** 0.5 + (self.nu / 2.0 + 1) ** (-0.5)) sig1 = lam1 - np.sqrt(lam1**2 - self.lambda0**2) ul = (1 + (2 ** (self.nu / 2.0) - 1) * np.exp(-self.nu / 2.0 * sig1 * (self.interval[0] + 2 * lam1 * t))) ** ( -2 / self.nu ) ur = (1 + (2 ** (self.nu / 2.0) - 1) * np.exp(-self.nu / 2.0 * sig1 * (self.interval[1] + 2 * lam1 * t))) ** ( -2 / self.nu ) uext = np.concatenate(([ul], u, [ur])) f = self.dtype_f(self.init) f[:] = self.A.dot(uext)[1:-1] + self.lambda0**2 * u * (1 - u**self.nu) return f
[docs] def u_exact(self, t): r""" Routine to compute the exact solution at time :math:`t`. Parameters ---------- t : float Time of the exact solution. Returns ------- me : dtype_u Exact solution. """ me = self.dtype_u(self.init) xvalues = np.array([(i + 1 - (self.nvars + 1) / 2) * self.dx for i in range(self.nvars)]) lam1 = self.lambda0 / 2.0 * ((self.nu / 2.0 + 1) ** 0.5 + (self.nu / 2.0 + 1) ** (-0.5)) sig1 = lam1 - np.sqrt(lam1**2 - self.lambda0**2) me[:] = (1 + (2 ** (self.nu / 2.0) - 1) * np.exp(-self.nu / 2.0 * sig1 * (xvalues + 2 * lam1 * t))) ** ( -2.0 / self.nu ) return me