Coverage for pySDC/implementations/problem_classes/HeatEquation_Chebychev.py: 100%
220 statements
« prev ^ index » next coverage.py v7.6.9, created at 2024-12-20 14:51 +0000
« prev ^ index » next coverage.py v7.6.9, created at 2024-12-20 14:51 +0000
1import numpy as np
2from scipy import sparse as sp
4from pySDC.core.problem import Problem
5from pySDC.implementations.datatype_classes.mesh import mesh
6from pySDC.implementations.problem_classes.generic_spectral import GenericSpectralLinear
9class Heat1DChebychev(GenericSpectralLinear):
10 """
11 1D Heat equation with Dirichlet Boundary conditions discretized on (-1, 1) using a Chebychev spectral method.
12 """
14 dtype_u = mesh
15 dtype_f = mesh
17 def __init__(self, nvars=128, a=0, b=0, f=1, nu=1.0, mode='T2U', **kwargs):
18 """
19 Constructor. `kwargs` are forwarded to parent class constructor.
21 Args:
22 nvars (int): Resolution
23 a (float): Left BC value
24 b (float): Right BC value
25 f (int): Frequency of the solution
26 nu (float): Diffusion parameter
27 mode ('T2T' or 'T2U'): Mode for Chebychev method.
29 """
30 self._makeAttributeAndRegister('nvars', 'a', 'b', 'f', 'nu', 'mode', localVars=locals(), readOnly=True)
32 bases = [{'base': 'chebychev', 'N': nvars}]
33 components = ['u', 'ux']
35 super().__init__(bases, components, real_spectral_coefficients=True, **kwargs)
37 self.x = self.get_grid()[0]
39 I = self.get_Id()
40 Dx = self.get_differentiation_matrix(axes=(0,))
41 self.Dx = Dx
43 self.T2U = self.get_basis_change_matrix(conv=mode)
45 L_lhs = {
46 'ux': {'u': -self.T2U @ Dx, 'ux': self.T2U @ I},
47 'u': {'ux': -nu * (self.T2U @ Dx)},
48 }
49 self.setup_L(L_lhs)
51 M_lhs = {'u': {'u': self.T2U @ I}}
52 self.setup_M(M_lhs)
54 self.add_BC(component='u', equation='u', axis=0, x=-1, v=a, kind="Dirichlet")
55 self.add_BC(component='u', equation='ux', axis=0, x=1, v=b, kind="Dirichlet")
56 self.setup_BCs()
58 def eval_f(self, u, *args, **kwargs):
59 f = self.f_init
60 iu, iux = self.index(self.components)
62 if self.spectral_space:
63 u_hat = u.copy()
64 else:
65 u_hat = self.transform(u)
67 u_hat[iu] = (self.nu * self.Dx @ u_hat[iux].flatten()).reshape(u_hat[iu].shape)
69 if self.spectral_space:
70 me = u_hat
71 else:
72 me = self.itransform(u_hat).real
74 f[iu] = me[iu]
75 return f
77 def u_exact(self, t, noise=0, seed=666):
78 """
79 Get exact solution at time `t`
81 Args:
82 t (float): When you want the exact solution
83 noise (float): Add noise of this level
84 seed (int): Random seed for the noise
86 Returns:
87 Heat1DChebychev.dtype_u: Exact solution
88 """
89 xp = self.xp
90 iu, iux = self.index(self.components)
91 u = self.spectral.u_init
93 u[iu] = (
94 xp.sin(self.f * np.pi * self.x) * xp.exp(-self.nu * (self.f * np.pi) ** 2 * t)
95 + (self.b - self.a) / 2 * self.x
96 + (self.b + self.a) / 2
97 )
98 u[iux] = (
99 self.f * np.pi * xp.cos(self.f * np.pi * self.x) * xp.exp(-self.nu * (self.f * np.pi) ** 2 * t)
100 + (self.b - self.a) / 2
101 )
103 if noise > 0:
104 assert t == 0
105 _noise = self.u_init
106 rng = self.xp.random.default_rng(seed=seed)
107 _noise[iu] = rng.normal(size=u[iu].shape)
108 noise_hat = self.transform(_noise)
109 low_pass = self.get_filter_matrix(axis=0, kmax=self.nvars - 2)
110 noise_hat[iu] = (low_pass @ noise_hat[iu].flatten()).reshape(noise_hat[iu].shape)
111 _noise[:] = self.itransform(noise_hat)
112 u += _noise * noise * (self.x - 1) * (self.x + 1)
114 self.check_BCs(u)
116 if self.spectral_space:
117 u_hat = self.u_init
118 u_hat[...] = self.transform(u)
119 return u_hat
120 else:
121 return u
124class Heat1DUltraspherical(GenericSpectralLinear):
125 """
126 1D Heat equation with Dirichlet Boundary conditions discretized on (-1, 1) using an ultraspherical spectral method.
127 """
129 dtype_u = mesh
130 dtype_f = mesh
132 def __init__(self, nvars=128, a=0, b=0, f=1, nu=1.0, **kwargs):
133 """
134 Constructor. `kwargs` are forwarded to parent class constructor.
136 Args:
137 nvars (int): Resolution
138 a (float): Left BC value
139 b (float): Right BC value
140 f (int): Frequency of the solution
141 nu (float): Diffusion parameter
142 """
143 self._makeAttributeAndRegister('nvars', 'a', 'b', 'f', 'nu', localVars=locals(), readOnly=True)
145 bases = [{'base': 'ultraspherical', 'N': nvars}]
146 components = ['u']
148 GenericSpectralLinear.__init__(self, bases, components, real_spectral_coefficients=True, **kwargs)
150 self.x = self.get_grid()[0]
152 I = self.get_Id()
153 Dxx = self.get_differentiation_matrix(axes=(0,), p=2)
155 S2 = self.get_basis_change_matrix(p_in=2, p_out=0)
156 U2 = self.get_basis_change_matrix(p_in=0, p_out=2)
158 self.Dxx = S2 @ Dxx
160 L_lhs = {
161 'u': {'u': -nu * Dxx},
162 }
163 self.setup_L(L_lhs)
165 M_lhs = {'u': {'u': U2 @ I}}
166 self.setup_M(M_lhs)
168 self.add_BC(component='u', equation='u', axis=0, x=-1, v=a, kind="Dirichlet", line=-1)
169 self.add_BC(component='u', equation='u', axis=0, x=1, v=b, kind="Dirichlet", line=-2)
170 self.setup_BCs()
172 def eval_f(self, u, *args, **kwargs):
173 f = self.f_init
174 iu = self.index('u')
176 if self.spectral_space:
177 u_hat = u.copy()
178 else:
179 u_hat = self.transform(u)
181 u_hat[iu] = (self.nu * (self.Dxx @ u_hat[iu].flatten())).reshape(u_hat[iu].shape)
183 if self.spectral_space:
184 me = u_hat
185 else:
186 me = self.itransform(u_hat).real
188 f[iu][...] = me[iu]
189 return f
191 def u_exact(self, t, noise=0, seed=666):
192 """
193 Get exact solution at time `t`
195 Args:
196 t (float): When you want the exact solution
197 noise (float): Add noise of this level
198 seed (int): Random seed for the noise
200 Returns:
201 Heat1DUltraspherical.dtype_u: Exact solution
202 """
203 xp = self.xp
204 iu = self.index('u')
205 u = self.spectral.u_init
207 u[iu] = (
208 xp.sin(self.f * np.pi * self.x) * xp.exp(-self.nu * (self.f * np.pi) ** 2 * t)
209 + (self.b - self.a) / 2 * self.x
210 + (self.b + self.a) / 2
211 )
213 if noise > 0:
214 assert t == 0
215 _noise = self.u_init
216 rng = self.xp.random.default_rng(seed=seed)
217 _noise[iu] = rng.normal(size=u[iu].shape)
218 noise_hat = self.transform(_noise)
219 low_pass = self.get_filter_matrix(axis=0, kmax=self.nvars - 2)
220 noise_hat[iu] = (low_pass @ noise_hat[iu].flatten()).reshape(noise_hat[iu].shape)
221 _noise[:] = self.itransform(noise_hat)
222 u += _noise * noise * (self.x - 1) * (self.x + 1)
224 self.check_BCs(u)
226 if self.spectral_space:
227 return self.transform(u)
228 else:
229 return u
232class Heat2DChebychev(GenericSpectralLinear):
233 """
234 2D Heat equation with Dirichlet Boundary conditions discretized on (-1, 1)x(-1,1) using spectral methods based on FFT and Chebychev.
235 """
237 dtype_u = mesh
238 dtype_f = mesh
240 def __init__(self, nx=128, ny=128, base_x='fft', base_y='chebychev', a=0, b=0, c=0, fx=1, fy=1, nu=1.0, **kwargs):
241 """
242 Constructor. `kwargs` are forwarded to parent class constructor.
244 Args:
245 nx (int): Resolution in x-direction
246 ny (int): Resolution in y-direction
247 base_x (str): Spectral base in x-direction
248 base_y (str): Spectral base in y-direction
249 a (float): BC at y=0 and x=-1
250 b (float): BC at y=0 and x=+1
251 c (float): BC at y=1 and x = -1
252 fx (int): Horizontal frequency of initial conditions
253 fy (int): Vertical frequency of initial conditions
254 nu (float): Diffusion parameter
255 """
256 assert nx % 2 == 1 or base_x == 'fft'
257 assert ny % 2 == 1 or base_y == 'fft'
258 self._makeAttributeAndRegister(
259 'nx', 'ny', 'base_x', 'base_y', 'a', 'b', 'c', 'fx', 'fy', 'nu', localVars=locals(), readOnly=True
260 )
262 bases = [{'base': base_x, 'N': nx}, {'base': base_y, 'N': ny}]
263 components = ['u', 'ux', 'uy']
265 super().__init__(bases, components, Dirichlet_recombination=False, spectral_space=False, **kwargs)
267 self.Y, self.X = self.get_grid()
269 I = self.get_Id()
270 self.Dx = self.get_differentiation_matrix(axes=(0,))
271 self.Dy = self.get_differentiation_matrix(axes=(1,))
273 L_lhs = {
274 'ux': {'u': -self.Dx, 'ux': I},
275 'uy': {'u': -self.Dy, 'uy': I},
276 'u': {'ux': -nu * self.Dx, 'uy': -nu * self.Dy},
277 }
278 self.setup_L(L_lhs)
280 M_lhs = {'u': {'u': I}}
281 self.setup_M(M_lhs)
283 for base in [base_x, base_y]:
284 assert base in ['chebychev', 'fft']
286 alpha = (self.b - self.a) / 2.0
287 beta = (self.c - self.b) / 2.0
288 gamma = (self.c + self.a) / 2.0
290 if base_x == 'chebychev':
291 y = self.Y[0, :]
292 if self.base_y == 'fft':
293 self.add_BC(component='u', equation='u', axis=0, x=-1, v=beta * y - alpha + gamma, kind='Dirichlet')
294 self.add_BC(component='ux', equation='ux', axis=0, v=2 * alpha, kind='integral')
295 else:
296 assert a == b, f'Need periodic boundary conditions in x for {base_x} method!'
297 if base_y == 'chebychev':
298 x = self.X[:, 0]
299 self.add_BC(component='u', equation='u', axis=1, x=-1, v=alpha * x - beta + gamma, kind='Dirichlet')
300 self.add_BC(component='uy', equation='uy', axis=1, v=2 * beta, kind='integral')
301 else:
302 assert c == b, f'Need periodic boundary conditions in y for {base_y} method!'
303 self.setup_BCs()
305 def eval_f(self, u, *args, **kwargs):
306 f = self.f_init
307 iu, iux, iuy = self.index(self.components)
309 me_hat = self.u_init_forward
310 me_hat[:] = self.transform(u)
311 me_hat[iu] = self.nu * (self.Dx @ me_hat[iux].flatten() + self.Dy @ me_hat[iuy].flatten()).reshape(
312 me_hat[iu].shape
313 )
314 me = self.itransform(me_hat)
316 f[self.index("u")] = me[iu].real
317 return f
319 def u_exact(self, t):
320 xp = self.xp
321 iu, iux, iuy = self.index(self.components)
322 u = self.u_init
324 fx = self.fx if self.base_x == 'fft' else np.pi * self.fx
325 fy = self.fy if self.base_y == 'fft' else np.pi * self.fy
327 time_dep = xp.exp(-self.nu * (fx**2 + fy**2) * t)
329 alpha = (self.b - self.a) / 2.0
330 beta = (self.c - self.b) / 2.0
331 gamma = (self.c + self.a) / 2.0
333 u[iu] = xp.sin(fx * self.X) * xp.sin(fy * self.Y) * time_dep + alpha * self.X + beta * self.Y + gamma
334 u[iux] = fx * xp.cos(fx * self.X) * xp.sin(fy * self.Y) * time_dep + alpha
335 u[iuy] = fy * xp.sin(fx * self.X) * xp.cos(fy * self.Y) * time_dep + beta
337 return u
340class Heat2DUltraspherical(GenericSpectralLinear):
341 """
342 2D Heat equation with Dirichlet Boundary conditions discretized on (-1, 1)x(-1,1) using spectral methods based on FFT and Gegenbauer.
343 """
345 dtype_u = mesh
346 dtype_f = mesh
348 def __init__(
349 self, nx=128, ny=128, base_x='fft', base_y='ultraspherical', a=0, b=0, c=0, fx=1, fy=1, nu=1.0, **kwargs
350 ):
351 """
352 Constructor. `kwargs` are forwarded to parent class constructor.
354 Args:
355 nx (int): Resolution in x-direction
356 ny (int): Resolution in y-direction
357 base_x (str): Spectral base in x-direction
358 base_y (str): Spectral base in y-direction
359 a (float): BC at y=0 and x=-1
360 b (float): BC at y=0 and x=+1
361 c (float): BC at y=1 and x = -1
362 fx (int): Horizontal frequency of initial conditions
363 fy (int): Vertical frequency of initial conditions
364 nu (float): Diffusion parameter
365 """
366 self._makeAttributeAndRegister(
367 'nx', 'ny', 'base_x', 'base_y', 'a', 'b', 'c', 'fx', 'fy', 'nu', localVars=locals(), readOnly=True
368 )
370 bases = [{'base': base_x, 'N': nx}, {'base': base_y, 'N': ny}]
371 components = ['u']
373 super().__init__(bases, components, Dirichlet_recombination=False, spectral_space=False, **kwargs)
375 self.Y, self.X = self.get_grid()
377 self.Dxx = self.get_differentiation_matrix(axes=(0,), p=2)
378 self.Dyy = self.get_differentiation_matrix(axes=(1,), p=2)
379 self.S2 = self.get_basis_change_matrix(p=2)
380 I = self.get_Id()
382 L_lhs = {
383 'u': {'u': -nu * self.Dxx - nu * self.Dyy},
384 }
385 self.setup_L(L_lhs)
387 M_lhs = {'u': {'u': I}}
388 self.setup_M(M_lhs)
390 for base in [base_x, base_y]:
391 assert base in ['ultraspherical', 'fft']
393 alpha = (self.b - self.a) / 2.0
394 beta = (self.c - self.b) / 2.0
395 gamma = (self.c + self.a) / 2.0
397 if base_x == 'ultraspherical':
398 y = self.Y[0, :]
399 if self.base_y == 'fft':
400 self.add_BC(component='u', equation='u', axis=0, x=-1, v=beta * y - alpha + gamma, kind='Dirichlet')
401 self.add_BC(component='u', equation='u', axis=0, v=beta * y + alpha + gamma, x=1, line=-2, kind='Dirichlet')
402 else:
403 assert a == b, f'Need periodic boundary conditions in x for {base_x} method!'
404 if base_y == 'ultraspherical':
405 x = self.X[:, 0]
406 self.add_BC(
407 component='u', equation='u', axis=1, x=-1, v=alpha * x - beta + gamma, kind='Dirichlet', line=-1
408 )
409 self.add_BC(
410 component='u', equation='u', axis=1, x=+1, v=alpha * x + beta + gamma, kind='Dirichlet', line=-2
411 )
412 else:
413 assert c == b, f'Need periodic boundary conditions in y for {base_y} method!'
414 self.setup_BCs()
416 def eval_f(self, u, *args, **kwargs):
417 f = self.f_init
418 iu = self.index('u')
420 me_hat = self.u_init_forward
421 me_hat[:] = self.transform(u)
422 me_hat[iu] = self.nu * (self.S2 @ (self.Dxx + self.Dyy) @ me_hat[iu].flatten()).reshape(me_hat[iu].shape)
423 me = self.itransform(me_hat)
425 f[iu] = me[iu].real
426 return f
428 def u_exact(self, t):
429 xp = self.xp
430 iu = self.index('u')
431 u = self.u_init
433 fx = self.fx if self.base_x == 'fft' else np.pi * self.fx
434 fy = self.fy if self.base_y == 'fft' else np.pi * self.fy
436 time_dep = xp.exp(-self.nu * (fx**2 + fy**2) * t)
438 alpha = (self.b - self.a) / 2.0
439 beta = (self.c - self.b) / 2.0
440 gamma = (self.c + self.a) / 2.0
442 u[iu] = xp.sin(fx * self.X) * xp.sin(fy * self.Y) * time_dep + alpha * self.X + beta * self.Y + gamma
444 return u