Coverage for pySDC/implementations/problem_classes/GeneralizedFisher_1D_PETSc.py: 99%

243 statements  

« prev     ^ index     » next       coverage.py v7.6.9, created at 2024-12-20 14:51 +0000

1import numpy as np 

2from petsc4py import PETSc 

3 

4from pySDC.core.problem import Problem 

5from pySDC.implementations.datatype_classes.petsc_vec import petsc_vec, petsc_vec_imex, petsc_vec_comp2 

6 

7 

8class Fisher_full(object): 

9 r""" 

10 Helper class to generate residual and Jacobian matrix for PETSc's nonlinear solver SNES. 

11 

12 Parameters 

13 ---------- 

14 da : DMDA object 

15 Object of ``PETSc``. 

16 prob : problem instance 

17 Contains problem information for ``PETSc``. 

18 factor : float 

19 Temporal factor :math:`\Delta t Q_\Delta`. 

20 dx : float 

21 Grid spacing in x direction. 

22 

23 Attributes 

24 ---------- 

25 localX : PETSc vector object 

26 Local vector for ``PETSc``. 

27 xs, xe : int 

28 Defines the ranges for spatial domain. 

29 mx : tuple 

30 Get sizes for the vector containing the spatial points. 

31 row : PETSc matrix stencil object 

32 Row for a matrix. 

33 col : PETSc matrix stencil object 

34 Column for a matrix. 

35 """ 

36 

37 def __init__(self, da, prob, factor, dx): 

38 """Initialization routine""" 

39 assert da.getDim() == 1 

40 self.da = da 

41 self.factor = factor 

42 self.dx = dx 

43 self.prob = prob 

44 self.localX = da.createLocalVec() 

45 self.xs, self.xe = self.da.getRanges()[0] 

46 self.mx = self.da.getSizes()[0] 

47 self.row = PETSc.Mat.Stencil() 

48 self.col = PETSc.Mat.Stencil() 

49 

50 def formFunction(self, snes, X, F): 

51 r""" 

52 Function to evaluate the residual for the Newton solver. This function should be equal to the RHS 

53 in the solution. 

54 

55 Parameters 

56 ---------- 

57 snes : PETSc solver object 

58 Nonlinear solver object. 

59 X : PETSc vector object 

60 Input vector. 

61 F : PETSc vector object 

62 Output vector :math:`F(X)`. 

63 

64 Returns 

65 ------- 

66 None 

67 Overwrites F. 

68 """ 

69 self.da.globalToLocal(X, self.localX) 

70 x = self.da.getVecArray(self.localX) 

71 f = self.da.getVecArray(F) 

72 

73 for i in range(self.xs, self.xe): 

74 if i == 0 or i == self.mx - 1: 

75 f[i] = x[i] 

76 else: 

77 u = x[i] # center 

78 u_e = x[i + 1] # east 

79 u_w = x[i - 1] # west 

80 u_xx = (u_e - 2 * u + u_w) / self.dx**2 

81 f[i] = x[i] - self.factor * (u_xx + self.prob.lambda0**2 * x[i] * (1 - x[i] ** self.prob.nu)) 

82 

83 def formJacobian(self, snes, X, J, P): 

84 """ 

85 Function to return the Jacobian matrix. 

86 

87 Parameters 

88 ---------- 

89 snes : PETSc solver object 

90 Nonlinear solver object. 

91 X : PETSc vector object 

92 Input vector. 

93 J : PETSc matrix object 

94 Current Jacobian matrix. 

95 P : PETSc matrix object 

96 New Jacobian matrix. 

97 

98 Returns 

99 ------- 

100 PETSc.Mat.Structure.SAME_NONZERO_PATTERN 

101 Matrix status. 

102 """ 

103 self.da.globalToLocal(X, self.localX) 

104 x = self.da.getVecArray(self.localX) 

105 P.zeroEntries() 

106 

107 for i in range(self.xs, self.xe): 

108 self.row.i = i 

109 self.row.field = 0 

110 if i == 0 or i == self.mx - 1: 

111 P.setValueStencil(self.row, self.row, 1.0) 

112 else: 

113 diag = 1.0 - self.factor * ( 

114 -2.0 / self.dx**2 + self.prob.lambda0**2 * (1.0 - (self.prob.nu + 1) * x[i] ** self.prob.nu) 

115 ) 

116 for index, value in [ 

117 (i - 1, -self.factor / self.dx**2), 

118 (i, diag), 

119 (i + 1, -self.factor / self.dx**2), 

120 ]: 

121 self.col.i = index 

122 self.col.field = 0 

123 P.setValueStencil(self.row, self.col, value) 

124 P.assemble() 

125 if J != P: 

126 J.assemble() # matrix-free operator 

127 return PETSc.Mat.Structure.SAME_NONZERO_PATTERN 

128 

129 

130class Fisher_reaction(object): 

131 r""" 

132 Helper class to generate residual and Jacobian matrix for ``PETSc``'s nonlinear solver SNES. 

133 

134 Parameters 

135 ---------- 

136 da : DMDA object 

137 Object of ``PETSc``. 

138 prob : problem instance 

139 Contains problem information for ``PETSc``. 

140 factor : float 

141 Temporal factor :math:`\Delta t Q_\Delta`. 

142 dx : float 

143 Grid spacing in x direction. 

144 

145 Attributes 

146 ---------- 

147 localX : PETSc vector object 

148 Local vector for ``PETSc``. 

149 """ 

150 

151 def __init__(self, da, prob, factor): 

152 """Initialization routine""" 

153 assert da.getDim() == 1 

154 self.da = da 

155 self.prob = prob 

156 self.factor = factor 

157 self.localX = da.createLocalVec() 

158 

159 def formFunction(self, snes, X, F): 

160 r""" 

161 Function to evaluate the residual for the Newton solver. This function should be equal to the RHS 

162 in the solution. 

163 

164 Parameters 

165 ---------- 

166 snes : PETSc solver object 

167 Nonlinear solver object. 

168 X : PETSc vector object 

169 Input vector. 

170 F : PETSc vector object 

171 Output vector :math:`F(X)`. 

172 

173 Returns 

174 ------- 

175 None 

176 Overwrites F. 

177 """ 

178 self.da.globalToLocal(X, self.localX) 

179 x = self.da.getVecArray(self.localX) 

180 f = self.da.getVecArray(F) 

181 mx = self.da.getSizes()[0] 

182 (xs, xe) = self.da.getRanges()[0] 

183 for i in range(xs, xe): 

184 if i == 0 or i == mx - 1: 

185 f[i] = x[i] 

186 else: 

187 f[i] = x[i] - self.factor * self.prob.lambda0**2 * x[i] * (1 - x[i] ** self.prob.nu) 

188 

189 def formJacobian(self, snes, X, J, P): 

190 """ 

191 Function to return the Jacobian matrix. 

192 

193 Parameters 

194 ---------- 

195 snes : PETSc solver object 

196 Nonlinear solver object. 

197 X : PETSc vector object 

198 Input vector. 

199 J : PETSc matrix object 

200 Current Jacobian matrix. 

201 P : PETSc matrix object 

202 New Jacobian matrix. 

203 

204 Returns 

205 ------- 

206 PETSc.Mat.Structure.SAME_NONZERO_PATTERN 

207 Matrix status. 

208 """ 

209 self.da.globalToLocal(X, self.localX) 

210 x = self.da.getVecArray(self.localX) 

211 P.zeroEntries() 

212 row = PETSc.Mat.Stencil() 

213 mx = self.da.getSizes()[0] 

214 (xs, xe) = self.da.getRanges()[0] 

215 for i in range(xs, xe): 

216 row.i = i 

217 row.field = 0 

218 if i == 0 or i == mx - 1: 

219 P.setValueStencil(row, row, 1.0) 

220 else: 

221 diag = 1.0 - self.factor * self.prob.lambda0**2 * (1.0 - (self.prob.nu + 1) * x[i] ** self.prob.nu) 

222 P.setValueStencil(row, row, diag) 

223 P.assemble() 

224 if J != P: 

225 J.assemble() # matrix-free operator 

226 return PETSc.Mat.Structure.SAME_NONZERO_PATTERN 

227 

228 

229class petsc_fisher_multiimplicit(Problem): 

230 r""" 

231 The following one-dimensional problem is an example of a reaction-diffusion equation with traveling waves, and can 

232 be seen as a generalized Fisher equation. This class implements a special case of the Kolmogorov-Petrovskii-Piskunov 

233 problem [1]_ using periodic boundary conditions 

234 

235 .. math:: 

236 \frac{\partial u}{\partial t} = \Delta u + \lambda_0^2 u (1 - u^\nu) 

237 

238 with exact solution 

239 

240 .. math:: 

241 u(x, 0) = \left[ 

242 1 + \left(2^{\nu / 2} - 1\right) \exp\left(-(\nu / 2)\sigma_1 x + 2 \lambda_1 t\right) 

243 \right]^{-2 / \nu} 

244 

245 for :math:`x \in \mathbb{R}`, and 

246 

247 .. math:: 

248 \sigma_1 = \lambda_1 - \sqrt{\lambda_1^2 - \lambda_0^2}, 

249 

250 .. math:: 

251 \lambda_1 = \frac{\lambda_0}{2} \left[ 

252 \left(1 + \frac{\nu}{2}\right)^{1/2} + \left(1 + \frac{\nu}{2}\right)^{-1/2} 

253 \right]. 

254 

255 This class is implemented to be solved in spatial using ``PETSc`` [2]_, [3]_. For time-stepping, the problem will be solved 

256 *multi-implicitly*. 

257 

258 Parameters 

259 ---------- 

260 nvars : int, optional 

261 Spatial resolution. 

262 lambda0 : float, optional 

263 Problem parameter : math:`\lambda_0`. 

264 nu : float, optional 

265 Problem parameter :math:`\nu`. 

266 interval : tuple, optional 

267 Defines the spatial domain. 

268 comm : PETSc.COMM_WORLD, optional 

269 Communicator for PETSc. 

270 lsol_tol : float, optional 

271 Tolerance for linear solver to terminate. 

272 nlsol_tol : float, optional 

273 Tolerance for nonlinear solver to terminate. 

274 lsol_maxiter : int, optional 

275 Maximum number of iterations for linear solver. 

276 nlsol_maxiter : int, optional 

277 Maximum number of iterations for nonlinear solver. 

278 

279 Attributes 

280 ---------- 

281 dx : float 

282 Mesh grid width. 

283 xs, xe : int 

284 Define the ranges. 

285 A : PETSc matrix object 

286 Discretization matrix. 

287 localX : PETSc vector object 

288 Local vector for solution. 

289 ksp : PETSc solver object 

290 Linear solver object. 

291 snes : PETSc solver object 

292 Nonlinear solver object. 

293 F : PETSc vector object 

294 Global vector. 

295 J : PETSc matrix object 

296 Jacobi matrix. 

297 

298 References 

299 ---------- 

300 .. [1] Z. Feng. Traveling wave behavior for a generalized fisher equation. Chaos Solitons Fractals 38(2), 

301 481–488 (2008). 

302 .. [2] PETSc Web page. Satish Balay et al. https://petsc.org/ (2023). 

303 .. [3] Parallel distributed computing using Python. Lisandro D. Dalcin, Rodrigo R. Paz, Pablo A. Kler, 

304 Alejandro Cosimo. Advances in Water Resources (2011). 

305 """ 

306 

307 dtype_u = petsc_vec 

308 dtype_f = petsc_vec_comp2 

309 

310 def __init__( 

311 self, 

312 nvars, 

313 lambda0, 

314 nu, 

315 interval, 

316 comm=PETSc.COMM_WORLD, 

317 lsol_tol=1e-10, 

318 nlsol_tol=1e-10, 

319 lsol_maxiter=None, 

320 nlsol_maxiter=None, 

321 ): 

322 """Initialization routine""" 

323 # create DMDA object which will be used for all grid operations 

324 da = PETSc.DMDA().create([nvars], dof=1, stencil_width=1, comm=comm) 

325 

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

327 super().__init__(init=da) 

328 self._makeAttributeAndRegister( 

329 'nvars', 

330 'lambda0', 

331 'nu', 

332 'interval', 

333 'comm', 

334 'lsol_tol', 

335 'nlsol_tol', 

336 'lsol_maxiter', 

337 'nlsol_maxiter', 

338 localVars=locals(), 

339 readOnly=True, 

340 ) 

341 

342 # compute dx and get local ranges 

343 self.dx = (self.interval[1] - self.interval[0]) / (self.nvars - 1) 

344 (self.xs, self.xe) = self.init.getRanges()[0] 

345 

346 # compute discretization matrix A and identity 

347 self.A = self.__get_A() 

348 self.localX = self.init.createLocalVec() 

349 

350 # setup linear solver 

351 self.ksp = PETSc.KSP() 

352 self.ksp.create(comm=self.comm) 

353 self.ksp.setType('cg') 

354 pc = self.ksp.getPC() 

355 pc.setType('ilu') 

356 self.ksp.setInitialGuessNonzero(True) 

357 self.ksp.setFromOptions() 

358 self.ksp.setTolerances(rtol=self.lsol_tol, atol=self.lsol_tol, max_it=self.lsol_maxiter) 

359 self.ksp_itercount = 0 

360 self.ksp_ncalls = 0 

361 

362 # setup nonlinear solver 

363 self.snes = PETSc.SNES() 

364 self.snes.create(comm=self.comm) 

365 if self.nlsol_maxiter <= 1: 

366 self.snes.setType('ksponly') 

367 self.snes.getKSP().setType('cg') 

368 pc = self.snes.getKSP().getPC() 

369 pc.setType('ilu') 

370 # self.snes.setType('ngmres') 

371 self.snes.setFromOptions() 

372 self.snes.setTolerances( 

373 rtol=self.nlsol_tol, 

374 atol=self.nlsol_tol, 

375 stol=self.nlsol_tol, 

376 max_it=self.nlsol_maxiter, 

377 ) 

378 self.snes_itercount = 0 

379 self.snes_ncalls = 0 

380 self.F = self.init.createGlobalVec() 

381 self.J = self.init.createMatrix() 

382 

383 def __get_A(self): 

384 r""" 

385 Helper function to assemble ``PETSc`` matrix A. 

386 

387 Returns 

388 ------- 

389 A : PETSc matrix object 

390 Discretization matrix. 

391 """ 

392 # create matrix and set basic options 

393 A = self.init.createMatrix() 

394 A.setType('aij') # sparse 

395 A.setFromOptions() 

396 A.setPreallocationNNZ((3, 3)) 

397 A.setUp() 

398 

399 # fill matrix 

400 A.zeroEntries() 

401 row = PETSc.Mat.Stencil() 

402 col = PETSc.Mat.Stencil() 

403 mx = self.init.getSizes()[0] 

404 (xs, xe) = self.init.getRanges()[0] 

405 for i in range(xs, xe): 

406 row.i = i 

407 row.field = 0 

408 if i == 0 or i == mx - 1: 

409 A.setValueStencil(row, row, 1.0) 

410 else: 

411 diag = -2.0 / self.dx**2 

412 for index, value in [ 

413 (i - 1, 1.0 / self.dx**2), 

414 (i, diag), 

415 (i + 1, 1.0 / self.dx**2), 

416 ]: 

417 col.i = index 

418 col.field = 0 

419 A.setValueStencil(row, col, value) 

420 A.assemble() 

421 return A 

422 

423 def get_sys_mat(self, factor): 

424 """ 

425 Helper function to assemble the system matrix of the linear problem. 

426 

427 Parameters 

428 ---------- 

429 factor : float 

430 Factor to define the system matrix. 

431 

432 Returns 

433 ------- 

434 A : PETSc matrix object 

435 Matrix for the system to solve. 

436 """ 

437 

438 # create matrix and set basic options 

439 A = self.init.createMatrix() 

440 A.setType('aij') # sparse 

441 A.setFromOptions() 

442 A.setPreallocationNNZ((3, 3)) 

443 A.setUp() 

444 

445 # fill matrix 

446 A.zeroEntries() 

447 row = PETSc.Mat.Stencil() 

448 col = PETSc.Mat.Stencil() 

449 mx = self.init.getSizes()[0] 

450 (xs, xe) = self.init.getRanges()[0] 

451 for i in range(xs, xe): 

452 row.i = i 

453 row.field = 0 

454 if i == 0 or i == mx - 1: 

455 A.setValueStencil(row, row, 1.0) 

456 else: 

457 diag = 1.0 + factor * 2.0 / self.dx**2 

458 for index, value in [ 

459 (i - 1, -factor / self.dx**2), 

460 (i, diag), 

461 (i + 1, -factor / self.dx**2), 

462 ]: 

463 col.i = index 

464 col.field = 0 

465 A.setValueStencil(row, col, value) 

466 A.assemble() 

467 return A 

468 

469 def eval_f(self, u, t): 

470 """ 

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

472 

473 Parameters 

474 ---------- 

475 u : dtype_u 

476 Current values of the numerical solution. 

477 t : float 

478 Current time the numerical solution is computed. 

479 

480 Returns 

481 ------- 

482 f : dtype_f 

483 Right-hand side of the problem. 

484 """ 

485 

486 f = self.dtype_f(self.init) 

487 self.A.mult(u, f.comp1) 

488 fa1 = self.init.getVecArray(f.comp1) 

489 fa1[0] = 0 

490 fa1[-1] = 0 

491 

492 fa2 = self.init.getVecArray(f.comp2) 

493 xa = self.init.getVecArray(u) 

494 for i in range(self.xs, self.xe): 

495 fa2[i] = self.lambda0**2 * xa[i] * (1 - xa[i] ** self.nu) 

496 fa2[0] = 0 

497 fa2[-1] = 0 

498 

499 return f 

500 

501 def solve_system_1(self, rhs, factor, u0, t): 

502 r""" 

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

504 

505 Parameters 

506 ---------- 

507 rhs : dtype_f 

508 Right-hand side for the linear system. 

509 factor : float 

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

511 u0 : dtype_u 

512 Initial guess for the iterative solver. 

513 t : float 

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

515 

516 Returns 

517 ------- 

518 me : dtype_u 

519 Solution as mesh. 

520 """ 

521 

522 me = self.dtype_u(u0) 

523 

524 self.ksp.setOperators(self.get_sys_mat(factor)) 

525 self.ksp.solve(rhs, me) 

526 

527 self.ksp_itercount += self.ksp.getIterationNumber() 

528 self.ksp_ncalls += 1 

529 

530 return me 

531 

532 def solve_system_2(self, rhs, factor, u0, t): 

533 r""" 

534 Nonlinear solver for :math:`(I - factor \cdot F)(\vec{u}) = \vec{rhs}`. 

535 

536 Parameters 

537 ---------- 

538 rhs : dtype_f 

539 Right-hand side for the linear system. 

540 factor : float 

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

542 u0 : dtype_u 

543 Initial guess for the iterative solver. 

544 t : float 

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

546 

547 Returns 

548 ------- 

549 me : dtype_u 

550 Solution as mesh. 

551 """ 

552 

553 me = self.dtype_u(u0) 

554 target = Fisher_reaction(self.init, self, factor) 

555 

556 # assign residual function and Jacobian 

557 F = self.init.createGlobalVec() 

558 self.snes.setFunction(target.formFunction, F) 

559 J = self.init.createMatrix() 

560 self.snes.setJacobian(target.formJacobian, J) 

561 

562 self.snes.solve(rhs, me) 

563 

564 self.snes_itercount += self.snes.getIterationNumber() 

565 self.snes_ncalls += 1 

566 

567 return me 

568 

569 def u_exact(self, t): 

570 r""" 

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

572 

573 Parameters 

574 ---------- 

575 t : float 

576 Time of the exact solution. 

577 

578 Returns 

579 ------- 

580 me : dtype_u 

581 Exact solution. 

582 """ 

583 

584 lam1 = self.lambda0 / 2.0 * ((self.nu / 2.0 + 1) ** 0.5 + (self.nu / 2.0 + 1) ** (-0.5)) 

585 sig1 = lam1 - np.sqrt(lam1**2 - self.lambda0**2) 

586 me = self.dtype_u(self.init) 

587 xa = self.init.getVecArray(me) 

588 for i in range(self.xs, self.xe): 

589 xa[i] = ( 

590 1 

591 + (2 ** (self.nu / 2.0) - 1) 

592 * np.exp(-self.nu / 2.0 * sig1 * (self.interval[0] + (i + 1) * self.dx + 2 * lam1 * t)) 

593 ) ** (-2.0 / self.nu) 

594 

595 return me 

596 

597 

598class petsc_fisher_fullyimplicit(petsc_fisher_multiimplicit): 

599 r""" 

600 The following one-dimensional problem is an example of a reaction-diffusion equation with traveling waves, and can 

601 be seen as a generalized Fisher equation. This class implements a special case of the Kolmogorov-Petrovskii-Piskunov 

602 problem [1]_ using periodic boundary conditions 

603 

604 .. math:: 

605 \frac{\partial u}{\partial t} = \Delta u + \lambda_0^2 u (1 - u^\nu) 

606 

607 with exact solution 

608 

609 .. math:: 

610 u(x, 0) = \left[ 

611 1 + \left(2^{\nu / 2} - 1\right) \exp\left(-(\nu / 2)\sigma_1 x + 2 \lambda_1 t\right) 

612 \right]^{-2 / \nu} 

613 

614 for :math:`x \in \mathbb{R}`, and 

615 

616 .. math:: 

617 \sigma_1 = \lambda_1 - \sqrt{\lambda_1^2 - \lambda_0^2}, 

618 

619 .. math:: 

620 \lambda_1 = \frac{\lambda_0}{2} \left[ 

621 \left(1 + \frac{\nu}{2}\right)^{1/2} + \left(1 + \frac{\nu}{2}\right)^{-1/2} 

622 \right]. 

623 

624 This class is implemented to be solved in spatial using ``PETSc`` [2]_, [3]_. For time-stepping, the problem is treated 

625 *fully-implicitly*. 

626 """ 

627 

628 dtype_f = petsc_vec 

629 

630 def eval_f(self, u, t): 

631 """ 

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

633 

634 Parameters 

635 ---------- 

636 u : dtype_u 

637 Current values of the numerical solution. 

638 t : float 

639 Current time the numerical solution is computed. 

640 

641 Returns 

642 ------- 

643 f : dtype_f 

644 Right-hand side of the problem. 

645 """ 

646 

647 f = self.dtype_f(self.init) 

648 self.A.mult(u, f) 

649 

650 fa2 = self.init.getVecArray(f) 

651 xa = self.init.getVecArray(u) 

652 for i in range(self.xs, self.xe): 

653 fa2[i] += self.lambda0**2 * xa[i] * (1 - xa[i] ** self.nu) 

654 fa2[0] = 0 

655 fa2[-1] = 0 

656 

657 return f 

658 

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

660 r""" 

661 Nonlinear solver for :math:`(I - factor \cdot F)(\vec{u}) = \vec{rhs}`. 

662 

663 Parameters 

664 ---------- 

665 rhs : dtype_f 

666 Right-hand side for the linear system. 

667 factor : float 

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

669 u0 : dtype_u 

670 Initial guess for the iterative solver. 

671 t : float 

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

673 

674 Returns 

675 ------- 

676 me : dtype_u 

677 Solution as mesh. 

678 """ 

679 

680 me = self.dtype_u(u0) 

681 target = Fisher_full(self.init, self, factor, self.dx) 

682 

683 # assign residual function and Jacobian 

684 

685 self.snes.setFunction(target.formFunction, self.F) 

686 self.snes.setJacobian(target.formJacobian, self.J) 

687 

688 self.snes.solve(rhs, me) 

689 

690 self.snes_itercount += self.snes.getIterationNumber() 

691 self.snes_ncalls += 1 

692 

693 return me 

694 

695 

696class petsc_fisher_semiimplicit(petsc_fisher_multiimplicit): 

697 r""" 

698 The following one-dimensional problem is an example of a reaction-diffusion equation with traveling waves, and can 

699 be seen as a generalized Fisher equation. This class implements a special case of the Kolmogorov-Petrovskii-Piskunov 

700 problem [1]_ using periodic boundary conditions 

701 

702 .. math:: 

703 \frac{\partial u}{\partial t} = \Delta u + \lambda_0^2 u (1 - u^\nu) 

704 

705 with exact solution 

706 

707 .. math:: 

708 u(x, 0) = \left[ 

709 1 + \left(2^{\nu / 2} - 1\right) \exp\left(-(\nu / 2)\sigma_1 x + 2 \lambda_1 t\right) 

710 \right]^{-2 / \nu} 

711 

712 for :math:`x \in \mathbb{R}`, and 

713 

714 .. math:: 

715 \sigma_1 = \lambda_1 - \sqrt{\lambda_1^2 - \lambda_0^2}, 

716 

717 .. math:: 

718 \lambda_1 = \frac{\lambda_0}{2} \left[ 

719 \left(1 + \frac{\nu}{2}\right)^{1/2} + \left(1 + \frac{\nu}{2}\right)^{-1/2} 

720 \right]. 

721 

722 This class is implemented to be solved in spatial using ``PETSc`` [2]_, [3]_. For time-stepping, the problem here will be 

723 solved in a *semi-implicit* way. 

724 """ 

725 

726 dtype_f = petsc_vec_imex 

727 

728 def eval_f(self, u, t): 

729 """ 

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

731 

732 Parameters 

733 ---------- 

734 u : dtype_u 

735 Current values of the numerical solution. 

736 t : float 

737 Current time the numerical solution is computed. 

738 

739 Returns 

740 ------- 

741 f : dtype_f 

742 Right-hand side of the problem. 

743 """ 

744 

745 f = self.dtype_f(self.init) 

746 self.A.mult(u, f.impl) 

747 fa1 = self.init.getVecArray(f.impl) 

748 fa1[0] = 0 

749 fa1[-1] = 0 

750 

751 fa2 = self.init.getVecArray(f.expl) 

752 xa = self.init.getVecArray(u) 

753 for i in range(self.xs, self.xe): 

754 fa2[i] = self.lambda0**2 * xa[i] * (1 - xa[i] ** self.nu) 

755 fa2[0] = 0 

756 fa2[-1] = 0 

757 

758 return f 

759 

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

761 r""" 

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

763 

764 Parameters 

765 ---------- 

766 rhs : dtype_f 

767 Right-hand side for the linear system. 

768 factor : float 

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

770 u0 : dtype_u 

771 Initial guess for the iterative solver. 

772 t : float 

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

774 

775 Returns 

776 ------- 

777 me : dtype_u 

778 Solution as mesh. 

779 """ 

780 

781 me = self.dtype_u(u0) 

782 

783 self.ksp.setOperators(self.get_sys_mat(factor)) 

784 self.ksp.solve(rhs, me) 

785 

786 self.ksp_itercount += self.ksp.getIterationNumber() 

787 self.ksp_ncalls += 1 

788 

789 return me