1D heat diffusion

16. 1D heat diffusion#

Uses the Python interface to PETSc (petsc4py) to solve the transient 1D heat
diffusion with Dirichlet Boundary Conditions. This is a super simple example
showcase of the linear iterative solvers PETSc has to offer. It runs
sequentially without MPI.

Consider the 1D heat equation defining how a temperature is distributed e.g.
over a one-dimensional rod.

    ∂u/∂t = ∂²u/∂x²

with Dirichlet Boundary Conditions at both ends of a unit domain

    u(t, x=0) = 0 = u(t, x=1)

and the following initial condition

         ┌────────────────────────────────────────┐   
         │⢸⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢰⠒⠒⠒⠒⠒⠒⢲⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ │ y1
         │⢸⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⠀⠀⠀⠀⠀⠀⢸⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ ⠀│   
         │⢸⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⠀⠀⠀⠀⠀⠀⢸⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ ⠀│   
         │⢸⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⠀⠀⠀⠀⠀⠀⢸⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ ⠀│   
         │⢸⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⠀⠀⠀⠀⠀⠀⢸⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ ⠀│   
         │⢸⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⠀⠀⠀⠀⠀⠀⢸⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ ⠀│   
         │⢸⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⠀⠀⠀⠀⠀⠀⢸⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ ⠀│   
         │⢸⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⠀⠀⠀⠀⠀⠀⢸⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ ⠀│   
         │⢸⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⠀⠀⠀⠀⠀⠀⢸⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ ⠀│   
         │⢸⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⠀⠀⠀⠀⠀⠀⢸⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ ⠀│   
         │⢸⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⠀⠀⠀⠀⠀⠀⢸⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ ⠀│   
         │⢸⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⠀⠀⠀⠀⠀⠀⢸⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ ⠀│   
         │⢸⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡸⠀⠀⠀⠀⠀⠀⢸⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ ⠀│   
         │⢸⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⢸⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ ⠀│   
         │⢼⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠧⠤⠤⠤⠤⠤⠤⠼⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤│   
         └────────────────────────────────────────┘   
          0.0                                   1.0

-----

Solution strategy (BTCS):

Denote with û the solution at the next point in time. Then use
Finite Difference discretization on a uniform mesh. Discretize
the time derivative by implicit Euler and the second spatial
derivative by central differences.

    (û[i] − u[i])/Δt = (û[i−1] − 2 û[i] + û[i+1])/Δx²

with the conditions on the Dirichlet Boundary points

    û[0] = 0.0

    û[-1] = 0.0

Move all points at next time step to the lhs and all terms at
previous time step to rhs.

    û[i] + Δt/Δx² (− û[i−1] + 2 û[i] − û[i+1]) = u[i]

This can be expressed as a linear system of equations

    A x = b

with A being

    +-----                                                ------+
    |   1.0                                                     |
    | −Δt/Δx²  1 + 2Δt/Δx²  −Δt/Δx²                             |
    |           −Δt/Δx²  1 + 2Δt/Δx²  −Δt/Δx²
A = |                       −Δt/Δx²  1 + 2Δt/Δx²  −Δt/Δx²
    |                           ⋱         ⋱         ⋱
    |                             ⋱         ⋱         ⋱
    |                            −Δt/Δx²  1 + 2Δt/Δx²  −Δt/Δx²  |
    |                                                    1.0    |
    +-----                                                ------+

A therefore has the following sparsity structure

    +---------------+
    |*              |
    |* * *          |
    |  * * *        |
A = |    * * *      |
    |      * * *    |
    |        * * *  |
    |          * * *|
    |              *|
    +---------------+

which is not fully tri-diagonal, because we did not eliminate the
Dirichlet DoF. Hence, it is also not symmetric.

-----

The PETSc linear solver can be set at runtime.

Use this command to monitor the solution process:

    python petsc_python_simple_heat_diffusion.py -ksp_monitor

Use this flag to swap the linear solver

    python petsc_python_simple_heat_diffusion.py -ksp_type [SOLVER_NAME]
from petsc4py import PETSc, init
import sys
import numpy as np
import matplotlib.pyplot as plt
[45f1bc1db132:04391] shmem: mmap: an error occurred while determining whether or not /tmp/ompi.45f1bc1db132.33333/jf.0/208076800/shared_mem_cuda_pool.45f1bc1db132 could be created.
[45f1bc1db132:04391] create_and_attach: unable to create shared memory BTL coordinating structure :: size 134217728 
init(sys.argv)
N_POINTS = 1001
TIME_STEP_LENGTH = 0.005
N_TIME_STEPS = 10
def main():
    element_length = 1.0 / (N_POINTS - 1)
    mesh = np.linspace(0.0, 1.0, N_POINTS)

    # Create a new sparse PETSc matrix, fill it and then assemble it
    A = PETSc.Mat().createAIJ([N_POINTS, N_POINTS])
    A.setUp()

    diagonal_entry = 1.0 + 2.0 * TIME_STEP_LENGTH / element_length**2
    off_diagonal_entry = - 1.0 * TIME_STEP_LENGTH / element_length**2

    A.setValue(0, 0, 1.0)
    A.setValue(N_POINTS-1, N_POINTS-1, 1.0)

    for i in range(1, N_POINTS - 1):
        A.setValue(i, i, diagonal_entry)
        A.setValue(i, i-1, off_diagonal_entry)
        A.setValue(i, i+1, off_diagonal_entry)
    
    A.assemble()

    # Define the initial condition
    initial_condition = np.where(
        (mesh > 0.3) & (mesh < 0.5),
        1.0,
        0.0,
    )

    # Assemble the initial rhs to the linear system
    b = PETSc.Vec().createSeq(N_POINTS)
    b.setArray(initial_condition)
    b.setValue(0, 0.0)
    b.setValue(N_POINTS-1, 0.0)

    # Allocate a PETSc vector storing the solution to the linear system
    x = PETSc.Vec().createSeq(N_POINTS)

    # Instantiate a linear solver: Krylow subspace linear iterative solver
    ksp = PETSc.KSP().create()
    ksp.setOperators(A)
    ksp.setFromOptions()

    chosen_solver = ksp.getType()
    print(f"Solving with {chosen_solver:}")

    plt.plot(mesh, initial_condition)
    for iter in range(N_TIME_STEPS):
        ksp.solve(b, x)

        # Re-assemble the rhs to move forward in time
        current_solution = x.getArray()
        b.setArray(current_solution)
        b.setValue(0, 0.0)
        b.setValue(N_POINTS - 1, 0.0)

        # Visualize
        plt.plot(mesh, current_solution)
        plt.draw()
        plt.pause(0.5)

    plt.show()
main()
Solving with gmres
_images/a50f947f25991d2b33ddbf34cc360ba49362253ca90cbeb8da5c5c25e965fe4c.png _images/3978e3b68a31f941288a4826c9fcae0c2394b8a4e277b23b925e881f85fefc4f.png _images/e68135a65a867d6b15e9f9a5d62dd549122594ad4f57a1edcbaedd6a763d755e.png _images/0877c99ecea6424989d455df57cfbb6a96addd4b5c9485d8d330aad435cdf036.png _images/80ee6b65df523f12a9093466280e71fea0a8b8dc035f8522d2b15ba2ef2d3567.png _images/5dc60bd459c54654dd303f51688f412304b8ee1a1b1323967914869b71ded670.png _images/2c201b2aace2128969b1939045e550e38a365d5b9da81b0d702c562fa26a1872.png _images/03b5b3ad76d522bcb4e2e67cb2aa776bd2e74ee53bb2353d1da1cd4b5697cc19.png _images/472030e91eb73a03f07a1f51b5b3c55fe477cc5d4ab87f7d96fa38868ba40441.png _images/a54bb700ded368c7643aaf17977e40e203548549e398195606043085434e5c54.png