Skip to content

Commit 71fe60f

Browse files
committed
added 1D Bayreuth example
1 parent d051bfe commit 71fe60f

File tree

4 files changed

+701
-3
lines changed

4 files changed

+701
-3
lines changed

pySDC/implementations/controller_classes/controller_MPI.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -462,7 +462,8 @@ def pfasst(self, comm, num_procs):
462462
self.logger.debug('isend data: process %s, stage %s, time %s, target %s, tag %s, iter %s' %
463463
(self.S.status.slot, self.S.status.stage, self.S.time, self.S.next,
464464
l, self.S.status.iter))
465-
self.req_send[l] = self.S.levels[l].uend.isend(dest=self.S.next, tag=self.S.status.iter, comm=comm)
465+
self.req_send[l] = self.S.levels[l].uend.isend(dest=self.S.next, tag=self.S.status.iter,
466+
comm=comm)
466467

467468
if not self.S.status.first and not self.S.status.prev_done and self.params.fine_comm:
468469
self.logger.debug('recv data: process %s, stage %s, time %s, source %s, tag %s, iter %s' %
@@ -546,8 +547,9 @@ def pfasst(self, comm, num_procs):
546547
self.logger.debug('isend data: process %s, stage %s, time %s, target %s, tag %s, iter %s' %
547548
(self.S.status.slot, self.S.status.stage, self.S.time, self.S.next,
548549
l - 1, self.S.status.iter))
549-
self.req_send[l - 1] = self.S.levels[l - 1].uend.isend(dest=self.S.next, tag=self.S.status.iter,
550-
comm=comm)
550+
self.req_send[l - 1] = self.S.levels[l - 1].uend.isend(dest=self.S.next,
551+
tag=self.S.status.iter,
552+
comm=comm)
551553

552554
if not self.S.status.first and not self.S.status.prev_done and self.params.fine_comm:
553555
self.logger.debug('recv data: process %s, stage %s, time %s, source %s, tag %s, iter %s' %
Lines changed: 299 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,299 @@
1+
2+
import numpy as np
3+
import scipy.sparse as sp
4+
from scipy.sparse.linalg import spsolve
5+
6+
from pySDC.core.Errors import ParameterError, ProblemError
7+
from pySDC.core.Problem import ptype
8+
from pySDC.implementations.datatype_classes.mesh import mesh
9+
10+
11+
# noinspection PyUnusedLocal
12+
class allencahn_front(ptype):
13+
"""
14+
Example implementing the Allen-Cahn equation in 1D with finite differences and inhomogeneous Dirichlet-BC,
15+
with driving force, 0-1 formulation (Bayreuth example)
16+
17+
Attributes:
18+
A: second-order FD discretization of the 1D laplace operator
19+
dx: distance between two spatial nodes
20+
"""
21+
22+
def __init__(self, problem_params, dtype_u=mesh, dtype_f=mesh):
23+
"""
24+
Initialization routine
25+
26+
Args:
27+
problem_params (dict): custom parameters for the example
28+
dtype_u: mesh data type (will be passed parent class)
29+
dtype_f: mesh data type (will be passed parent class)
30+
"""
31+
32+
# these parameters will be used later, so assert their existence
33+
essential_keys = ['nvars', 'dw', 'eps', 'newton_maxiter', 'newton_tol', 'interval']
34+
for key in essential_keys:
35+
if key not in problem_params:
36+
msg = 'need %s to instantiate problem, only got %s' % (key, str(problem_params.keys()))
37+
raise ParameterError(msg)
38+
39+
# we assert that nvars looks very particular here.. this will be necessary for coarsening in space later on
40+
if (problem_params['nvars'] + 1) % 2 != 0:
41+
raise ProblemError('setup requires nvars = 2^p - 1')
42+
43+
if 'stop_at_nan' not in problem_params:
44+
problem_params['stop_at_nan'] = True
45+
46+
# invoke super init, passing number of dofs, dtype_u and dtype_f
47+
super(allencahn_front, self).__init__(problem_params['nvars'], dtype_u, dtype_f, problem_params)
48+
49+
# compute dx and get discretization matrix A
50+
self.dx = (self.params.interval[1] - self.params.interval[0]) / (self.params.nvars + 1)
51+
self.xvalues = np.array([(i + 1 - (self.params.nvars + 1) / 2) * self.dx for i in range(self.params.nvars)])
52+
53+
self.A = self.__get_A(self.params.nvars, self.dx)
54+
self.uext = self.dtype_u(self.init + 2, val=0.0)
55+
56+
self.newton_itercount = 0
57+
self.lin_itercount = 0
58+
self.newton_ncalls = 0
59+
self.lin_ncalls = 0
60+
61+
@staticmethod
62+
def __get_A(N, dx):
63+
"""
64+
Helper function to assemble FD matrix A in sparse format
65+
66+
Args:
67+
N (int): number of dofs
68+
dx (float): distance between two spatial nodes
69+
70+
Returns:
71+
scipy.sparse.csc_matrix: matrix A in CSC format
72+
"""
73+
74+
stencil = [1, -2, 1]
75+
A = sp.diags(stencil, [-1, 0, 1], shape=(N + 2, N + 2), format='lil')
76+
A *= 1.0 / (dx ** 2)
77+
78+
return A
79+
80+
def solve_system(self, rhs, factor, u0, t):
81+
"""
82+
Simple Newton solver
83+
84+
Args:
85+
rhs (dtype_f): right-hand side for the nonlinear system
86+
factor (float): abbrev. for the node-to-node stepsize (or any other factor required)
87+
u0 (dtype_u): initial guess for the iterative solver
88+
t (float): current time (required here for the BC)
89+
90+
Returns:
91+
dtype_u: solution u
92+
"""
93+
94+
u = self.dtype_u(u0).values
95+
z = self.dtype_u(self.init, val=0.0).values
96+
eps2 = self.params.eps ** 2
97+
dw = self.params.dw
98+
99+
Id = sp.eye(self.params.nvars)
100+
101+
v = 3.0 * np.sqrt(2) * self.params.eps * self.params.dw
102+
self.uext.values[0] = 0.5 * (1 + np.tanh((self.params.interval[0] - v * t) / (np.sqrt(2) * self.params.eps)))
103+
self.uext.values[-1] = 0.5 * (1 + np.tanh((self.params.interval[1] - v * t) / (np.sqrt(2) * self.params.eps)))
104+
105+
A = self.A[1:-1, 1:-1]
106+
# start newton iteration
107+
n = 0
108+
res = 99
109+
while n < self.params.newton_maxiter:
110+
# print(n)
111+
# form the function g with g(u) = 0
112+
self.uext.values[1:-1] = u[:]
113+
g = u - rhs.values \
114+
- factor * (self.A.dot(self.uext.values)[1:-1] - 2.0 / eps2 * u * (1.0 - u) * (1.0 - 2.0 * u) -
115+
6.0 * dw * u * (1.0 - u))
116+
117+
# if g is close to 0, then we are done
118+
res = np.linalg.norm(g, np.inf)
119+
120+
if res < self.params.newton_tol:
121+
break
122+
123+
# assemble dg
124+
dg = Id - factor * (A - 2.0 / eps2 * sp.diags(
125+
(1.0 - u) * (1.0 - 2.0 * u) - u * ((1.0 - 2.0 * u) + 2.0 * (1.0 - u)), offsets=0) - 6.0 * dw * sp.diags(
126+
(1.0 - u) - u, offsets=0))
127+
128+
# newton update: u1 = u0 - g/dg
129+
u -= spsolve(dg, g)
130+
# u -= gmres(dg, g, x0=z, tol=self.params.lin_tol)[0]
131+
# increase iteration count
132+
n += 1
133+
134+
if np.isnan(res) and self.params.stop_at_nan:
135+
raise ProblemError('Newton got nan after %i iterations, aborting...' % n)
136+
elif np.isnan(res):
137+
self.logger.warning('Newton got nan after %i iterations...' % n)
138+
139+
if n == self.params.newton_maxiter:
140+
self.logger.warning('Newton did not converge after %i iterations, error is %s' % (n, res))
141+
142+
self.newton_ncalls += 1
143+
self.newton_itercount += n
144+
145+
me = self.dtype_u(self.init)
146+
me.values = u
147+
148+
return me
149+
150+
def eval_f(self, u, t):
151+
"""
152+
Routine to evaluate the RHS
153+
154+
Args:
155+
u (dtype_u): current values
156+
t (float): current time
157+
158+
Returns:
159+
dtype_f: the RHS
160+
"""
161+
# set up boundary values to embed inner points
162+
v = 3.0 * np.sqrt(2) * self.params.eps * self.params.dw
163+
self.uext.values[0] = 0.5 * (1 + np.tanh((self.params.interval[0] - v * t) / (np.sqrt(2) * self.params.eps)))
164+
self.uext.values[-1] = 0.5 * (1 + np.tanh((self.params.interval[1] - v * t) / (np.sqrt(2) * self.params.eps)))
165+
166+
self.uext.values[1:-1] = u.values[:]
167+
168+
f = self.dtype_f(self.init)
169+
f.values = self.A.dot(self.uext.values)[1:-1] - \
170+
2.0 / self.params.eps ** 2 * u.values * (1.0 - u.values) * (1.0 - 2 * u.values) - \
171+
6.0 * self.params.dw * u.values * (1.0 - u.values)
172+
return f
173+
174+
def u_exact(self, t):
175+
"""
176+
Routine to compute the exact solution at time t
177+
178+
Args:
179+
t (float): current time
180+
181+
Returns:
182+
dtype_u: exact solution
183+
"""
184+
185+
v = 3.0 * np.sqrt(2) * self.params.eps * self.params.dw
186+
me = self.dtype_u(self.init, val=0.0)
187+
me.values = 0.5 * (1 + np.tanh((self.xvalues - v * t) / (np.sqrt(2) * self.params.eps)))
188+
return me
189+
190+
191+
# noinspection PyUnusedLocal
192+
class allencahn_front_finel(allencahn_front):
193+
"""
194+
Example implementing the Allen-Cahn equation in 1D with finite differences and inhomogeneous Dirichlet-BC,
195+
with driving force, 0-1 formulation (Bayreuth example), Finel's trick/parametrization
196+
197+
Attributes:
198+
A: second-order FD discretization of the 1D laplace operator
199+
dx: distance between two spatial nodes
200+
"""
201+
202+
# noinspection PyTypeChecker
203+
def solve_system(self, rhs, factor, u0, t):
204+
"""
205+
Simple Newton solver
206+
207+
Args:
208+
rhs (dtype_f): right-hand side for the nonlinear system
209+
factor (float): abbrev. for the node-to-node stepsize (or any other factor required)
210+
u0 (dtype_u): initial guess for the iterative solver
211+
t (float): current time (required here for the BC)
212+
213+
Returns:
214+
dtype_u: solution u
215+
"""
216+
217+
u = self.dtype_u(u0).values
218+
z = self.dtype_u(self.init, val=0.0).values
219+
eps2 = self.params.eps ** 2
220+
dw = self.params.dw
221+
a2 = np.tanh(self.dx / (np.sqrt(2) * self.params.eps)) ** 2
222+
223+
Id = sp.eye(self.params.nvars)
224+
225+
v = 3.0 * np.sqrt(2) * self.params.eps * self.params.dw
226+
self.uext.values[0] = 0.5 * (1 + np.tanh((self.params.interval[0] - v * t) / (np.sqrt(2) * self.params.eps)))
227+
self.uext.values[-1] = 0.5 * (1 + np.tanh((self.params.interval[1] - v * t) / (np.sqrt(2) * self.params.eps)))
228+
229+
A = self.A[1:-1, 1:-1]
230+
# start newton iteration
231+
n = 0
232+
res = 99
233+
while n < self.params.newton_maxiter:
234+
# print(n)
235+
# form the function g with g(u) = 0
236+
self.uext.values[1:-1] = u[:]
237+
gprim = 1.0 / self.dx ** 2 * ((1.0 - a2) / (1.0 - a2 * (2.0 * u - 1.0) ** 2) - 1.0) * (2.0 * u - 1.0)
238+
g = u - rhs.values - factor * (self.A.dot(self.uext.values)[1:-1] - 1.0 * gprim - 6.0 * dw * u * (1.0 - u))
239+
240+
# if g is close to 0, then we are done
241+
res = np.linalg.norm(g, np.inf)
242+
243+
if res < self.params.newton_tol:
244+
break
245+
246+
# assemble dg
247+
dgprim = 1.0 / self.dx ** 2 * \
248+
(2.0 * ((1.0 - a2) / (1.0 - a2 * (2.0 * u - 1.0) ** 2) - 1.0) +
249+
(2.0 * u - 1) ** 2 * (1.0 - a2) * 4 * a2 / (1.0 - a2 * (2.0 * u - 1.0) ** 2) ** 2)
250+
251+
dg = Id - factor * (A - 1.0 * sp.diags(dgprim, offsets=0) - 6.0 * dw * sp.diags((1.0 - u) - u, offsets=0))
252+
253+
# newton update: u1 = u0 - g/dg
254+
u -= spsolve(dg, g)
255+
# For some reason, doing cg or gmres does not work so well here...
256+
# u -= cg(dg, g, x0=z, tol=self.params.lin_tol)[0]
257+
# increase iteration count
258+
n += 1
259+
260+
if np.isnan(res) and self.params.stop_at_nan:
261+
raise ProblemError('Newton got nan after %i iterations, aborting...' % n)
262+
elif np.isnan(res):
263+
self.logger.warning('Newton got nan after %i iterations...' % n)
264+
265+
if n == self.params.newton_maxiter:
266+
self.logger.warning('Newton did not converge after %i iterations, error is %s' % (n, res))
267+
268+
self.newton_ncalls += 1
269+
self.newton_itercount += n
270+
271+
me = self.dtype_u(self.init)
272+
me.values = u
273+
274+
return me
275+
276+
def eval_f(self, u, t):
277+
"""
278+
Routine to evaluate the RHS
279+
280+
Args:
281+
u (dtype_u): current values
282+
t (float): current time
283+
284+
Returns:
285+
dtype_f: the RHS
286+
"""
287+
# set up boundary values to embed inner points
288+
v = 3.0 * np.sqrt(2) * self.params.eps * self.params.dw
289+
self.uext.values[0] = 0.5 * (1 + np.tanh((self.params.interval[0] - v * t) / (np.sqrt(2) * self.params.eps)))
290+
self.uext.values[-1] = 0.5 * (1 + np.tanh((self.params.interval[1] - v * t) / (np.sqrt(2) * self.params.eps)))
291+
292+
self.uext.values[1:-1] = u.values[:]
293+
294+
a2 = np.tanh(self.dx / (np.sqrt(2) * self.params.eps)) ** 2
295+
gprim = 1.0 / self.dx ** 2 * ((1.0 - a2) / (1.0 - a2 * (2.0 * u.values - 1.0) ** 2) - 1) \
296+
* (2.0 * u.values - 1.0)
297+
f = self.dtype_f(self.init)
298+
f.values = self.A.dot(self.uext.values)[1:-1] - 1.0 * gprim - 6.0 * self.params.dw * u.values * (1.0 - u.values)
299+
return f

0 commit comments

Comments
 (0)