Skip to content

Commit 5795e36

Browse files
committed
more playing around with the Bayreuth example
1 parent b787fe6 commit 5795e36

File tree

4 files changed

+578
-0
lines changed

4 files changed

+578
-0
lines changed

pySDC/implementations/problem_classes/AllenCahn_1D_FD.py

Lines changed: 242 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -374,3 +374,245 @@ def eval_f(self, u, t):
374374
f = self.dtype_f(self.init)
375375
f.values = self.A.dot(self.uext.values)[1:-1] - 1.0 * gprim - 6.0 * self.params.dw * u.values * (1.0 - u.values)
376376
return f
377+
378+
379+
class allencahn_periodic_fullyimplicit(ptype):
380+
"""
381+
Example implementing the Allen-Cahn equation in 1D with finite differences and periodic BC,
382+
with driving force, 0-1 formulation (Bayreuth example)
383+
384+
Attributes:
385+
A: second-order FD discretization of the 1D laplace operator
386+
dx: distance between two spatial nodes
387+
"""
388+
389+
def __init__(self, problem_params, dtype_u=mesh, dtype_f=mesh):
390+
"""
391+
Initialization routine
392+
393+
Args:
394+
problem_params (dict): custom parameters for the example
395+
dtype_u: mesh data type (will be passed parent class)
396+
dtype_f: mesh data type (will be passed parent class)
397+
"""
398+
399+
# these parameters will be used later, so assert their existence
400+
essential_keys = ['nvars', 'dw', 'eps', 'newton_maxiter', 'newton_tol', 'interval', 'radius']
401+
for key in essential_keys:
402+
if key not in problem_params:
403+
msg = 'need %s to instantiate problem, only got %s' % (key, str(problem_params.keys()))
404+
raise ParameterError(msg)
405+
406+
# we assert that nvars looks very particular here.. this will be necessary for coarsening in space later on
407+
if (problem_params['nvars']) % 2 != 0:
408+
raise ProblemError('setup requires nvars = 2^p')
409+
410+
if 'stop_at_nan' not in problem_params:
411+
problem_params['stop_at_nan'] = True
412+
413+
# invoke super init, passing number of dofs, dtype_u and dtype_f
414+
super(allencahn_periodic_fullyimplicit, self).__init__(problem_params['nvars'], dtype_u, dtype_f,
415+
problem_params)
416+
417+
# compute dx and get discretization matrix A
418+
self.dx = (self.params.interval[1] - self.params.interval[0]) / self.params.nvars
419+
self.xvalues = np.array([self.params.interval[0] + i * self.dx for i in range(self.params.nvars)])
420+
421+
self.A = self.__get_A(self.params.nvars, self.dx)
422+
423+
self.newton_itercount = 0
424+
self.lin_itercount = 0
425+
self.newton_ncalls = 0
426+
self.lin_ncalls = 0
427+
428+
@staticmethod
429+
def __get_A(N, dx):
430+
"""
431+
Helper function to assemble FD matrix A in sparse format
432+
433+
Args:
434+
N (int): number of dofs
435+
dx (float): distance between two spatial nodes
436+
437+
Returns:
438+
scipy.sparse.csc_matrix: matrix A in CSC format
439+
"""
440+
441+
stencil = [1, -2, 1]
442+
zero_pos = 2
443+
dstencil = np.concatenate((stencil, np.delete(stencil, zero_pos - 1)))
444+
offsets = np.concatenate(([N - i - 1 for i in reversed(range(zero_pos - 1))],
445+
[i - zero_pos + 1 for i in range(zero_pos - 1, len(stencil))]))
446+
doffsets = np.concatenate((offsets, np.delete(offsets, zero_pos - 1) - N))
447+
448+
A = sp.diags(dstencil, doffsets, shape=(N, N), format='csc')
449+
A *= 1.0 / (dx ** 2)
450+
451+
return A
452+
453+
def solve_system(self, rhs, factor, u0, t):
454+
"""
455+
Simple Newton solver
456+
457+
Args:
458+
rhs (dtype_f): right-hand side for the nonlinear system
459+
factor (float): abbrev. for the node-to-node stepsize (or any other factor required)
460+
u0 (dtype_u): initial guess for the iterative solver
461+
t (float): current time (required here for the BC)
462+
463+
Returns:
464+
dtype_u: solution u
465+
"""
466+
467+
u = self.dtype_u(u0).values
468+
eps2 = self.params.eps ** 2
469+
dw = self.params.dw
470+
471+
Id = sp.eye(self.params.nvars)
472+
473+
# start newton iteration
474+
n = 0
475+
res = 99
476+
while n < self.params.newton_maxiter:
477+
# print(n)
478+
# form the function g with g(u) = 0
479+
g = u - rhs.values - factor * (self.A.dot(u) - 2.0 / eps2 * u * (1.0 - u) * (1.0 - 2.0 * u) -
480+
6.0 * dw * u * (1.0 - u))
481+
482+
# if g is close to 0, then we are done
483+
res = np.linalg.norm(g, np.inf)
484+
485+
if res < self.params.newton_tol:
486+
break
487+
488+
# assemble dg
489+
dg = Id - factor * (self.A - 2.0 / eps2 * sp.diags(
490+
(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(
491+
(1.0 - u) - u, offsets=0))
492+
493+
# newton update: u1 = u0 - g/dg
494+
u -= spsolve(dg, g)
495+
# u -= gmres(dg, g, x0=z, tol=self.params.lin_tol)[0]
496+
# increase iteration count
497+
n += 1
498+
499+
if np.isnan(res) and self.params.stop_at_nan:
500+
raise ProblemError('Newton got nan after %i iterations, aborting...' % n)
501+
elif np.isnan(res):
502+
self.logger.warning('Newton got nan after %i iterations...' % n)
503+
504+
if n == self.params.newton_maxiter:
505+
self.logger.warning('Newton did not converge after %i iterations, error is %s' % (n, res))
506+
507+
self.newton_ncalls += 1
508+
self.newton_itercount += n
509+
510+
me = self.dtype_u(self.init)
511+
me.values = u
512+
513+
return me
514+
515+
def eval_f(self, u, t):
516+
"""
517+
Routine to evaluate the RHS
518+
519+
Args:
520+
u (dtype_u): current values
521+
t (float): current time
522+
523+
Returns:
524+
dtype_f: the RHS
525+
"""
526+
f = self.dtype_f(self.init)
527+
f.values = self.A.dot(u.values) - \
528+
2.0 / self.params.eps ** 2 * u.values * (1.0 - u.values) * (1.0 - 2 * u.values) - \
529+
6.0 * self.params.dw * u.values * (1.0 - u.values)
530+
return f
531+
532+
def u_exact(self, t):
533+
"""
534+
Routine to compute the exact solution at time t
535+
536+
Args:
537+
t (float): current time
538+
539+
Returns:
540+
dtype_u: exact solution
541+
"""
542+
543+
v = 3.0 * np.sqrt(2) * self.params.eps * self.params.dw
544+
me = self.dtype_u(self.init, val=0.0)
545+
me.values = 0.5 * (1 + np.tanh((self.params.radius - abs(self.xvalues) - v * t) /
546+
(np.sqrt(2) * self.params.eps)))
547+
return me
548+
549+
550+
class allencahn_periodic_semiimplicit(allencahn_periodic_fullyimplicit):
551+
"""
552+
Example implementing the Allen-Cahn equation in 1D with finite differences and periodic BC,
553+
with driving force, 0-1 formulation (Bayreuth example)
554+
"""
555+
556+
def __init__(self, problem_params, dtype_u=mesh, dtype_f=rhs_imex_mesh):
557+
"""
558+
Initialization routine
559+
560+
Args:
561+
problem_params (dict): custom parameters for the example
562+
dtype_u: mesh data type (will be passed parent class)
563+
dtype_f: mesh data type (will be passed parent class)
564+
"""
565+
566+
# these parameters will be used later, so assert their existence
567+
essential_keys = ['nvars', 'dw', 'eps', 'newton_maxiter', 'newton_tol', 'interval', 'radius']
568+
for key in essential_keys:
569+
if key not in problem_params:
570+
msg = 'need %s to instantiate problem, only got %s' % (key, str(problem_params.keys()))
571+
raise ParameterError(msg)
572+
573+
# we assert that nvars looks very particular here.. this will be necessary for coarsening in space later on
574+
if (problem_params['nvars']) % 2 != 0:
575+
raise ProblemError('setup requires nvars = 2^p')
576+
577+
if 'stop_at_nan' not in problem_params:
578+
problem_params['stop_at_nan'] = True
579+
580+
# invoke super init, passing number of dofs, dtype_u and dtype_f
581+
super(allencahn_periodic_semiimplicit, self).__init__(problem_params, dtype_u, dtype_f)
582+
583+
self.A -= sp.eye(self.init) * 2.0 / self.params.eps ** 2
584+
585+
def solve_system(self, rhs, factor, u0, t):
586+
"""
587+
Simple linear solver for (I-factor*A)u = rhs
588+
589+
Args:
590+
rhs (dtype_f): right-hand side for the linear system
591+
factor (float): abbrev. for the local stepsize (or any other factor required)
592+
u0 (dtype_u): initial guess for the iterative solver
593+
t (float): current time (e.g. for time-dependent BCs)
594+
595+
Returns:
596+
dtype_u: solution as mesh
597+
"""
598+
599+
me = self.dtype_u(u0)
600+
me.values = spsolve(sp.eye(self.params.nvars, format='csc') - factor * self.A, rhs.values)
601+
return me
602+
603+
def eval_f(self, u, t):
604+
"""
605+
Routine to evaluate the RHS
606+
607+
Args:
608+
u (dtype_u): current values
609+
t (float): current time
610+
611+
Returns:
612+
dtype_f: the RHS
613+
"""
614+
f = self.dtype_f(self.init)
615+
f.impl.values = self.A.dot(u.values)
616+
f.expl.values = -2.0 / self.params.eps ** 2 * u.values * (1.0 - u.values) * (1.0 - 2 * u.values) - \
617+
6.0 * self.params.dw * u.values * (1.0 - u.values) + 2.0 / self.params.eps ** 2 * u.values
618+
return f

0 commit comments

Comments
 (0)