xref: /petsc/src/binding/petsc4py/demo/poisson2d/poisson2d.py (revision 55a74a43bb44613d95e937906bec3b8c3581b432)
1*55a74a43SLisandro Dalcin# Poisson in 2D
2*55a74a43SLisandro Dalcin# =============
35808f684SSatish Balay#
4*55a74a43SLisandro Dalcin# Solve a constant coefficient Poisson problem on a regular grid. The source
5*55a74a43SLisandro Dalcin# code for this demo can be `downloaded here <../../_static/poisson2d.py>`__
65808f684SSatish Balay#
7*55a74a43SLisandro Dalcin# .. math::
85808f684SSatish Balay#
9*55a74a43SLisandro Dalcin#     - u_{xx} - u_{yy} = 1 \quad\textsf{in}\quad [0,1]^2\\
10*55a74a43SLisandro Dalcin#     u = 0 \quad\textsf{on the boundary.}
115808f684SSatish Balay
12*55a74a43SLisandro Dalcin# This is a naïve, parallel implementation, using :math:`n` interior grid
13*55a74a43SLisandro Dalcin# points per dimension and a lexicographic ordering of the nodes.
145808f684SSatish Balay
15*55a74a43SLisandro Dalcin# This code is kept as simple as possible. However, simplicity comes at a
16*55a74a43SLisandro Dalcin# price. Here we use a naive decomposition that does not lead to an optimal
17*55a74a43SLisandro Dalcin# communication complexity for the matrix-vector product. An optimal complexity
18*55a74a43SLisandro Dalcin# decomposition of a structured grid could be achieved using `PETSc.DMDA`.
19*55a74a43SLisandro Dalcin#
20*55a74a43SLisandro Dalcin# This demo is structured as a script to be executed using:
21*55a74a43SLisandro Dalcin#
22*55a74a43SLisandro Dalcin# .. code-block:: console
23*55a74a43SLisandro Dalcin#
24*55a74a43SLisandro Dalcin#   $ python poisson2d.py
25*55a74a43SLisandro Dalcin#
26*55a74a43SLisandro Dalcin# potentially with additional options passed at the end of the command.
27*55a74a43SLisandro Dalcin#
28*55a74a43SLisandro Dalcin# At the start of your script, call `petsc4py.init` passing `sys.argv` so that
29*55a74a43SLisandro Dalcin# command-line arguments to the script are passed through to PETSc.
30*55a74a43SLisandro Dalcin
31*55a74a43SLisandro Dalcinimport sys
32*55a74a43SLisandro Dalcinimport petsc4py
33*55a74a43SLisandro Dalcin
345808f684SSatish Balaypetsc4py.init(sys.argv)
355808f684SSatish Balay
36*55a74a43SLisandro Dalcin# The full PETSc4py API is to be found in the `petsc4py.PETSc` module.
37*55a74a43SLisandro Dalcin
385808f684SSatish Balayfrom petsc4py import PETSc
395808f684SSatish Balay
40*55a74a43SLisandro Dalcin# PETSc is extensively programmable using the `PETSc.Options` database. For
41*55a74a43SLisandro Dalcin# more information see `working with PETSc Options <petsc_options>`.
425808f684SSatish Balay
435808f684SSatish BalayOptDB = PETSc.Options()
445808f684SSatish Balay
45*55a74a43SLisandro Dalcin# Grid size and spacing using a default value of ``5``. The user can specify a
46*55a74a43SLisandro Dalcin# different number of points in each direction by passing the ``-n`` option to
47*55a74a43SLisandro Dalcin# the script.
485808f684SSatish Balay
49*55a74a43SLisandro Dalcinn = OptDB.getInt('n', 5)
50*55a74a43SLisandro Dalcinh = 1.0 / (n + 1)
515808f684SSatish Balay
52*55a74a43SLisandro Dalcin# Sparse matrices are represented by `PETSc.Mat` objects.
53*55a74a43SLisandro Dalcin#
54*55a74a43SLisandro Dalcin# You can omit the ``comm`` argument if your objects live on
55*55a74a43SLisandro Dalcin# `PETSc.COMM_WORLD` but it is a dangerous choice to rely on default values
56*55a74a43SLisandro Dalcin# for such important arguments.
575808f684SSatish Balay
58*55a74a43SLisandro DalcinA = PETSc.Mat()
59*55a74a43SLisandro DalcinA.create(comm=PETSc.COMM_WORLD)
60*55a74a43SLisandro Dalcin
61*55a74a43SLisandro Dalcin# Specify global matrix shape with a tuple.
62*55a74a43SLisandro Dalcin
63*55a74a43SLisandro DalcinA.setSizes((n * n, n * n))
64*55a74a43SLisandro Dalcin
65*55a74a43SLisandro Dalcin# The call above implicitly assumes that we leave the parallel decomposition of
66*55a74a43SLisandro Dalcin# the matrix rows to PETSc by using `PETSc.DECIDE` for local sizes.
67*55a74a43SLisandro Dalcin# It is equivalent to:
68*55a74a43SLisandro Dalcin#
69*55a74a43SLisandro Dalcin# .. code-block:: python
70*55a74a43SLisandro Dalcin#
71*55a74a43SLisandro Dalcin#     A.setSizes(((PETSc.DECIDE, n * n), (PETSc.DECIDE, n * n)))
72*55a74a43SLisandro Dalcin
73*55a74a43SLisandro Dalcin# Various `sparse matrix formats <petsc4py.PETSc.Mat.Type>` can be selected:
74*55a74a43SLisandro Dalcin
75*55a74a43SLisandro DalcinA.setType(PETSc.Mat.Type.AIJ)
76*55a74a43SLisandro Dalcin
77*55a74a43SLisandro Dalcin# Finally we allow the user to set any options they want to on the matrix from
78*55a74a43SLisandro Dalcin# the command line:
79*55a74a43SLisandro Dalcin
80*55a74a43SLisandro DalcinA.setFromOptions()
81*55a74a43SLisandro Dalcin
82*55a74a43SLisandro Dalcin# Insertion into some matrix types is vastly more efficient if we preallocate
83*55a74a43SLisandro Dalcin# space rather than allow this to happen dynamically. Here we hint the number
84*55a74a43SLisandro Dalcin# of nonzeros to be expected on each row.
85*55a74a43SLisandro Dalcin
86*55a74a43SLisandro DalcinA.setPreallocationNNZ(5)
87*55a74a43SLisandro Dalcin
88*55a74a43SLisandro Dalcin# We can now write out our finite difference matrix assembly using conventional
89*55a74a43SLisandro Dalcin# Python syntax. `Mat.getOwnershipRange` is used to retrieve the range of rows
90*55a74a43SLisandro Dalcin# local to this processor.
91*55a74a43SLisandro Dalcin
92*55a74a43SLisandro Dalcin
93*55a74a43SLisandro Dalcindef index_to_grid(r):
94*55a74a43SLisandro Dalcin    """Convert a row number into a grid point."""
95*55a74a43SLisandro Dalcin    return (r // n, r % n)
96*55a74a43SLisandro Dalcin
97*55a74a43SLisandro Dalcin
98*55a74a43SLisandro Dalcinrstart, rend = A.getOwnershipRange()
99*55a74a43SLisandro Dalcinfor row in range(rstart, rend):
100*55a74a43SLisandro Dalcin    i, j = index_to_grid(row)
101*55a74a43SLisandro Dalcin    A[row, row] = 4.0 / h**2
102*55a74a43SLisandro Dalcin    if i > 0:
103*55a74a43SLisandro Dalcin        column = row - n
104*55a74a43SLisandro Dalcin        A[row, column] = -1.0 / h**2
105*55a74a43SLisandro Dalcin    if i < n - 1:
106*55a74a43SLisandro Dalcin        column = row + n
107*55a74a43SLisandro Dalcin        A[row, column] = -1.0 / h**2
108*55a74a43SLisandro Dalcin    if j > 0:
109*55a74a43SLisandro Dalcin        column = row - 1
110*55a74a43SLisandro Dalcin        A[row, column] = -1.0 / h**2
111*55a74a43SLisandro Dalcin    if j < n - 1:
112*55a74a43SLisandro Dalcin        column = row + 1
113*55a74a43SLisandro Dalcin        A[row, column] = -1.0 / h**2
114*55a74a43SLisandro Dalcin
115*55a74a43SLisandro Dalcin# At this stage, any parallel synchronization required in the matrix assembly
116*55a74a43SLisandro Dalcin# process has not occurred. We achieve this by calling `Mat.assemblyBegin` and
117*55a74a43SLisandro Dalcin# then `Mat.assemblyEnd`.
118*55a74a43SLisandro Dalcin
119*55a74a43SLisandro DalcinA.assemblyBegin()
120*55a74a43SLisandro DalcinA.assemblyEnd()
121*55a74a43SLisandro Dalcin
122*55a74a43SLisandro Dalcin# We set up an additional option so that the user can print the matrix by
123*55a74a43SLisandro Dalcin# passing ``-view_mat`` to the script.
124*55a74a43SLisandro Dalcin
125*55a74a43SLisandro DalcinA.viewFromOptions('-view_mat')
126*55a74a43SLisandro Dalcin
127*55a74a43SLisandro Dalcin# PETSc represents all linear solvers as preconditioned Krylov subspace methods
128*55a74a43SLisandro Dalcin# of type `PETSc.KSP`. Here we create a KSP object for a conjugate gradient
129*55a74a43SLisandro Dalcin# solver preconditioned with an algebraic multigrid method.
130*55a74a43SLisandro Dalcin
131*55a74a43SLisandro Dalcinksp = PETSc.KSP()
132*55a74a43SLisandro Dalcinksp.create(comm=A.getComm())
133*55a74a43SLisandro Dalcinksp.setType(PETSc.KSP.Type.CG)
134*55a74a43SLisandro Dalcinksp.getPC().setType(PETSc.PC.Type.GAMG)
135*55a74a43SLisandro Dalcin
136*55a74a43SLisandro Dalcin# We set the matrix in our linear solver and allow the user to program the
137*55a74a43SLisandro Dalcin# solver with options.
138*55a74a43SLisandro Dalcin
1395808f684SSatish Balayksp.setOperators(A)
1405808f684SSatish Balayksp.setFromOptions()
1415808f684SSatish Balay
142*55a74a43SLisandro Dalcin# Since the matrix knows its size and parallel distribution, we can retrieve
143*55a74a43SLisandro Dalcin# appropriately-scaled vectors using `Mat.createVecs`. PETSc vectors are
144*55a74a43SLisandro Dalcin# objects of type `PETSc.Vec`. Here we set the right hand side of our system to
145*55a74a43SLisandro Dalcin# a vector of ones, and then solve.
146*55a74a43SLisandro Dalcin
147*55a74a43SLisandro Dalcinx, b = A.createVecs()
148*55a74a43SLisandro Dalcinb.set(1.0)
1495808f684SSatish Balayksp.solve(b, x)
1505808f684SSatish Balay
151*55a74a43SLisandro Dalcin# Finally, allow the user to print the solution by passing ``-view_sol`` to the
152*55a74a43SLisandro Dalcin# script.
1535808f684SSatish Balay
154*55a74a43SLisandro Dalcinx.viewFromOptions('-view_sol')
155*55a74a43SLisandro Dalcin
156*55a74a43SLisandro Dalcin# Things to try
157*55a74a43SLisandro Dalcin# -------------
158*55a74a43SLisandro Dalcin#
159*55a74a43SLisandro Dalcin# - Show the solution with ``-view_sol``.
160*55a74a43SLisandro Dalcin# - Show the matrix with ``-view_mat``.
161*55a74a43SLisandro Dalcin# - Change the resolution with ``-n``.
162*55a74a43SLisandro Dalcin# - Use a direct solver by passing ``-ksp_type preonly -pc_type lu``.
163*55a74a43SLisandro Dalcin# - Run in parallel on two processors using:
164*55a74a43SLisandro Dalcin#
165*55a74a43SLisandro Dalcin#   .. code-block:: console
166*55a74a43SLisandro Dalcin#
167*55a74a43SLisandro Dalcin#       mpiexec -n 2 python poisson2d.py
168