Coverage for pySDC/implementations/problem_classes/GrayScott_2D_PETSc_periodic.py: 99%
305 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
2from petsc4py import PETSc
4from pySDC.core.problem import Problem
5from pySDC.implementations.datatype_classes.petsc_vec import petsc_vec, petsc_vec_imex, petsc_vec_comp2
8class GS_full(object):
9 r"""
10 Helper class to generate residual and Jacobian matrix for ``PETSc``'s nonlinear solver SNES.
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 dy : float
23 Grid spacing in y direction.
25 Attributes
26 ----------
27 localX : PETSc vector object
28 Local vector for ``PETSc``.
29 """
31 def __init__(self, da, prob, factor, dx, dy):
32 """Initialization routine"""
33 assert da.getDim() == 2
34 self.da = da
35 self.prob = prob
36 self.factor = factor
37 self.dx = dx
38 self.dy = dy
39 self.localX = da.createLocalVec()
41 def formFunction(self, snes, X, F):
42 r"""
43 Function to evaluate the residual for the Newton solver. This function should be equal to the RHS
44 in the solution.
46 Parameters
47 ----------
48 snes : PETSc solver object
49 Nonlinear solver object.
50 X : PETSc vector object
51 Input vector.
52 F : PETSc vector object
53 Output vector :math:`F(X)`.
55 Returns
56 -------
57 None
58 Overwrites F.
59 """
60 self.da.globalToLocal(X, self.localX)
61 x = self.da.getVecArray(self.localX)
62 f = self.da.getVecArray(F)
63 (xs, xe), (ys, ye) = self.da.getRanges()
64 for j in range(ys, ye):
65 for i in range(xs, xe):
66 u = x[i, j] # center
67 u_e = x[i + 1, j] # east
68 u_w = x[i - 1, j] # west
69 u_s = x[i, j + 1] # south
70 u_n = x[i, j - 1] # north
71 u_xx = u_e - 2 * u + u_w
72 u_yy = u_n - 2 * u + u_s
73 f[i, j, 0] = x[i, j, 0] - (
74 self.factor
75 * (
76 self.prob.Du * (u_xx[0] / self.dx**2 + u_yy[0] / self.dy**2)
77 - x[i, j, 0] * x[i, j, 1] ** 2
78 + self.prob.A * (1 - x[i, j, 0])
79 )
80 )
81 f[i, j, 1] = x[i, j, 1] - (
82 self.factor
83 * (
84 self.prob.Dv * (u_xx[1] / self.dx**2 + u_yy[1] / self.dy**2)
85 + x[i, j, 0] * x[i, j, 1] ** 2
86 - self.prob.B * x[i, j, 1]
87 )
88 )
90 def formJacobian(self, snes, X, J, P):
91 """
92 Function to return the Jacobian matrix.
94 Parameters
95 ----------
96 snes : PETSc solver object
97 Nonlinear solver object.
98 X : PETSc vector object
99 Input vector.
100 J : PETSc matrix object
101 Current Jacobian matrix.
102 P : PETSc matrix object
103 New Jacobian matrix.
105 Returns
106 -------
107 PETSc.Mat.Structure.SAME_NONZERO_PATTERN
108 Matrix status.
109 """
110 self.da.globalToLocal(X, self.localX)
111 x = self.da.getVecArray(self.localX)
112 P.zeroEntries()
113 row = PETSc.Mat.Stencil()
114 col = PETSc.Mat.Stencil()
115 (xs, xe), (ys, ye) = self.da.getRanges()
117 for j in range(ys, ye):
118 for i in range(xs, xe):
119 # diagnoal 2-by-2 block (for u and v)
120 row.index = (i, j)
121 col.index = (i, j)
122 row.field = 0
123 col.field = 0
124 val = 1.0 - self.factor * (
125 self.prob.Du * (-2.0 / self.dx**2 - 2.0 / self.dy**2) - x[i, j, 1] ** 2 - self.prob.A
126 )
127 P.setValueStencil(row, col, val)
128 row.field = 0
129 col.field = 1
130 val = self.factor * 2.0 * x[i, j, 0] * x[i, j, 1]
131 P.setValueStencil(row, col, val)
132 row.field = 1
133 col.field = 1
134 val = 1.0 - self.factor * (
135 self.prob.Dv * (-2.0 / self.dx**2 - 2.0 / self.dy**2) + 2.0 * x[i, j, 0] * x[i, j, 1] - self.prob.B
136 )
137 P.setValueStencil(row, col, val)
138 row.field = 1
139 col.field = 0
140 val = -self.factor * x[i, j, 1] ** 2
141 P.setValueStencil(row, col, val)
143 # coupling through finite difference part
144 col.index = (i, j - 1)
145 col.field = 0
146 row.field = 0
147 P.setValueStencil(row, col, -self.factor * self.prob.Du / self.dx**2)
148 col.field = 1
149 row.field = 1
150 P.setValueStencil(row, col, -self.factor * self.prob.Dv / self.dy**2)
151 col.index = (i, j + 1)
152 col.field = 0
153 row.field = 0
154 P.setValueStencil(row, col, -self.factor * self.prob.Du / self.dx**2)
155 col.field = 1
156 row.field = 1
157 P.setValueStencil(row, col, -self.factor * self.prob.Dv / self.dy**2)
158 col.index = (i - 1, j)
159 col.field = 0
160 row.field = 0
161 P.setValueStencil(row, col, -self.factor * self.prob.Du / self.dx**2)
162 col.field = 1
163 row.field = 1
164 P.setValueStencil(row, col, -self.factor * self.prob.Dv / self.dy**2)
165 col.index = (i + 1, j)
166 col.field = 0
167 row.field = 0
168 P.setValueStencil(row, col, -self.factor * self.prob.Du / self.dx**2)
169 col.field = 1
170 row.field = 1
171 P.setValueStencil(row, col, -self.factor * self.prob.Dv / self.dy**2)
173 P.assemble()
174 if J != P:
175 J.assemble() # matrix-free operator
176 return PETSc.Mat.Structure.SAME_NONZERO_PATTERN
179class GS_reaction(object):
180 r"""
181 Helper class to generate residual and Jacobian matrix for ``PETSc``'s nonlinear solver SNES.
183 Parameters
184 ----------
185 da : DMDA object
186 Object of PETSc.
187 prob : problem instance
188 Contains problem information for ``PETSc``.
189 factor : float
190 Temporal factor :math:`\Delta t Q_\Delta`.
192 Attributes
193 ----------
194 localX : PETSc vector object
195 Local vector for ``PETSc``.
196 """
198 def __init__(self, da, prob, factor):
199 """Initialization routine"""
200 assert da.getDim() == 2
201 self.da = da
202 self.prob = prob
203 self.factor = factor
204 self.localX = da.createLocalVec()
206 def formFunction(self, snes, X, F):
207 r"""
208 Function to evaluate the residual for the Newton solver. This function should be equal to the RHS
209 in the solution.
211 Parameters
212 ----------
213 snes : PETSc solver object
214 Nonlinear solver object.
215 X : PETSc vector object
216 Input vector.
217 F : PETSc vector object
218 Output vector :math:`F(X)`.
220 Returns
221 -------
222 None
223 Overwrites F.
224 """
225 self.da.globalToLocal(X, self.localX)
226 x = self.da.getVecArray(self.localX)
227 f = self.da.getVecArray(F)
228 (xs, xe), (ys, ye) = self.da.getRanges()
229 for j in range(ys, ye):
230 for i in range(xs, xe):
231 f[i, j, 0] = x[i, j, 0] - (
232 self.factor * (-x[i, j, 0] * x[i, j, 1] ** 2 + self.prob.A * (1 - x[i, j, 0]))
233 )
234 f[i, j, 1] = x[i, j, 1] - (self.factor * (x[i, j, 0] * x[i, j, 1] ** 2 - self.prob.B * x[i, j, 1]))
236 def formJacobian(self, snes, X, J, P):
237 """
238 Function to return the Jacobian matrix.
240 Parameters
241 ----------
242 snes : PETSc solver object
243 Nonlinear solver object.
244 X : PETSc vector object
245 Input vector.
246 J : PETSc matrix object
247 Current Jacobian matrix.
248 P : PETSc matrix object
249 New Jacobian matrix.
251 Returns
252 -------
253 PETSc.Mat.Structure.SAME_NONZERO_PATTERN
254 Matrix status.
255 """
256 self.da.globalToLocal(X, self.localX)
257 x = self.da.getVecArray(self.localX)
258 P.zeroEntries()
259 row = PETSc.Mat.Stencil()
260 col = PETSc.Mat.Stencil()
261 (xs, xe), (ys, ye) = self.da.getRanges()
262 for j in range(ys, ye):
263 for i in range(xs, xe):
264 row.index = (i, j)
265 col.index = (i, j)
266 row.field = 0
267 col.field = 0
268 P.setValueStencil(row, col, 1.0 - self.factor * (-x[i, j, 1] ** 2 - self.prob.A))
269 row.field = 0
270 col.field = 1
271 P.setValueStencil(row, col, self.factor * 2.0 * x[i, j, 0] * x[i, j, 1])
272 row.field = 1
273 col.field = 1
274 P.setValueStencil(row, col, 1.0 - self.factor * (2.0 * x[i, j, 0] * x[i, j, 1] - self.prob.B))
275 row.field = 1
276 col.field = 0
277 P.setValueStencil(row, col, -self.factor * x[i, j, 1] ** 2)
279 P.assemble()
280 if J != P:
281 J.assemble() # matrix-free operator
282 return PETSc.Mat.Structure.SAME_NONZERO_PATTERN
285class petsc_grayscott_multiimplicit(Problem):
286 r"""
287 2D periodic Gray-Scott system with PETSc finite differences, multi-implicit: diffusion (CG), reaction (SNES).
289 The Gray-Scott system [1]_ describes a reaction-diffusion process of two substances :math:`u` and :math:`v`,
290 where they diffuse over time. During the reaction :math:`u` is used up with overall decay rate :math:`B`,
291 whereas :math:`v` is produced with feed rate :math:`A`. :math:`D_u,\, D_v` are the diffusion rates for
292 :math:`u,\, v`. This process is described by the two-dimensional model using periodic boundary conditions
294 .. math::
295 \frac{\partial u}{\partial t} = D_u \Delta u - u v^2 + A (1 - u),
297 .. math::
298 \frac{\partial v}{\partial t} = D_v \Delta v + u v^2 - B u
300 for :math:`x \in \Omega:=[0, 100]`. The spatial solve of the problem is realized by ``PETSc`` [2]_, [3]_. For time-stepping,
301 the diffusion part is solved by one of ``PETSc``'s linear solver, whereas the reaction part will be solved by a nonlinear
302 solver.
304 Parameters
305 ----------
306 nvars : tuple of int, optional
307 Spatial resolution, i.e., number of degrees of freedom in space, e.g. ``nvars=(256, 256)``.
308 Du : float, optional
309 Diffusion rate for :math:`u`.
310 Dv: float, optional
311 Diffusion rate for :math:`v`.
312 A : float, optional
313 Feed rate for :math:`v`.
314 B : float, optional
315 Overall decay rate for :math:`u`.
316 comm : PETSc.COMM_WORLD, optional
317 Communicator for ``PETSc``.
318 lsol_tol : float, optional
319 Tolerance for linear solver to terminate.
320 nlsol_tol : float, optional
321 Tolerance for nonlinear solver to terminate.
322 lsol_maxiter : int, optional
323 Maximum number of iterations for linear solver.
324 nlsol_maxiter : int, optional
325 Maximum number of iterations for nonlinear solver.
327 Attributes
328 ----------
329 dx : float
330 Mesh grid width in x direction.
331 dy : float
332 Mesh grid width in y direction.
333 AMat : PETSc matrix object
334 Discretization matrix.
335 Id : PETSc matrix object
336 Identity matrix.
337 localX : PETSc vector object
338 Local vector for solution.
339 ksp : PETSc solver object
340 Linear solver object.
341 snes : PETSc solver object
342 Nonlinear solver object.
343 snes_itercount : int
344 Number of iterations done by nonlinear solver.
345 snes_ncalls : int
346 Number of calls of ``PETSc``'s nonlinear solver.
348 References
349 ----------
350 .. [1] Autocatalytic reactions in the isothermal, continuous stirred tank reactor: Isolas and other forms
351 of multistability. P. Gray, S. K. Scott. Chem. Eng. Sci. 38, 1 (1983).
352 .. [2] PETSc Web page. Satish Balay et al. https://petsc.org/ (2023).
353 .. [3] Parallel distributed computing using Python. Lisandro D. Dalcin, Rodrigo R. Paz, Pablo A. Kler,
354 Alejandro Cosimo. Advances in Water Resources (2011).
355 """
357 dtype_u = petsc_vec
358 dtype_f = petsc_vec_comp2
360 def __init__(
361 self,
362 nvars,
363 Du,
364 Dv,
365 A,
366 B,
367 comm=PETSc.COMM_WORLD,
368 lsol_tol=1e-10,
369 nlsol_tol=1e-10,
370 lsol_maxiter=None,
371 nlsol_maxiter=None,
372 ):
373 """Initialization routine"""
374 # create DMDA object which will be used for all grid operations (boundary_type=3 -> periodic BC)
375 da = PETSc.DMDA().create(
376 [nvars[0], nvars[1]],
377 dof=2,
378 boundary_type=3,
379 stencil_width=1,
380 comm=comm,
381 )
383 # invoke super init, passing number of dofs, dtype_u and dtype_f
384 super().__init__(init=da)
385 self._makeAttributeAndRegister(
386 'nvars',
387 'Du',
388 'Dv',
389 'A',
390 'B',
391 'comm',
392 'lsol_tol',
393 'lsol_maxiter',
394 'nlsol_tol',
395 'nlsol_maxiter',
396 localVars=locals(),
397 readOnly=True,
398 )
400 # compute dx, dy and get local ranges
401 self.dx = 100.0 / (self.nvars[0])
402 self.dy = 100.0 / (self.nvars[1])
403 (self.xs, self.xe), (self.ys, self.ye) = self.init.getRanges()
405 # compute discretization matrix A and identity
406 self.AMat = self.__get_A()
407 self.Id = self.__get_Id()
408 self.localX = self.init.createLocalVec()
410 # setup linear solver
411 self.ksp = PETSc.KSP()
412 self.ksp.create(comm=self.comm)
413 self.ksp.setType('cg')
414 pc = self.ksp.getPC()
415 pc.setType('none')
416 self.ksp.setInitialGuessNonzero(True)
417 self.ksp.setFromOptions()
418 self.ksp.setTolerances(rtol=self.lsol_tol, atol=self.lsol_tol, max_it=self.lsol_maxiter)
419 self.ksp_itercount = 0
420 self.ksp_ncalls = 0
422 # setup nonlinear solver
423 self.snes = PETSc.SNES()
424 self.snes.create(comm=self.comm)
425 # self.snes.getKSP().setType('cg')
426 # self.snes.setType('ngmres')
427 self.snes.setFromOptions()
428 self.snes.setTolerances(
429 rtol=self.nlsol_tol,
430 atol=self.nlsol_tol,
431 stol=self.nlsol_tol,
432 max_it=self.nlsol_maxiter,
433 )
434 self.snes_itercount = 0
435 self.snes_ncalls = 0
437 def __get_A(self):
438 r"""
439 Helper function to assemble ``PETSc`` matrix A.
441 Returns
442 -------
443 A : PETSc matrix object
444 Discretization matrix.
445 """
446 A = self.init.createMatrix()
447 A.setType('aij') # sparse
448 A.setFromOptions()
449 A.setPreallocationNNZ((5, 5))
450 A.setUp()
452 A.zeroEntries()
453 row = PETSc.Mat.Stencil()
454 col = PETSc.Mat.Stencil()
455 mx, my = self.init.getSizes()
456 (xs, xe), (ys, ye) = self.init.getRanges()
457 for j in range(ys, ye):
458 for i in range(xs, xe):
459 row.index = (i, j)
460 row.field = 0
461 A.setValueStencil(row, row, self.Du * (-2.0 / self.dx**2 - 2.0 / self.dy**2))
462 row.field = 1
463 A.setValueStencil(row, row, self.Dv * (-2.0 / self.dx**2 - 2.0 / self.dy**2))
464 # if j > 0:
465 col.index = (i, j - 1)
466 col.field = 0
467 row.field = 0
468 A.setValueStencil(row, col, self.Du / self.dy**2)
469 col.field = 1
470 row.field = 1
471 A.setValueStencil(row, col, self.Dv / self.dy**2)
472 # if j < my - 1:
473 col.index = (i, j + 1)
474 col.field = 0
475 row.field = 0
476 A.setValueStencil(row, col, self.Du / self.dy**2)
477 col.field = 1
478 row.field = 1
479 A.setValueStencil(row, col, self.Dv / self.dy**2)
480 # if i > 0:
481 col.index = (i - 1, j)
482 col.field = 0
483 row.field = 0
484 A.setValueStencil(row, col, self.Du / self.dx**2)
485 col.field = 1
486 row.field = 1
487 A.setValueStencil(row, col, self.Dv / self.dx**2)
488 # if i < mx - 1:
489 col.index = (i + 1, j)
490 col.field = 0
491 row.field = 0
492 A.setValueStencil(row, col, self.Du / self.dx**2)
493 col.field = 1
494 row.field = 1
495 A.setValueStencil(row, col, self.Dv / self.dx**2)
496 A.assemble()
498 return A
500 def __get_Id(self):
501 r"""
502 Helper function to assemble ``PETSc`` identity matrix.
504 Returns
505 -------
506 Id : PETSc matrix object
507 Identity matrix.
508 """
510 Id = self.init.createMatrix()
511 Id.setType('aij') # sparse
512 Id.setFromOptions()
513 Id.setPreallocationNNZ((1, 1))
514 Id.setUp()
516 Id.zeroEntries()
517 row = PETSc.Mat.Stencil()
518 mx, my = self.init.getSizes()
519 (xs, xe), (ys, ye) = self.init.getRanges()
520 for j in range(ys, ye):
521 for i in range(xs, xe):
522 for indx in [0, 1]:
523 row.index = (i, j)
524 row.field = indx
525 Id.setValueStencil(row, row, 1.0)
527 Id.assemble()
529 return Id
531 def eval_f(self, u, t):
532 """
533 Routine to evaluate the right-hand side of the problem.
535 Parameters
536 ----------
537 u : dtype_u
538 Current values of the numerical solution.
539 t : float
540 Current time the numerical solution is computed.
542 Returns
543 -------
544 f : dtype_f
545 Right-hand side of the problem.
546 """
548 f = self.dtype_f(self.init)
549 self.AMat.mult(u, f.comp1)
551 fa = self.init.getVecArray(f.comp2)
552 xa = self.init.getVecArray(u)
553 for i in range(self.xs, self.xe):
554 for j in range(self.ys, self.ye):
555 fa[i, j, 0] = -xa[i, j, 0] * xa[i, j, 1] ** 2 + self.A * (1 - xa[i, j, 0])
556 fa[i, j, 1] = xa[i, j, 0] * xa[i, j, 1] ** 2 - self.B * xa[i, j, 1]
558 return f
560 def solve_system_1(self, rhs, factor, u0, t):
561 r"""
562 Linear solver for :math:`(I - factor \cdot A)\vec{u} = \vec{rhs}`.
564 Parameters
565 ----------
566 rhs : dtype_f
567 Right-hand side for the linear system.
568 factor : float
569 Abbrev. for the local stepsize (or any other factor required).
570 u0 : dtype_u
571 Initial guess for the iterative solver.
572 t : float
573 Current time (e.g. for time-dependent BCs).
575 Returns
576 -------
577 me : dtype_u
578 Solution as mesh.
579 """
581 me = self.dtype_u(u0)
582 self.ksp.setOperators(self.Id - factor * self.AMat)
583 self.ksp.solve(rhs, me)
585 self.ksp_ncalls += 1
586 self.ksp_itercount += self.ksp.getIterationNumber()
588 return me
590 def solve_system_2(self, rhs, factor, u0, t):
591 r"""
592 Nonlinear solver for :math:`(I - factor \cdot F)(\vec{u}) = \vec{rhs}`.
594 Parameters
595 ----------
596 rhs : dtype_f
597 Right-hand side for the linear system.
598 factor : float
599 Abbrev. for the local stepsize (or any other factor required).
600 u0 : dtype_u
601 Initial guess for the iterative solver.
602 t : float
603 Current time (e.g. for time-dependent BCs).
605 Returns
606 -------
607 me : dtype_u
608 Solution as mesh.
609 """
611 me = self.dtype_u(u0)
612 target = GS_reaction(self.init, self, factor)
614 F = self.init.createGlobalVec()
615 self.snes.setFunction(target.formFunction, F)
616 J = self.init.createMatrix()
617 self.snes.setJacobian(target.formJacobian, J)
619 self.snes.solve(rhs, me)
621 self.snes_ncalls += 1
622 self.snes_itercount += self.snes.getIterationNumber()
624 return me
626 def u_exact(self, t):
627 r"""
628 Routine to compute the exact solution at time :math:`t`.
630 Parameters
631 ----------
632 t : float
633 Time of the exact solution.
635 Returns
636 -------
637 me : dtype_u
638 Exact solution.
639 """
641 assert t == 0, 'ERROR: u_exact is only valid for the initial solution'
643 me = self.dtype_u(self.init)
644 xa = self.init.getVecArray(me)
645 for i in range(self.xs, self.xe):
646 for j in range(self.ys, self.ye):
647 xa[i, j, 0] = 1.0 - 0.5 * np.power(
648 np.sin(np.pi * i * self.dx / 100) * np.sin(np.pi * j * self.dy / 100), 100
649 )
650 xa[i, j, 1] = 0.25 * np.power(
651 np.sin(np.pi * i * self.dx / 100) * np.sin(np.pi * j * self.dy / 100), 100
652 )
654 return me
657class petsc_grayscott_fullyimplicit(petsc_grayscott_multiimplicit):
658 r"""
659 2D periodic Gray-Scott system with PETSc finite differences, fully implicit with PETSc's SNES.
661 The Gray-Scott system [1]_ describes a reaction-diffusion process of two substances :math:`u` and :math:`v`,
662 where they diffuse over time. During the reaction :math:`u` is used up with overall decay rate :math:`B`,
663 whereas :math:`v` is produced with feed rate :math:`A`. :math:`D_u,\, D_v` are the diffusion rates for
664 :math:`u,\, v`. This process is described by the two-dimensional model using periodic boundary conditions
666 .. math::
667 \frac{\partial u}{\partial t} = D_u \Delta u - u v^2 + A (1 - u),
669 .. math::
670 \frac{\partial v}{\partial t} = D_v \Delta v + u v^2 - B u
672 for :math:`x \in \Omega:=[0, 100]`. The spatial solve of the problem is realized by ``PETSc`` [2]_, [3]_. For time-stepping, the
673 problem is handled in a *fully-implicit* way, i.e., the nonlinear system containing the full right-hand side will be
674 solved by PETSc's nonlinear solver.
675 """
677 dtype_f = petsc_vec
679 def eval_f(self, u, t):
680 """
681 Routine to evaluate the right-hand side of the problem.
683 Parameters
684 ----------
685 u : dtype_u
686 Current values of the numerical solution.
687 t : float
688 Current time the numerical solution is computed.
690 Returns
691 -------
692 f : dtype_f
693 Right-hand side of the problem.
694 """
696 f = self.dtype_f(self.init)
697 self.AMat.mult(u, f)
699 fa = self.init.getVecArray(f)
700 xa = self.init.getVecArray(u)
701 for i in range(self.xs, self.xe):
702 for j in range(self.ys, self.ye):
703 fa[i, j, 0] += -xa[i, j, 0] * xa[i, j, 1] ** 2 + self.A * (1 - xa[i, j, 0])
704 fa[i, j, 1] += xa[i, j, 0] * xa[i, j, 1] ** 2 - self.B * xa[i, j, 1]
706 return f
708 def solve_system(self, rhs, factor, u0, t):
709 r"""
710 Nonlinear solver for :math:`(I - factor \cdot F)(\vec{u}) = \vec{rhs}`.
712 Parameters
713 ----------
714 rhs : dtype_f
715 Right-hand side for the linear system.
716 factor : float
717 Abbrev. for the local stepsize (or any other factor required).
718 u0 : dtype_u
719 Initial guess for the iterative solver.
720 t : float
721 Current time (e.g. for time-dependent BCs).
723 Returns
724 -------
725 me : dtype_u
726 Solution as mesh.
727 """
729 me = self.dtype_u(u0)
730 target = GS_full(self.init, self, factor, self.dx, self.dy)
732 # assign residual function and Jacobian
733 F = self.init.createGlobalVec()
734 self.snes.setFunction(target.formFunction, F)
735 J = self.init.createMatrix()
736 self.snes.setJacobian(target.formJacobian, J)
738 self.snes.solve(rhs, me)
740 self.snes_ncalls += 1
741 self.snes_itercount += self.snes.getIterationNumber()
743 return me
746class petsc_grayscott_semiimplicit(petsc_grayscott_multiimplicit):
747 r"""
748 2D periodic Gray-Scott system with PETSc finite differences, IMEX with diffusion implicit, reaction explicit.
750 The Gray-Scott system [1]_ describes a reaction-diffusion process of two substances :math:`u` and :math:`v`,
751 where they diffuse over time. During the reaction :math:`u` is used up with overall decay rate :math:`B`,
752 whereas :math:`v` is produced with feed rate :math:`A`. :math:`D_u,\, D_v` are the diffusion rates for
753 :math:`u,\, v`. This process is described by the two-dimensional model using periodic boundary conditions
755 .. math::
756 \frac{\partial u}{\partial t} = D_u \Delta u - u v^2 + A (1 - u),
758 .. math::
759 \frac{\partial v}{\partial t} = D_v \Delta v + u v^2 - B u
761 for :math:`x \in \Omega:=[0, 100]`. The spatial solve of the problem is realized by ``PETSc`` [2]_, [3]_. For time-stepping,
762 the problem is treated *semi-implicitly*, i.e., the system with diffusion part is solved by PETSc's linear solver.
763 """
765 dtype_f = petsc_vec_imex
767 def eval_f(self, u, t):
768 """
769 Routine to evaluate the right-hand side of the problem.
771 Parameters
772 ----------
773 u : dtype_u
774 Current values of the numerical solution.
775 t : float
776 Current time the numerical solution is computed.
778 Returns
779 -------
780 f : dtype_f
781 Right-hand side of the problem.
782 """
784 f = self.dtype_f(self.init)
785 self.AMat.mult(u, f.impl)
787 fa = self.init.getVecArray(f.expl)
788 xa = self.init.getVecArray(u)
789 for i in range(self.xs, self.xe):
790 for j in range(self.ys, self.ye):
791 fa[i, j, 0] = -xa[i, j, 0] * xa[i, j, 1] ** 2 + self.A * (1 - xa[i, j, 0])
792 fa[i, j, 1] = xa[i, j, 0] * xa[i, j, 1] ** 2 - self.B * xa[i, j, 1]
794 return f
796 def solve_system(self, rhs, factor, u0, t):
797 r"""
798 Linear solver for :math:`(I - factor \cdot A)\vec{u} = \vec{rhs}`.
800 Parameters
801 ----------
802 rhs : dtype_f
803 Right-hand side for the linear system.
804 factor : float
805 Abbrev. for the local stepsize (or any other factor required).
806 u0 : dtype_u
807 Initial guess for the iterative solver.
808 t : float
809 Current time (e.g. for time-dependent BCs).
811 Returns
812 -------
813 me : dtype_u
814 Solution as mesh.
815 """
817 me = self.dtype_u(u0)
818 self.ksp.setOperators(self.Id - factor * self.AMat)
819 self.ksp.solve(rhs, me)
821 self.ksp_ncalls += 1
822 self.ksp_itercount += self.ksp.getIterationNumber()
824 return me