Coverage for pySDC/implementations/problem_classes/Boussinesq_2D_FD_imex.py: 0%

66 statements  

« prev     ^ index     » next       coverage.py v7.6.7, created at 2024-11-16 14:51 +0000

1import numpy as np 

2from scipy.sparse.linalg import gmres 

3 

4from pySDC.core.problem import Problem 

5from pySDC.implementations.datatype_classes.mesh import mesh, imex_mesh 

6from pySDC.implementations.problem_classes.boussinesq_helpers.build2DFDMatrix import get2DMesh 

7from pySDC.implementations.problem_classes.boussinesq_helpers.buildBoussinesq2DMatrix import getBoussinesq2DMatrix 

8from pySDC.implementations.problem_classes.boussinesq_helpers.buildBoussinesq2DMatrix import getBoussinesq2DUpwindMatrix 

9from pySDC.implementations.problem_classes.boussinesq_helpers.helper_classes import Callback, logging 

10from pySDC.implementations.problem_classes.boussinesq_helpers.unflatten import unflatten 

11 

12 

13# noinspection PyUnusedLocal 

14class boussinesq_2d_imex(Problem): 

15 r""" 

16 This class implements the two-dimensional Boussinesq equations for different boundary conditions with 

17 

18 .. math:: 

19 \frac{\partial u}{\partial t} + U \frac{\partial u}{\partial x} + \frac{\partial p}{\partial x} = 0, 

20 

21 .. math:: 

22 \frac{\partial w}{\partial t} + U \frac{\partial w}{\partial x} + \frac{\partial p}{\partial z} = 0, 

23 

24 .. math:: 

25 \frac{\partial b}{\partial t} + U \frac{\partial b}{\partial x} + N^2 w = 0, 

26 

27 .. math:: 

28 \frac{\partial p}{\partial t} + U \frac{\partial p}{\partial x} + c^2 (\frac{\partial u}{\partial x} + \frac{\partial w}{\partial z}) = 0. 

29 

30 They can be derived from the linearized Euler equations by a transformation of variables [1]_. 

31 

32 Parameters 

33 ---------- 

34 nvars : list of tuple, optional 

35 List of number of unknowns nvars, e.g. ``nvars=[(4, 300, 3)]``. 

36 c_s : float, optional 

37 Acoustic velocity :math:`c_s`. 

38 u_adv : float, optional 

39 Advection speed :math:`U`. 

40 Nfreq : float, optional 

41 Stability frequency. 

42 x_bounds : list, optional 

43 Domain in x-direction. 

44 z_bounds : list, optional 

45 Domain in z-direction. 

46 order_upwind : int, optional 

47 Order of upwind scheme for discretization. 

48 order : int, optional 

49 Order for discretization. 

50 gmres_maxiter : int, optional 

51 Maximum number of iterations for GMRES solver. 

52 gmres_restart : int, optional 

53 Number of iterations between restarts in GMRES solver. 

54 gmres_tol_limit : float, optional 

55 Tolerance for GMRES solver to terminate. 

56 

57 Attributes 

58 ---------- 

59 N : list 

60 List of number of unknowns. 

61 bc_hor : list 

62 Contains type of boundary conditions for both boundaries for both dimensions. 

63 bc_ver : 

64 Contains type of boundary conditions for both boundaries for both dimemsions, e.g. ``'neumann'`` or ``'dirichlet'``. 

65 xx : np.ndarray 

66 List of np.ndarrays for mesh in x-direction. 

67 zz : np.ndarray 

68 List of np.ndarrays for mesh in z-direction. 

69 h : float 

70 Mesh size. 

71 Id : sp.sparse.eye 

72 Identity matrix for the equation of appropriate size. 

73 M : np.ndarray 

74 Boussinesq 2D Matrix. 

75 D_upwind : sp.csc_matrix 

76 Boussinesq 2D Upwind matrix for discretization. 

77 gmres_logger : object 

78 Logger for GMRES solver. 

79 

80 References 

81 ---------- 

82 .. [1] D. R. Durran. Numerical Methods for Fluid Dynamics. Texts Appl. Math. 32. Springer-Verlag, New York (2010) 

83 http://dx.doi.org/10.1007/978-1-4419-6412-0. 

84 """ 

85 

86 dtype_u = mesh 

87 dtype_f = imex_mesh 

88 

89 def __init__( 

90 self, 

91 nvars=None, 

92 c_s=0.3, 

93 u_adv=0.02, 

94 Nfreq=0.01, 

95 x_bounds=None, 

96 z_bounds=None, 

97 order_upw=5, 

98 order=4, 

99 gmres_maxiter=500, 

100 gmres_restart=10, 

101 gmres_tol_limit=1e-5, 

102 ): 

103 """Initialization routine""" 

104 

105 if nvars is None: 

106 nvars = [(4, 300, 30)] 

107 

108 if x_bounds is None: 

109 x_bounds = [(-150.0, 150.0)] 

110 

111 if z_bounds is None: 

112 z_bounds = [(0.0, 10.0)] 

113 

114 # invoke super init, passing number of dofs, dtype_u and dtype_f 

115 super().__init__(init=(nvars, None, np.dtype('float64'))) 

116 self._makeAttributeAndRegister( 

117 'nvars', 

118 'c_s', 

119 'u_adv', 

120 'Nfreq', 

121 'x_bounds', 

122 'z_bounds', 

123 'order_upw', 

124 'order', 

125 'gmres_maxiter', 

126 'gmres_restart', 

127 'gmres_tol_limit', 

128 localVars=locals(), 

129 readOnly=True, 

130 ) 

131 

132 self.N = [self.nvars[1], self.nvars[2]] 

133 

134 self.bc_hor = [ 

135 ['periodic', 'periodic'], 

136 ['periodic', 'periodic'], 

137 ['periodic', 'periodic'], 

138 ['periodic', 'periodic'], 

139 ] 

140 self.bc_ver = [ 

141 ['neumann', 'neumann'], 

142 ['dirichlet', 'dirichlet'], 

143 ['dirichlet', 'dirichlet'], 

144 ['neumann', 'neumann'], 

145 ] 

146 

147 self.xx, self.zz, self.h = get2DMesh(self.N, self.x_bounds, self.z_bounds, self.bc_hor[0], self.bc_ver[0]) 

148 

149 self.Id, self.M = getBoussinesq2DMatrix( 

150 self.N, self.h, self.bc_hor, self.bc_ver, self.c_s, self.Nfreq, self.order 

151 ) 

152 self.D_upwind = getBoussinesq2DUpwindMatrix(self.N, self.h[0], self.u_adv, self.order_upw) 

153 

154 self.gmres_logger = logging() 

155 

156 def solve_system(self, rhs, factor, u0, t): 

157 r""" 

158 Simple linear solver for :math:`(I - factor \cdot A) \vec{u} = \vec{rhs}` using GMRES. 

159 

160 Parameters 

161 ---------- 

162 rhs : dtype_f 

163 Right-hand side for the nonlinear system. 

164 factor : float 

165 Abbrev. for the node-to-node stepsize (or any other factor required). 

166 u0 : dtype_u 

167 Initial guess for the iterative solver (not used here so far). 

168 t : float 

169 Current time (e.g. for time-dependent BCs). 

170 

171 Returns 

172 ------- 

173 me : dtype_u 

174 The solution as mesh. 

175 """ 

176 

177 b = rhs.flatten() 

178 cb = Callback() 

179 

180 sol, info = gmres( 

181 self.Id - factor * self.M, 

182 b, 

183 x0=u0.flatten(), 

184 rtol=self.gmres_tol_limit, 

185 restart=self.gmres_restart, 

186 maxiter=self.gmres_maxiter, 

187 atol=0, 

188 callback=cb, 

189 ) 

190 # If this is a dummy call with factor==0.0, do not log because it should not be counted as a solver call 

191 if factor != 0.0: 

192 self.gmres_logger.add(cb.getcounter()) 

193 me = self.dtype_u(self.init) 

194 me[:] = unflatten(sol, 4, self.N[0], self.N[1]) 

195 

196 return me 

197 

198 def __eval_fexpl(self, u, t): 

199 """ 

200 Helper routine to evaluate the explicit part of the right-hand side. 

201 

202 Parameters 

203 ---------- 

204 u : dtype_u 

205 Current values of the numerical solution. 

206 t : float 

207 Current time at which the numerical solution is computed (not used here). 

208 

209 Returns 

210 ------- 

211 fexpl : dtype_u 

212 Explicit part of right-hand side. 

213 """ 

214 

215 # Evaluate right hand side 

216 fexpl = self.dtype_u(self.init) 

217 temp = u.flatten() 

218 temp = self.D_upwind.dot(temp) 

219 fexpl[:] = unflatten(temp, 4, self.N[0], self.N[1]) 

220 

221 return fexpl 

222 

223 def __eval_fimpl(self, u, t): 

224 """ 

225 Helper routine to evaluate the implicit part of the right-hand side. 

226 

227 Parameters 

228 ---------- 

229 u : dtype_u 

230 Current values of the numerical solution. 

231 t : float 

232 Current time at which the numerical solution is computed (not used here). 

233 

234 Returns 

235 ------- 

236 fexpl : dtype_u 

237 Implicit part of right-hand side. 

238 """ 

239 

240 temp = u.flatten() 

241 temp = self.M.dot(temp) 

242 fimpl = self.dtype_u(self.init) 

243 fimpl[:] = unflatten(temp, 4, self.N[0], self.N[1]) 

244 

245 return fimpl 

246 

247 def eval_f(self, u, t): 

248 """ 

249 Routine to evaluate both parts of the right-hand side. 

250 

251 Parameters 

252 ---------- 

253 u : dtype_u 

254 Current values of the numerical solution. 

255 t : float 

256 Current time the numerical solution is computed. 

257 

258 Returns 

259 ------- 

260 f : dtype_f 

261 Right-hand side divided into two parts. 

262 """ 

263 

264 f = self.dtype_f(self.init) 

265 f.impl[:] = self.__eval_fimpl(u, t) 

266 f.expl[:] = self.__eval_fexpl(u, t) 

267 return f 

268 

269 def u_exact(self, t): 

270 r""" 

271 Routine to compute the exact solution at time :math:`t`. 

272 

273 Parameters 

274 ---------- 

275 t : float 

276 Time of the exact solution. 

277 

278 Returns 

279 ------- 

280 me : dtype_u 

281 The exact solution. 

282 """ 

283 assert t == 0, 'ERROR: u_exact only valid for t=0' 

284 

285 dtheta = 0.01 

286 H = 10.0 

287 a = 5.0 

288 x_c = -50.0 

289 

290 me = self.dtype_u(self.init) 

291 me[0, :, :] = 0.0 * self.xx 

292 me[1, :, :] = 0.0 * self.xx 

293 # me[2,:,:] = 0.0*self.xx 

294 # me[3,:,:] = np.exp(-0.5*(self.xx-0.0)**2.0/0.15**2.0)*np.exp(-0.5*(self.zz-0.5)**2/0.15**2) 

295 # me[2,:,:] = np.exp(-0.5*(self.xx-0.0)**2.0/0.05**2.0)*np.exp(-0.5*(self.zz-0.5)**2/0.2**2) 

296 me[2, :, :] = dtheta * np.sin(np.pi * self.zz / H) / (1.0 + np.square(self.xx - x_c) / (a * a)) 

297 me[3, :, :] = 0.0 * self.xx 

298 return me