Coverage for pySDC/implementations/problem_classes/Burgers.py: 95%
96 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
3from pySDC.implementations.datatype_classes.mesh import mesh, imex_mesh
4from pySDC.implementations.problem_classes.generic_spectral import GenericSpectralLinear
7class Burgers1D(GenericSpectralLinear):
8 """
9 See https://en.wikipedia.org/wiki/Burgers'_equation for the equation that is solved.
10 Discretization is done with a Chebychev method, which requires a first order derivative formulation.
11 Feel free to do a more efficient implementation using an ultraspherical method to avoid the first order business.
13 Parameters:
14 N (int): Spatial resolution
15 epsilon (float): viscosity
16 BCl (float): Value at left boundary
17 BCr (float): Value at right boundary
18 f (int): Frequency of the initial conditions
19 mode (str): 'T2U' or 'T2T'. Use 'T2U' to get sparse differentiation matrices
20 """
22 dtype_u = mesh
23 dtype_f = imex_mesh
25 def __init__(self, N=64, epsilon=0.1, BCl=1, BCr=-1, f=0, mode='T2U', **kwargs):
26 """
27 Constructor. `kwargs` are forwarded to parent class constructor.
29 Args:
30 N (int): Spatial resolution
31 epsilon (float): viscosity
32 BCl (float): Value at left boundary
33 BCr (float): Value at right boundary
34 f (int): Frequency of the initial conditions
35 mode (str): 'T2U' or 'T2T'. Use 'T2U' to get sparse differentiation matrices
36 """
37 self._makeAttributeAndRegister('N', 'epsilon', 'BCl', 'BCr', 'f', 'mode', localVars=locals(), readOnly=True)
39 bases = [{'base': 'cheby', 'N': N}]
40 components = ['u', 'ux']
42 super().__init__(bases=bases, components=components, spectral_space=False, **kwargs)
44 self.x = self.get_grid()[0]
46 # prepare matrices
47 Dx = self.get_differentiation_matrix(axes=(0,))
48 I = self.get_Id()
50 T2U = self.get_basis_change_matrix(conv=mode)
52 self.Dx = Dx
54 # construct linear operator
55 L_lhs = {'u': {'ux': -epsilon * (T2U @ Dx)}, 'ux': {'u': -T2U @ Dx, 'ux': T2U @ I}}
56 self.setup_L(L_lhs)
58 # construct mass matrix
59 M_lhs = {'u': {'u': T2U @ I}}
60 self.setup_M(M_lhs)
62 # boundary conditions
63 self.add_BC(component='u', equation='u', axis=0, x=1, v=BCr, kind='Dirichlet')
64 self.add_BC(component='u', equation='ux', axis=0, x=-1, v=BCl, kind='Dirichlet')
65 self.setup_BCs()
67 def u_exact(self, t=0, *args, **kwargs):
68 me = self.u_init
70 # x = (self.x + 1) / 2
71 # g = 4 * (1 + np.exp(-(4 * x + t)/self.epsilon/32))
72 # g_x = 4 * np.exp(-(4 * x + t)/self.epsilon/32) * (-4/self.epsilon/32)
74 # me[0] = 3./4. - 1./g
75 # me[1] = 1/g**2 * g_x
77 # return me
79 if t == 0:
80 me[self.index('u')][:] = ((self.BCr + self.BCl) / 2 + (self.BCr - self.BCl) / 2 * self.x) * np.cos(
81 self.x * np.pi * self.f
82 )
83 me[self.index('ux')][:] = (self.BCr - self.BCl) / 2 * np.cos(self.x * np.pi * self.f) + (
84 (self.BCr + self.BCl) / 2 + (self.BCr - self.BCl) / 2 * self.x
85 ) * self.f * np.pi * -np.sin(self.x * np.pi * self.f)
86 elif t == np.inf and self.f == 0 and self.BCl == -self.BCr:
87 me[0] = (self.BCl * np.exp((self.BCr - self.BCl) / (2 * self.epsilon) * self.x) + self.BCr) / (
88 np.exp((self.BCr - self.BCl) / (2 * self.epsilon) * self.x) + 1
89 )
90 else:
91 raise NotImplementedError
93 return me
95 def eval_f(self, u, *args, **kwargs):
96 f = self.f_init
97 iu, iux = self.index('u'), self.index('ux')
99 u_hat = self.transform(u)
101 Dx_u_hat = self.u_init_forward
102 Dx_u_hat[iux] = (self.Dx @ u_hat[iux].flatten()).reshape(u_hat[iu].shape)
104 f.impl[iu] = self.epsilon * self.itransform(Dx_u_hat)[iux].real
105 f.expl[iu] = -u[iu] * u[iux]
106 return f
108 def get_fig(self): # pragma: no cover
109 """
110 Get a figure suitable to plot the solution of this problem.
112 Returns
113 -------
114 self.fig : matplotlib.pyplot.figure.Figure
115 """
116 import matplotlib.pyplot as plt
118 plt.rcParams['figure.constrained_layout.use'] = True
119 self.fig, axs = plt.subplots()
120 return self.fig
122 def plot(self, u, t=None, fig=None, comp='u'): # pragma: no cover
123 r"""
124 Plot the solution.
126 Parameters
127 ----------
128 u : dtype_u
129 Solution to be plotted
130 t : float
131 Time to display at the top of the figure
132 fig : matplotlib.pyplot.figure.Figure, optional
133 Figure with the same structure as a figure generated by `self.get_fig`. If none is supplied, a new figure will be generated.
135 Returns
136 -------
137 None
138 """
139 fig = self.get_fig() if fig is None else fig
140 ax = fig.axes[0]
142 ax.plot(self.x, u[self.index(comp)])
144 if t is not None:
145 fig.suptitle(f't = {t:.2e}')
147 ax.set_xlabel(r'$x$')
148 ax.set_ylabel(r'$u$')
151class Burgers2D(GenericSpectralLinear):
152 """
153 See https://en.wikipedia.org/wiki/Burgers'_equation for the equation that is solved.
154 This implementation is discretized with FFTs in x and Chebychev in z.
156 Parameters:
157 nx (int): Spatial resolution in x direction
158 nz (int): Spatial resolution in z direction
159 epsilon (float): viscosity
160 BCl (float): Value at left boundary
161 BCr (float): Value at right boundary
162 fux (int): Frequency of the initial conditions in x-direction
163 fuz (int): Frequency of the initial conditions in z-direction
164 mode (str): 'T2U' or 'T2T'. Use 'T2U' to get sparse differentiation matrices
165 """
167 dtype_u = mesh
168 dtype_f = imex_mesh
170 def __init__(self, nx=64, nz=64, epsilon=0.1, fux=2, fuz=1, mode='T2U', **kwargs):
171 """
172 Constructor. `kwargs` are forwarded to parent class constructor.
174 Args:
175 nx (int): Spatial resolution in x direction
176 nz (int): Spatial resolution in z direction
177 epsilon (float): viscosity
178 fux (int): Frequency of the initial conditions in x-direction
179 fuz (int): Frequency of the initial conditions in z-direction
180 mode (str): 'T2U' or 'T2T'. Use 'T2U' to get sparse differentiation matrices
181 """
182 self._makeAttributeAndRegister('nx', 'nz', 'epsilon', 'fux', 'fuz', 'mode', localVars=locals(), readOnly=True)
184 bases = [
185 {'base': 'fft', 'N': nx},
186 {'base': 'cheby', 'N': nz},
187 ]
188 components = ['u', 'v', 'ux', 'uz', 'vx', 'vz']
189 super().__init__(bases=bases, components=components, spectral_space=False, **kwargs)
191 self.Z, self.X = self.get_grid()
193 # prepare matrices
194 Dx = self.get_differentiation_matrix(axes=(0,))
195 Dz = self.get_differentiation_matrix(axes=(1,))
196 I = self.get_Id()
198 T2U = self.get_basis_change_matrix(axes=(1,), conv=mode)
200 self.Dx = Dx
201 self.Dz = Dz
203 # construct linear operator
204 L_lhs = {
205 'u': {'ux': -epsilon * T2U @ Dx, 'uz': -epsilon * T2U @ Dz},
206 'v': {'vx': -epsilon * T2U @ Dx, 'vz': -epsilon * T2U @ Dz},
207 'ux': {'u': -T2U @ Dx, 'ux': T2U @ I},
208 'uz': {'u': -T2U @ Dz, 'uz': T2U @ I},
209 'vx': {'v': -T2U @ Dx, 'vx': T2U @ I},
210 'vz': {'v': -T2U @ Dz, 'vz': T2U @ I},
211 }
212 self.setup_L(L_lhs)
214 # construct mass matrix
215 M_lhs = {
216 'u': {'u': T2U @ I},
217 'v': {'v': T2U @ I},
218 }
219 self.setup_M(M_lhs)
221 # boundary conditions
222 self.BCtop = 1
223 self.BCbottom = -self.BCtop
224 self.BCtopu = 0
225 self.add_BC(component='v', equation='v', axis=1, v=self.BCtop, x=1, kind='Dirichlet')
226 self.add_BC(component='v', equation='vz', axis=1, v=self.BCbottom, x=-1, kind='Dirichlet')
227 self.add_BC(component='u', equation='uz', axis=1, v=self.BCtopu, x=1, kind='Dirichlet')
228 self.add_BC(component='u', equation='u', axis=1, v=self.BCtopu, x=-1, kind='Dirichlet')
229 self.setup_BCs()
231 def u_exact(self, t=0, *args, noise_level=0, **kwargs):
232 me = self.u_init
234 iu, iv, iux, iuz, ivx, ivz = self.index(self.components)
235 if t == 0:
236 me[iu] = self.xp.cos(self.X * self.fux) * self.xp.sin(self.Z * np.pi * self.fuz) + self.BCtopu
237 me[iux] = -self.xp.sin(self.X * self.fux) * self.fux * self.xp.sin(self.Z * np.pi * self.fuz)
238 me[iuz] = self.xp.cos(self.X * self.fux) * self.xp.cos(self.Z * np.pi * self.fuz) * np.pi * self.fuz
240 me[iv] = (self.BCtop + self.BCbottom) / 2 + (self.BCtop - self.BCbottom) / 2 * self.Z
241 me[ivz][:] = (self.BCtop - self.BCbottom) / 2
243 # add noise
244 rng = self.xp.random.default_rng(seed=99)
245 me[iv].real += rng.normal(size=me[iv].shape) * (self.Z - 1) * (self.Z + 1) * noise_level
247 else:
248 raise NotImplementedError
250 return me
252 def eval_f(self, u, *args, **kwargs):
253 f = self.f_init
254 iu, iv, iux, iuz, ivx, ivz = self.index(self.components)
256 u_hat = self.transform(u)
257 f_hat = self.u_init_forward
258 f_hat[iu] = self.epsilon * ((self.Dx @ u_hat[iux].flatten() + self.Dz @ u_hat[iuz].flatten())).reshape(
259 u_hat[iux].shape
260 )
261 f_hat[iv] = self.epsilon * ((self.Dx @ u_hat[ivx].flatten() + self.Dz @ u_hat[ivz].flatten())).reshape(
262 u_hat[iux].shape
263 )
264 f.impl[...] = self.itransform(f_hat).real
266 f.expl[iu] = -(u[iu] * u[iux] + u[iv] * u[iuz])
267 f.expl[iv] = -(u[iu] * u[ivx] + u[iv] * u[ivz])
268 return f
270 def compute_vorticity(self, u):
271 me = self.u_init_forward
273 u_hat = self.transform(u)
274 iu, iv = self.index(['u', 'v'])
276 me[iu] = (self.Dx @ u_hat[iv].flatten() + self.Dz @ u_hat[iu].flatten()).reshape(u[iu].shape)
277 return self.itransform(me)[iu].real
279 def get_fig(self): # pragma: no cover
280 """
281 Get a figure suitable to plot the solution of this problem
283 Returns
284 -------
285 self.fig : matplotlib.pyplot.figure.Figure
286 """
287 import matplotlib.pyplot as plt
288 from mpl_toolkits.axes_grid1 import make_axes_locatable
290 plt.rcParams['figure.constrained_layout.use'] = True
291 self.fig, axs = plt.subplots(3, 1, sharex=True, sharey=True, figsize=((8, 7)))
292 self.cax = []
293 divider = make_axes_locatable(axs[0])
294 self.cax += [divider.append_axes('right', size='3%', pad=0.03)]
295 divider2 = make_axes_locatable(axs[1])
296 self.cax += [divider2.append_axes('right', size='3%', pad=0.03)]
297 divider3 = make_axes_locatable(axs[2])
298 self.cax += [divider3.append_axes('right', size='3%', pad=0.03)]
299 return self.fig
301 def plot(self, u, t=None, fig=None, vmin=None, vmax=None): # pragma: no cover
302 r"""
303 Plot the solution. Please supply a figure with the same structure as returned by ``self.get_fig``.
305 Parameters
306 ----------
307 u : dtype_u
308 Solution to be plotted
309 t : float
310 Time to display at the top of the figure
311 fig : matplotlib.pyplot.figure.Figure
312 Figure with the correct structure
314 Returns
315 -------
316 None
317 """
318 fig = self.get_fig() if fig is None else fig
319 axs = fig.axes
321 iu, iv = self.index(['u', 'v'])
323 imU = axs[0].pcolormesh(self.X, self.Z, u[iu].real, vmin=vmin, vmax=vmax)
324 imV = axs[1].pcolormesh(self.X, self.Z, u[iv].real, vmin=vmin, vmax=vmax)
325 imVort = axs[2].pcolormesh(self.X, self.Z, self.compute_vorticity(u).real)
327 for i, label in zip([0, 1, 2], [r'$u$', '$v$', 'vorticity']):
328 axs[i].set_aspect(1)
329 axs[i].set_title(label)
331 if t is not None:
332 fig.suptitle(f't = {t:.2e}')
333 axs[-1].set_xlabel(r'$x$')
334 axs[-1].set_ylabel(r'$z$')
335 fig.colorbar(imU, self.cax[0])
336 fig.colorbar(imV, self.cax[1])
337 fig.colorbar(imVort, self.cax[2])