Coverage for pySDC/implementations/problem_classes/Van_der_Pol_implicit.py: 96%
57 statements
« prev ^ index » next coverage.py v7.5.0, created at 2024-04-29 09:02 +0000
« prev ^ index » next coverage.py v7.5.0, created at 2024-04-29 09:02 +0000
1import numpy as np
3from pySDC.core.Errors import ProblemError
4from pySDC.core.Problem import ptype, WorkCounter
5from pySDC.implementations.datatype_classes.mesh import mesh
8# noinspection PyUnusedLocal
9class vanderpol(ptype):
10 r"""
11 This class implements the stiff Van der Pol oscillator given by the equation
13 .. math::
14 \frac{d^2 u(t)}{d t^2} - \mu (1 - u(t)^2) \frac{d u(t)}{dt} + u(t) = 0.
16 Parameters
17 ----------
18 u0 : sequence of array_like, optional
19 Initial condition.
20 mu : float, optional
21 Stiff parameter :math:`\mu`.
22 newton_maxiter : int, optional
23 Maximum number of iterations for Newton's method to terminate.
24 newton_tol : float, optional
25 Tolerance for Newton to terminate.
26 stop_at_nan : bool, optional
27 Indicate whether Newton's method should stop if ``nan`` values arise.
28 crash_at_maxiter : bool, optional
29 Indicates whether Newton's method should stop if maximum number of iterations
30 ``newton_maxiter`` is reached.
32 Attributes
33 ----------
34 work_counters : WorkCounter
35 Counts different things, here: Number of evaluations of the right-hand side in ``eval_f``
36 and number of Newton calls in each Newton iterations are counted.
37 """
39 dtype_u = mesh
40 dtype_f = mesh
42 def __init__(self, u0=None, mu=5.0, newton_maxiter=100, newton_tol=1e-9, stop_at_nan=True, crash_at_maxiter=True):
43 """Initialization routine"""
44 nvars = 2
46 if u0 is None:
47 u0 = [2.0, 0.0]
49 super().__init__((nvars, None, np.dtype('float64')))
50 self._makeAttributeAndRegister('nvars', 'u0', localVars=locals(), readOnly=True)
51 self._makeAttributeAndRegister(
52 'mu', 'newton_maxiter', 'newton_tol', 'stop_at_nan', 'crash_at_maxiter', localVars=locals()
53 )
54 self.work_counters['newton'] = WorkCounter()
55 self.work_counters['rhs'] = WorkCounter()
57 def u_exact(self, t, u_init=None, t_init=None):
58 r"""
59 Routine to approximate the exact solution at time t by ``SciPy`` or give initial conditions when called at :math:`t=0`.
61 Parameters
62 ----------
63 t : float
64 Current time.
65 u_init : pySDC.problem.vanderpol.dtype_u
66 Initial conditions for getting the exact solution.
67 t_init : float
68 The starting time.
70 Returns
71 -------
72 me : dtype_u
73 Approximate exact solution.
74 """
76 me = self.dtype_u(self.init)
78 if t > 0.0:
80 def eval_rhs(t, u):
81 return self.eval_f(u, t)
83 me[:] = self.generate_scipy_reference_solution(eval_rhs, t, u_init, t_init)
84 else:
85 me[:] = self.u0
86 return me
88 def eval_f(self, u, t):
89 """
90 Routine to compute the right-hand side for both components simultaneously.
92 Parameters
93 ----------
94 u : dtype_u
95 Current values of the numerical solution.
96 t : float
97 Current time at which the numerical solution is computed (not used here).
99 Returns
100 -------
101 f : dtype_f
102 The right-hand side (contains 2 components).
103 """
105 x1 = u[0]
106 x2 = u[1]
107 f = self.f_init
108 f[0] = x2
109 f[1] = self.mu * (1 - x1**2) * x2 - x1
110 self.work_counters['rhs']()
111 return f
113 def solve_system(self, rhs, dt, u0, t):
114 """
115 Simple Newton solver for the nonlinear system.
117 Parameters
118 ----------
119 rhs : dtype_f
120 Right-hand side for the nonlinear system.
121 dt : float
122 Abbrev. for the node-to-node stepsize (or any other factor required).
123 u0 : dtype_u
124 Initial guess for the iterative solver.
125 t : float
126 Current time (e.g. for time-dependent BCs).
128 Returns
129 -------
130 u : dtype_u
131 The solution u.
132 """
134 mu = self.mu
136 # create new mesh object from u0 and set initial values for iteration
137 u = self.dtype_u(u0)
138 x1 = u[0]
139 x2 = u[1]
141 # start newton iteration
142 n = 0
143 res = 99
144 while n < self.newton_maxiter:
145 # form the function g with g(u) = 0
146 g = np.array([x1 - dt * x2 - rhs[0], x2 - dt * (mu * (1 - x1**2) * x2 - x1) - rhs[1]])
148 # if g is close to 0, then we are done
149 res = np.linalg.norm(g, np.inf)
150 if res < self.newton_tol or np.isnan(res):
151 break
153 # prefactor for dg/du
154 c = 1.0 / (-2 * dt**2 * mu * x1 * x2 - dt**2 - 1 + dt * mu * (1 - x1**2))
155 # assemble dg/du
156 dg = c * np.array([[dt * mu * (1 - x1**2) - 1, -dt], [2 * dt * mu * x1 * x2 + dt, -1]])
158 # newton update: u1 = u0 - g/dg
159 u -= np.dot(dg, g)
161 # set new values and increase iteration count
162 x1 = u[0]
163 x2 = u[1]
164 n += 1
165 self.work_counters['newton']()
167 if np.isnan(res) and self.stop_at_nan:
168 self.logger.warning('Newton got nan after %i iterations...' % n)
169 raise ProblemError('Newton got nan after %i iterations, aborting...' % n)
170 elif np.isnan(res):
171 self.logger.warning('Newton got nan after %i iterations...' % n)
173 if n == self.newton_maxiter and self.crash_at_maxiter:
174 raise ProblemError('Newton did not converge after %i iterations, error is %s' % (n, res))
176 return u