@@ -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