Skip to content

Commit 51aa701

Browse files
Added IMEX Dahlquist equation (#529)
1 parent 5e9bbb5 commit 51aa701

File tree

2 files changed

+148
-1
lines changed

2 files changed

+148
-1
lines changed

pySDC/implementations/problem_classes/TestEquation_0D.py

Lines changed: 116 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
import scipy.sparse as nsp
33

44
from pySDC.core.problem import Problem, WorkCounter
5-
from pySDC.implementations.datatype_classes.mesh import mesh
5+
from pySDC.implementations.datatype_classes.mesh import mesh, imex_mesh
66

77

88
class testequation0d(Problem):
@@ -145,3 +145,118 @@ def u_exact(self, t, u_init=None, t_init=None):
145145
me = self.dtype_u(self.init)
146146
me[:] = u_init * self.xp.exp((t - t_init) * self.lambdas)
147147
return me
148+
149+
150+
class test_equation_IMEX(Problem):
151+
dtype_f = imex_mesh
152+
dtype_u = mesh
153+
xp = np
154+
xsp = nsp
155+
156+
def __init__(self, lambdas_implicit=None, lambdas_explicit=None, u0=0.0):
157+
"""Initialization routine"""
158+
159+
if lambdas_implicit is None:
160+
re = self.xp.linspace(-30, 19, 50)
161+
im = self.xp.linspace(-50, 49, 50)
162+
lambdas_implicit = self.xp.array(
163+
[[complex(re[i], im[j]) for i in range(len(re))] for j in range(len(im))]
164+
).reshape((len(re) * len(im)))
165+
if lambdas_explicit is None:
166+
re = self.xp.linspace(-30, 19, 50)
167+
im = self.xp.linspace(-50, 49, 50)
168+
lambdas_implicit = self.xp.array(
169+
[[complex(re[i], im[j]) for i in range(len(re))] for j in range(len(im))]
170+
).reshape((len(re) * len(im)))
171+
lambdas_implicit = self.xp.asarray(lambdas_implicit)
172+
lambdas_explicit = self.xp.asarray(lambdas_explicit)
173+
174+
assert lambdas_implicit.ndim == 1, f'expect flat list here, got {lambdas_implicit}'
175+
assert lambdas_explicit.shape == lambdas_implicit.shape
176+
nvars = lambdas_implicit.size
177+
assert nvars > 0, 'expect at least one lambda parameter here'
178+
179+
# invoke super init, passing number of dofs, dtype_u and dtype_f
180+
super().__init__(init=(nvars, None, self.xp.dtype('complex128')))
181+
182+
self.A = self.xsp.diags(lambdas_implicit)
183+
self._makeAttributeAndRegister(
184+
'nvars', 'lambdas_implicit', 'lambdas_explicit', 'u0', localVars=locals(), readOnly=True
185+
)
186+
self.work_counters['rhs'] = WorkCounter()
187+
188+
def eval_f(self, u, t):
189+
"""
190+
Routine to evaluate the right-hand side of the problem.
191+
192+
Parameters
193+
----------
194+
u : dtype_u
195+
Current values of the numerical solution.
196+
t : float
197+
Current time of the numerical solution is computed.
198+
199+
Returns
200+
-------
201+
f : dtype_f
202+
The right-hand side of the problem.
203+
"""
204+
205+
f = self.dtype_f(self.init)
206+
f.impl[:] = u * self.lambdas_implicit
207+
f.expl[:] = u * self.lambdas_explicit
208+
self.work_counters['rhs']()
209+
return f
210+
211+
def solve_system(self, rhs, factor, u0, t):
212+
r"""
213+
Simple linear solver for :math:`(I-factor\cdot A)\vec{u}=\vec{rhs}`.
214+
215+
Parameters
216+
----------
217+
rhs : dtype_f
218+
Right-hand side for the linear system.
219+
factor : float
220+
Abbrev. for the local stepsize (or any other factor required).
221+
u0 : dtype_u
222+
Initial guess for the iterative solver.
223+
t : float
224+
Current time (e.g. for time-dependent BCs).
225+
226+
Returns
227+
-------
228+
me : dtype_u
229+
The solution as mesh.
230+
"""
231+
me = self.dtype_u(self.init)
232+
L = 1 - factor * self.lambdas_implicit
233+
L[L == 0] = 1 # to avoid potential divisions by zeros
234+
me[:] = rhs
235+
me /= L
236+
return me
237+
238+
def u_exact(self, t, u_init=None, t_init=None):
239+
"""
240+
Routine to compute the exact solution at time t.
241+
242+
Parameters
243+
----------
244+
t : float
245+
Time of the exact solution.
246+
u_init : pySDC.problem.testequation0d.dtype_u
247+
Initial solution.
248+
t_init : float
249+
The initial time.
250+
251+
Returns
252+
-------
253+
me : dtype_u
254+
The exact solution.
255+
"""
256+
257+
u_init = (self.u0 if u_init is None else u_init) * 1.0
258+
t_init = 0.0 if t_init is None else t_init * 1.0
259+
260+
me = self.dtype_u(self.init)
261+
me[:] = u_init * self.xp.exp((t - t_init) * (self.lambdas_implicit + self.lambdas_explicit))
262+
return me
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
def test_Dahlquist_IMEX():
2+
from pySDC.implementations.problem_classes.TestEquation_0D import test_equation_IMEX
3+
import numpy as np
4+
5+
N = 1
6+
dt = 1e-2
7+
8+
lambdas_implicit = np.ones(N) * -10
9+
lambdas_explicit = np.ones(N) * -1e-3
10+
11+
prob = test_equation_IMEX(lambdas_explicit=lambdas_explicit, lambdas_implicit=lambdas_implicit, u0=1)
12+
13+
u0 = prob.u_exact(0)
14+
15+
# do IMEX Euler step forward
16+
f0 = prob.eval_f(u0, 0)
17+
u1 = prob.solve_system(u0 + dt * f0.expl, dt, u0, 0)
18+
19+
exact = prob.u_exact(dt)
20+
error = abs(u1 - exact)
21+
error0 = abs(u0 - exact)
22+
assert error < error0 * 1e-1
23+
24+
# do explicit Euler step backwards
25+
f = prob.eval_f(u1, dt)
26+
u02 = u1 - dt * (f.impl + f0.expl)
27+
28+
assert np.allclose(u0, u02)
29+
30+
31+
if __name__ == '__main__':
32+
test_Dahlquist_IMEX()

0 commit comments

Comments
 (0)