File: mat.py

package info (click to toggle)
petsc4py 3.23.1-1exp2
  • links: PTS, VCS
  • area: main
  • in suites: experimental
  • size: 3,448 kB
  • sloc: python: 12,503; ansic: 1,697; makefile: 343; f90: 313; sh: 14
file content (154 lines) | stat: -rw-r--r-- 4,938 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
# ------------------------------------------------------------------------
#
#  Poisson problem. This problem is modeled by the partial
#  differential equation
#
#          -Laplacian(u) = 1,  0 < x,y < 1,
#
#  with boundary conditions
#
#           u = 0  for  x = 0, x = 1, y = 0, y = 1
#
#  A finite difference approximation with the usual 5-point stencil
#  is used to discretize the boundary value problem to obtain a
#  nonlinear system of equations. The problem is solved in a 2D
#  rectangular domain, using distributed arrays (DAs) to partition
#  the parallel grid.
#
# ------------------------------------------------------------------------

# We first import petsc4py and sys to initialize PETSc
import sys
import petsc4py

petsc4py.init(sys.argv)

# Import the PETSc module
from petsc4py import PETSc


# Here we define a class representing the discretized operator
# This allows us to apply the operator "matrix-free"
class Poisson2D:
    def __init__(self, da):
        self.da = da
        self.localX = da.createLocalVec()

    # This is the method that PETSc will look for when applying
    # the operator. `X` is the PETSc input vector, `Y` the output vector,
    # while `mat` is the PETSc matrix holding the PETSc datastructures.
    def mult(self, mat, X, Y):
        # Grid sizes
        mx, my = self.da.getSizes()
        hx, hy = (1.0 / m for m in [mx, my])

        # Bounds for the local part of the grid this process owns
        (xs, xe), (ys, ye) = self.da.getRanges()

        # Map global vector to local vectors
        self.da.globalToLocal(X, self.localX)

        # We can access the vector data as NumPy arrays
        x = self.da.getVecArray(self.localX)
        y = self.da.getVecArray(Y)

        # Loop on the local grid and compute the local action of the operator
        for j in range(ys, ye):
            for i in range(xs, xe):
                u = x[i, j]  # center
                u_e = u_w = u_n = u_s = 0
                if i > 0:
                    u_w = x[i - 1, j]  # west
                if i < mx - 1:
                    u_e = x[i + 1, j]  # east
                if j > 0:
                    u_s = x[i, j - 1]  # south
                if j < ny - 1:
                    u_n = x[i, j + 1]  # north
                u_xx = (-u_e + 2 * u - u_w) * hy / hx
                u_yy = (-u_n + 2 * u - u_s) * hx / hy
                y[i, j] = u_xx + u_yy

    # This is the method that PETSc will look for when the diagonal of the matrix is needed.
    def getDiagonal(self, mat, D):
        mx, my = self.da.getSizes()
        hx, hy = (1.0 / m for m in [mx, my])
        (xs, xe), (ys, ye) = self.da.getRanges()

        d = self.da.getVecArray(D)

        # Loop on the local grid and compute the diagonal
        for j in range(ys, ye):
            for i in range(xs, xe):
                d[i, j] = 2 * hy / hx + 2 * hx / hy

    # The class can contain other methods that PETSc won't use
    def formRHS(self, B):
        b = self.da.getVecArray(B)
        mx, my = self.da.getSizes()
        hx, hy = (1.0 / m for m in [mx, my])
        (xs, xe), (ys, ye) = self.da.getRanges()
        for j in range(ys, ye):
            for i in range(xs, xe):
                b[i, j] = 1 * hx * hy


# Access the option database and read options from the command line
OptDB = PETSc.Options()
nx, ny = OptDB.getIntArray(
    'grid', (16, 16)
)  # Read `-grid <int,int>`, defaults to 16,16

# Create the distributed memory implementation for structured grid
da = PETSc.DMDA().create([nx, ny], stencil_width=1)

# Create vectors to hold the solution and the right-hand side
x = da.createGlobalVec()
b = da.createGlobalVec()

# Instantiate an object of our Poisson2D class
pde = Poisson2D(da)

# Create a PETSc matrix of type Python using `pde` as context
A = PETSc.Mat().create(comm=da.comm)
A.setSizes([x.getSizes(), b.getSizes()])
A.setType(PETSc.Mat.Type.PYTHON)
A.setPythonContext(pde)
A.setUp()

# Create a Conjugate Gradient Krylov solver
ksp = PETSc.KSP().create()
ksp.setType(PETSc.KSP.Type.CG)

# Use diagonal preconditioning
ksp.getPC().setType(PETSc.PC.Type.JACOBI)

# Allow command-line customization
ksp.setFromOptions()

# Assemble right-hand side and solve the linear system
pde.formRHS(b)
ksp.setOperators(A)
ksp.solve(b, x)

# Here we programmatically visualize the solution
if OptDB.getBool('plot', True):
    # Modify the option database: keep the X window open for 1 second
    OptDB['draw_pause'] = 1

    # Obtain a viewer of type DRAW
    draw = PETSc.Viewer.DRAW(x.comm)

    # View the vector in the X window
    draw(x)

# We can also visualize the solution by command line options
# For example, we can dump a VTK file with:
#
#     $ python poisson2d.py -plot 0 -view_solution vtk:sol.vts:
#
# or obtain the same visualization as programmatically done above as:
#
#     $ python poisson2d.py -plot 0 -view_solution draw -draw_pause 1
#
x.viewFromOptions('-view_solution')