@@ -15,8 +15,8 @@ class allencahn2d_imex(ptype):
1515 xvalues: grid points in space
1616 dx: mesh width
1717 lap: spectral operator for Laplacian
18- fft_object : planned FFT for forward transformation
19- ifft_object : planned IFFT for backward transformation
18+ rfft_object : planned real FFT for forward transformation
19+ irfft_object : planned IFFT for backward transformation
2020 """
2121
2222 def __init__ (self , problem_params , dtype_u = mesh , dtype_f = rhs_imex_mesh ):
@@ -57,28 +57,25 @@ def __init__(self, problem_params, dtype_u=mesh, dtype_f=rhs_imex_mesh):
5757 self .xvalues = np .array ([i * self .dx - self .params .L / 2.0 for i in range (self .params .nvars [0 ])])
5858
5959 kx = np .zeros (self .init [0 ])
60- ky = np .zeros (self .init [1 ])
60+ ky = np .zeros (self .init [1 ] // 2 + 1 )
6161 for i in range (0 , int (self .init [0 ] / 2 ) + 1 ):
6262 kx [i ] = 2 * np .pi / self .params .L * i
63- for i in range (0 , int (self .init [1 ] / 2 ) + 1 ):
63+ for i in range (0 , int (self .init [1 ] // 2 ) + 1 ):
6464 ky [i ] = 2 * np .pi / self .params .L * i
6565 for i in range (int (self .init [0 ] / 2 ) + 1 , self .init [0 ]):
6666 kx [i ] = 2 * np .pi / self .params .L * (- self .init [0 ] + i )
67- for i in range (int (self .init [1 ] / 2 ) + 1 , self .init [1 ]):
68- ky [i ] = 2 * np .pi / self .params .L * (- self .init [1 ] + i )
6967
70- self .lap = np .zeros (self .init )
68+ self .lap = np .zeros (( self .init [ 0 ], self . init [ 1 ] // 2 + 1 ) )
7169 for i in range (self .init [0 ]):
72- for j in range (self .init [1 ]):
70+ for j in range (self .init [1 ] // 2 + 1 ):
7371 self .lap [i , j ] = - kx [i ] ** 2 - ky [j ] ** 2
7472
75- # TODO: cleanup and move to real-valued FFT
76- fft_in = pyfftw .empty_aligned (self .init , dtype = 'complex128' )
77- fft_out = pyfftw .empty_aligned (self .init , dtype = 'complex128' )
78- ifft_in = pyfftw .empty_aligned (self .init , dtype = 'complex128' )
79- ifft_out = pyfftw .empty_aligned (self .init , dtype = 'complex128' )
80- self .fft_object = pyfftw .FFTW (fft_in , fft_out , direction = 'FFTW_FORWARD' , axes = (0 , 1 ))
81- self .ifft_object = pyfftw .FFTW (ifft_in , ifft_out , direction = 'FFTW_BACKWARD' , axes = (0 , 1 ))
73+ rfft_in = pyfftw .empty_aligned (self .init , dtype = 'float64' )
74+ fft_out = pyfftw .empty_aligned ((self .init [0 ], self .init [1 ] // 2 + 1 ), dtype = 'complex128' )
75+ ifft_in = pyfftw .empty_aligned ((self .init [0 ], self .init [1 ] // 2 + 1 ), dtype = 'complex128' )
76+ irfft_out = pyfftw .empty_aligned (self .init , dtype = 'float64' )
77+ self .rfft_object = pyfftw .FFTW (rfft_in , fft_out , direction = 'FFTW_FORWARD' , axes = (0 , 1 ))
78+ self .irfft_object = pyfftw .FFTW (ifft_in , irfft_out , direction = 'FFTW_BACKWARD' , axes = (0 , 1 ))
8279
8380 def eval_f (self , u , t ):
8481 """
@@ -94,8 +91,8 @@ def eval_f(self, u, t):
9491
9592 f = self .dtype_f (self .init )
9693 v = u .values .flatten ()
97- tmp = self .lap * self .fft_object (u .values )
98- f .impl .values [:] = np .real (self .ifft_object (tmp ))
94+ tmp = self .lap * self .rfft_object (u .values )
95+ f .impl .values [:] = np .real (self .irfft_object (tmp ))
9996 if self .params .eps > 0 :
10097 f .expl .values = 1.0 / self .params .eps ** 2 * v * (1.0 - v ** self .params .nu )
10198 f .expl .values = f .expl .values .reshape (self .params .nvars )
@@ -117,8 +114,8 @@ def solve_system(self, rhs, factor, u0, t):
117114
118115 me = self .dtype_u (self .init )
119116
120- tmp = self .fft_object (rhs .values ) / (1.0 - factor * self .lap )
121- me .values [:] = np .real (self .ifft_object (tmp ))
117+ tmp = self .rfft_object (rhs .values ) / (1.0 - factor * self .lap )
118+ me .values [:] = np .real (self .irfft_object (tmp ))
122119
123120 return me
124121
@@ -149,3 +146,85 @@ def u_exact(self, t):
149146 raise NotImplementedError ('type of initial value not implemented, got %s' % self .params .init_type )
150147
151148 return me
149+
150+
151+ class allencahn2d_imex_stab (allencahn2d_imex ):
152+ """
153+ Example implementing Allen-Cahn equation in 2D using FFTs for solving linear parts, IMEX time-stepping with
154+ stabilized splitting
155+
156+ Attributes:
157+ xvalues: grid points in space
158+ dx: mesh width
159+ lap: spectral operator for Laplacian
160+ rfft_object: planned real FFT for forward transformation
161+ irfft_object: planned IFFT for backward transformation
162+ """
163+
164+ def __init__ (self , problem_params , dtype_u = mesh , dtype_f = rhs_imex_mesh ):
165+ """
166+ Initialization routine
167+
168+ Args:
169+ problem_params (dict): custom parameters for the example
170+ dtype_u: mesh data type (will be passed to parent class)
171+ dtype_f: mesh data type wuth implicit and explicit parts (will be passed to parent class)
172+ """
173+ super (allencahn2d_imex_stab , self ).__init__ (problem_params = problem_params , dtype_u = dtype_u , dtype_f = dtype_f )
174+
175+ kx = np .zeros (self .init [0 ])
176+ ky = np .zeros (self .init [1 ] // 2 + 1 )
177+ for i in range (0 , int (self .init [0 ] / 2 ) + 1 ):
178+ kx [i ] = 2 * np .pi / self .params .L * i
179+ for i in range (0 , int (self .init [1 ] // 2 ) + 1 ):
180+ ky [i ] = 2 * np .pi / self .params .L * i
181+ for i in range (int (self .init [0 ] / 2 ) + 1 , self .init [0 ]):
182+ kx [i ] = 2 * np .pi / self .params .L * (- self .init [0 ] + i )
183+
184+ self .lap = np .zeros ((self .init [0 ], self .init [1 ] // 2 + 1 ))
185+ for i in range (self .init [0 ]):
186+ for j in range (self .init [1 ] // 2 + 1 ):
187+ self .lap [i , j ] = - kx [i ] ** 2 - ky [j ] ** 2 - 2.0 / self .params .eps ** 2
188+
189+ def eval_f (self , u , t ):
190+ """
191+ Routine to evaluate the RHS
192+
193+ Args:
194+ u (dtype_u): current values
195+ t (float): current time
196+
197+ Returns:
198+ dtype_f: the RHS
199+ """
200+
201+ f = self .dtype_f (self .init )
202+ v = u .values .flatten ()
203+ tmp = self .lap * self .rfft_object (u .values )
204+ f .impl .values [:] = np .real (self .irfft_object (tmp ))
205+ if self .params .eps > 0 :
206+ f .expl .values = 1.0 / self .params .eps ** 2 * v * (1.0 - v ** self .params .nu ) + \
207+ 2.0 / self .params .eps ** 2 * v
208+ f .expl .values = f .expl .values .reshape (self .params .nvars )
209+ return f
210+
211+ def solve_system (self , rhs , factor , u0 , t ):
212+ """
213+ Simple FFT solver for the diffusion part
214+
215+ Args:
216+ rhs (dtype_f): right-hand side for the linear system
217+ factor (float) : abbrev. for the node-to-node stepsize (or any other factor required)
218+ u0 (dtype_u): initial guess for the iterative solver (not used here so far)
219+ t (float): current time (e.g. for time-dependent BCs)
220+
221+ Returns:
222+ dtype_u: solution as mesh
223+ """
224+
225+ me = self .dtype_u (self .init )
226+
227+ tmp = self .rfft_object (rhs .values ) / (1.0 - factor * self .lap )
228+ me .values [:] = np .real (self .irfft_object (tmp ))
229+
230+ return me
0 commit comments