Coverage for pySDC/implementations/problem_classes/AdvectionDiffusionEquation_1D_FFT.py: 100%
63 statements
« prev ^ index » next coverage.py v7.17.0, created at 2026-11-02 12:30 +0000
« prev ^ index » next coverage.py v7.17.0, created at 2026-11-02 12:30 +0000
1import numpy as np
3from pySDC.core.errors import ParameterError, ProblemError
4from pySDC.core.problem import Problem, WorkCounter
5from pySDC.implementations.datatype_classes.mesh import mesh, imex_mesh
8# noinspection PyUnusedLocal
9class advectiondiffusion1d_imex(Problem):
10 r"""
11 Periodic 1D advection-diffusion equation with FFTs, IMEX with diffusion implicit and advection explicit.
13 Example implementing the unforced one-dimensional advection diffusion equation
15 .. math::
16 \frac{\partial u(x,t)}{\partial t} = - c \frac{\partial u(x,t)}{\partial x} + \nu \frac{\partial^2 u(x,t)}{\partial x^2}
18 with periodic boundary conditions in :math:`[-\frac{L}{2}, \frac{L}{2}]` in spectral space. The advection part
19 :math:`- c \frac{\partial u(x,t)}{\partial x}` is treated explicitly, whereas the diffusion part
20 :math:`\nu \frac{\partial^2 u(x,t)}{\partial x^2}` will be treated numerically in an implicit way. The exact solution is
21 given by
23 .. math::
24 u(x, t) = \sin(\omega (x - c t)) \exp(-t \nu \omega^2)
26 for :math:`\omega=2 \pi k`, where :math:`k` denotes the wave number. Fast Fourier transform is used for the spatial
27 discretization.
29 Parameters
30 ----------
31 nvars : int, optional
32 Number of points in spatial discretization.
33 c : float, optional
34 Advection speed :math:`c`.
35 freq : int, optional
36 Wave number :math:`k`.
37 nu : float, optional
38 Diffusion coefficient :math:`\nu`.
39 L : float, optional
40 Denotes the period of the function to be approximated for the Fourier transform.
42 Attributes
43 ----------
44 xvalues : np.1darray
45 Contains the grid points in space.
46 ddx : np.1darray
47 Spectral operator for gradient.
48 lap : np.1darray
49 Spectral operator for Laplacian.
50 """
52 dtype_u = mesh
53 dtype_f = imex_mesh
55 def __init__(self, nvars=256, c=1.0, freq=-1, nu=0.02, L=1.0):
56 """Initialization routine"""
58 # invoke super init, passing number of dofs, dtype_u and dtype_f
59 super().__init__(init=(nvars, None, np.dtype('float64')))
60 self._makeAttributeAndRegister('nvars', 'c', 'freq', 'nu', 'L', localVars=locals(), readOnly=True)
62 # we assert that nvars looks very particular here.. this will be necessary for coarsening in space later on
63 if (self.nvars) % 2 != 0:
64 raise ProblemError('setup requires nvars = 2^p')
66 self.xvalues = np.array([i * self.L / self.nvars - self.L / 2.0 for i in range(self.nvars)])
68 kx = np.zeros(self.init[0] // 2 + 1)
69 for i in range(0, len(kx)):
70 kx[i] = 2 * np.pi / self.L * i
72 self.ddx = kx * 1j
73 self.lap = -(kx**2)
75 self.work_counters['rhs'] = WorkCounter()
77 def eval_f(self, u, t):
78 """
79 Routine to evaluate the right-hand side of the problem.
81 Parameters
82 ----------
83 u : dtype_u
84 Current values of the numerical solution.
85 t : float
86 Current time at which the numerical solution is computed.
88 Returns
89 -------
90 f : dtype_f
91 The right-hand side of the problem.
92 """
94 f = self.dtype_f(self.init)
95 tmp_u = np.fft.rfft(u)
96 tmp_impl = self.nu * self.lap * tmp_u
97 tmp_expl = -self.c * self.ddx * tmp_u
98 f.impl[:] = np.fft.irfft(tmp_impl)
99 f.expl[:] = np.fft.irfft(tmp_expl)
101 self.work_counters['rhs']()
102 return f
104 def solve_system(self, rhs, factor, u0, t):
105 """
106 Simple FFT solver for the diffusion part.
108 Parameters
109 ----------
110 rhs : dtype_f
111 Right-hand side for the linear system.
112 factor : float
113 Abbrev. for the node-to-node stepsize (or any other factor required).
114 u0 : dtype_u
115 Initial guess for the iterative solver (not used here so far).
116 t : float
117 Current time (e.g. for time-dependent BCs).
119 Returns
120 -------
121 me : dtype_u
122 The solution as mesh.
123 """
125 me = self.dtype_u(self.init)
126 tmp = np.fft.rfft(rhs) / (1.0 - self.nu * factor * self.lap)
127 me[:] = np.fft.irfft(tmp)
129 return me
131 def u_exact(self, t):
132 r"""
133 Routine to compute the exact solution at time :math:`t`.
135 Parameters
136 ----------
137 t : float
138 Time of the exact solution.
140 Returns
141 -------
142 me : dtype_u
143 The exact solution.
144 """
146 me = self.dtype_u(self.init, val=0.0)
147 if self.freq > 0:
148 omega = 2.0 * np.pi * self.freq
149 me[:] = np.sin(omega * (self.xvalues - self.c * t)) * np.exp(-t * self.nu * omega**2)
150 elif self.freq == 0:
151 np.random.seed(1)
152 me[:] = np.random.rand(self.nvars)
153 else:
154 t00 = 0.08
155 if self.nu > 0:
156 nbox = int(np.ceil(np.sqrt(4.0 * self.nu * (t00 + t) * 37.0 / (self.L**2))))
157 for k in range(-nbox, nbox + 1):
158 for i in range(self.init[0]):
159 x = self.xvalues[i] - self.c * t + k * self.L
160 me[i] += np.sqrt(t00) / np.sqrt(t00 + t) * np.exp(-(x**2) / (4.0 * self.nu * (t00 + t)))
161 else:
162 raise ParameterError("There is no exact solution implemented for negative frequency and negative nu!")
163 return me
166class advectiondiffusion1d_implicit(advectiondiffusion1d_imex):
167 r"""
168 Periodic 1D advection-diffusion equation with FFTs, fully implicit in advection and diffusion.
170 Example implementing the unforced one-dimensional advection diffusion equation
172 .. math::
173 \frac{\partial u(x,t)}{\partial t} = - c \frac{\partial u(x,t)}{\partial x} + \nu \frac{\partial^2 u(x,t)}{\partial x^2}
175 with periodic boundary conditions in :math:`[-\frac{L}{2}, \frac{L}{2}]` in spectral space. This class implements the
176 problem solving it with fully-implicit time-stepping. The exact solution is given by
178 .. math::
179 u(x, t) = \sin(\omega (x - c t)) \exp(-t \nu \omega^2)
181 for :math:`\omega=2 \pi k`, where :math:`k` denotes the wave number. Fast Fourier transform is used for the spatial
182 discretization.
184 Note
185 ----
186 This class has the same attributes as the class it inherits from.
187 """
189 dtype_u = mesh
190 dtype_f = mesh
192 def eval_f(self, u, t):
193 """
194 Routine to evaluate the right-hand side of the problem.
196 Parameters
197 ----------
198 u : dtype_u
199 Current values of the numerical solution.
200 t : float
201 Current time at which the numerical solution is computed.
203 Returns
204 -------
205 f : dtype_f
206 The right-hand side of the problem.
207 """
209 f = self.dtype_f(self.init)
210 tmp_u = np.fft.rfft(u)
211 tmp = self.nu * self.lap * tmp_u - self.c * self.ddx * tmp_u
212 f[:] = np.fft.irfft(tmp)
214 self.work_counters['rhs']()
215 return f
217 def solve_system(self, rhs, factor, u0, t):
218 """
219 Simple FFT solver for the diffusion and advection part (both are linear!).
221 Parameters
222 ----------
223 rhs : dtype_f
224 Right-hand side for the linear system.
225 factor : float
226 Abbrev. for the node-to-node stepsize (or any other factor required).
227 u0 : dtype_u
228 Initial guess for the iterative solver (not used here so far).
229 t : float
230 Current time (e.g. for time-dependent BCs).
232 Returns
233 -------
234 me : dtype_u
235 The solution as mesh.
236 """
238 me = self.dtype_u(self.init)
239 tmp = np.fft.rfft(rhs) / (1.0 - factor * (self.nu * self.lap - self.c * self.ddx))
240 me[:] = np.fft.irfft(tmp)
242 return me