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