Coverage for pySDC/implementations/problem_classes/TestEquation_0D.py: 67%

104 statements  

« prev     ^ index     » next       coverage.py v7.8.0, created at 2025-04-18 08:18 +0000

1import numpy as np 

2import scipy.sparse as nsp 

3 

4from pySDC.core.problem import Problem, WorkCounter 

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

6from pySDC.helpers.fieldsIO import Scalar 

7 

8 

9class testequation0d(Problem): 

10 r""" 

11 This class implements the simple test equation of the form 

12 

13 .. math:: 

14 \frac{d u(t)}{dt} = A u(t) 

15 

16 for :math:`A = diag(\lambda_1, .. ,\lambda_n)`. 

17 

18 Parameters 

19 ---------- 

20 lambdas : sequence of array_like, optional 

21 List of lambda parameters. 

22 u0 : sequence of array_like, optional 

23 Initial condition. 

24 

25 Attributes 

26 ---------- 

27 A : scipy.sparse.csc_matrix 

28 Diagonal matrix containing :math:`\lambda_1,..,\lambda_n`. 

29 """ 

30 

31 xp = np 

32 xsp = nsp 

33 dtype_u = mesh 

34 dtype_f = mesh 

35 

36 @classmethod 

37 def setup_GPU(cls): 

38 """ 

39 Switch to GPU modules 

40 """ 

41 from pySDC.implementations.datatype_classes.cupy_mesh import cupy_mesh 

42 import cupy as cp 

43 import cupyx.scipy.sparse as csp 

44 

45 cls.xp = cp 

46 cls.xsp = csp 

47 cls.dtype_u = cupy_mesh 

48 cls.dtype_f = cupy_mesh 

49 

50 def __init__(self, lambdas=None, u0=0.0, useGPU=False): 

51 """Initialization routine""" 

52 if useGPU: 

53 self.setup_GPU() 

54 

55 if lambdas is None: 

56 re = self.xp.linspace(-30, 19, 50) 

57 im = self.xp.linspace(-50, 49, 50) 

58 lambdas = self.xp.array([[complex(re[i], im[j]) for i in range(len(re))] for j in range(len(im))]).reshape( 

59 (len(re) * len(im)) 

60 ) 

61 lambdas = self.xp.asarray(lambdas) 

62 assert lambdas.ndim == 1, f'expect flat list here, got {lambdas}' 

63 nvars = lambdas.size 

64 assert nvars > 0, 'expect at least one lambda parameter here' 

65 

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

67 super().__init__(init=(nvars, None, self.xp.dtype('complex128'))) 

68 

69 lambdas = self.xp.array(lambdas) 

70 self.A = self.xsp.diags(lambdas) 

71 self._makeAttributeAndRegister('nvars', 'lambdas', 'u0', 'useGPU', localVars=locals(), readOnly=True) 

72 self.work_counters['rhs'] = WorkCounter() 

73 

74 def eval_f(self, u, t): 

75 """ 

76 Routine to evaluate the right-hand side of the problem. 

77 

78 Parameters 

79 ---------- 

80 u : dtype_u 

81 Current values of the numerical solution. 

82 t : float 

83 Current time of the numerical solution is computed. 

84 

85 Returns 

86 ------- 

87 f : dtype_f 

88 The right-hand side of the problem. 

89 """ 

90 

91 f = self.dtype_f(self.init) 

92 f[:] = u 

93 f *= self.lambdas 

94 self.work_counters['rhs']() 

95 return f 

96 

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

98 r""" 

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

100 

101 Parameters 

102 ---------- 

103 rhs : dtype_f 

104 Right-hand side for the linear system. 

105 factor : float 

106 Abbrev. for the local stepsize (or any other factor required). 

107 u0 : dtype_u 

108 Initial guess for the iterative solver. 

109 t : float 

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

111 

112 Returns 

113 ------- 

114 me : dtype_u 

115 The solution as mesh. 

116 """ 

117 me = self.dtype_u(self.init) 

118 L = 1 - factor * self.lambdas 

119 L[L == 0] = 1 # to avoid potential divisions by zeros 

120 me[:] = rhs 

121 me /= L 

122 return me 

123 

124 def u_exact(self, t, u_init=None, t_init=None): 

125 """ 

126 Routine to compute the exact solution at time t. 

127 

128 Parameters 

129 ---------- 

130 t : float 

131 Time of the exact solution. 

132 u_init : pySDC.problem.testequation0d.dtype_u 

133 Initial solution. 

134 t_init : float 

135 The initial time. 

136 

137 Returns 

138 ------- 

139 me : dtype_u 

140 The exact solution. 

141 """ 

142 

143 u_init = (self.u0 if u_init is None else u_init) * 1.0 

144 t_init = 0.0 if t_init is None else t_init * 1.0 

145 

146 me = self.dtype_u(self.init) 

147 me[:] = u_init * self.xp.exp((t - t_init) * self.lambdas) 

148 return me 

149 

150 def getOutputFile(self, fileName): 

151 fOut = Scalar(np.complex128, fileName=fileName) 

152 fOut.setHeader(self.lambdas.size) 

153 fOut.initialize() 

154 return fOut 

155 

156 def processSolutionForOutput(self, u): 

157 return u.flatten() 

158 

159 

160class test_equation_IMEX(Problem): 

161 dtype_f = imex_mesh 

162 dtype_u = mesh 

163 xp = np 

164 xsp = nsp 

165 

166 def __init__(self, lambdas_implicit=None, lambdas_explicit=None, u0=0.0): 

167 """Initialization routine""" 

168 

169 if lambdas_implicit is None: 

170 re = self.xp.linspace(-30, 19, 50) 

171 im = self.xp.linspace(-50, 49, 50) 

172 lambdas_implicit = self.xp.array( 

173 [[complex(re[i], im[j]) for i in range(len(re))] for j in range(len(im))] 

174 ).reshape((len(re) * len(im))) 

175 if lambdas_explicit is None: 

176 re = self.xp.linspace(-30, 19, 50) 

177 im = self.xp.linspace(-50, 49, 50) 

178 lambdas_implicit = self.xp.array( 

179 [[complex(re[i], im[j]) for i in range(len(re))] for j in range(len(im))] 

180 ).reshape((len(re) * len(im))) 

181 lambdas_implicit = self.xp.asarray(lambdas_implicit) 

182 lambdas_explicit = self.xp.asarray(lambdas_explicit) 

183 

184 assert lambdas_implicit.ndim == 1, f'expect flat list here, got {lambdas_implicit}' 

185 assert lambdas_explicit.shape == lambdas_implicit.shape 

186 nvars = lambdas_implicit.size 

187 assert nvars > 0, 'expect at least one lambda parameter here' 

188 

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

190 super().__init__(init=(nvars, None, self.xp.dtype('complex128'))) 

191 

192 self.A = self.xsp.diags(lambdas_implicit) 

193 self._makeAttributeAndRegister( 

194 'nvars', 'lambdas_implicit', 'lambdas_explicit', 'u0', localVars=locals(), readOnly=True 

195 ) 

196 self.work_counters['rhs'] = WorkCounter() 

197 

198 def eval_f(self, u, t): 

199 """ 

200 Routine to evaluate the right-hand side of the problem. 

201 

202 Parameters 

203 ---------- 

204 u : dtype_u 

205 Current values of the numerical solution. 

206 t : float 

207 Current time of the numerical solution is computed. 

208 

209 Returns 

210 ------- 

211 f : dtype_f 

212 The right-hand side of the problem. 

213 """ 

214 

215 f = self.dtype_f(self.init) 

216 f.impl[:] = u * self.lambdas_implicit 

217 f.expl[:] = u * self.lambdas_explicit 

218 self.work_counters['rhs']() 

219 return f 

220 

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

222 r""" 

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

224 

225 Parameters 

226 ---------- 

227 rhs : dtype_f 

228 Right-hand side for the linear system. 

229 factor : float 

230 Abbrev. for the local stepsize (or any other factor required). 

231 u0 : dtype_u 

232 Initial guess for the iterative solver. 

233 t : float 

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

235 

236 Returns 

237 ------- 

238 me : dtype_u 

239 The solution as mesh. 

240 """ 

241 me = self.dtype_u(self.init) 

242 L = 1 - factor * self.lambdas_implicit 

243 L[L == 0] = 1 # to avoid potential divisions by zeros 

244 me[:] = rhs 

245 me /= L 

246 return me 

247 

248 def u_exact(self, t, u_init=None, t_init=None): 

249 """ 

250 Routine to compute the exact solution at time t. 

251 

252 Parameters 

253 ---------- 

254 t : float 

255 Time of the exact solution. 

256 u_init : pySDC.problem.testequation0d.dtype_u 

257 Initial solution. 

258 t_init : float 

259 The initial time. 

260 

261 Returns 

262 ------- 

263 me : dtype_u 

264 The exact solution. 

265 """ 

266 

267 u_init = (self.u0 if u_init is None else u_init) * 1.0 

268 t_init = 0.0 if t_init is None else t_init * 1.0 

269 

270 me = self.dtype_u(self.init) 

271 me[:] = u_init * self.xp.exp((t - t_init) * (self.lambdas_implicit + self.lambdas_explicit)) 

272 return me