Heat equation in 1D

To verify our Laplacian calculation, we solve the heat equation in one dimension with zero flux at the boundaries. The initial condition is a cosine, which yields the trivial analytical solution:

\(f(x, t) = \cos(2\pi x)e^{-4\pi^2 t}\).

[1]:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import numpy as np
import matplotlib.pyplot as plt

x, dx = np.linspace(0, 1, 60, retstep=True)
dt = 0.5 * dx**2
print(f"{dt = :.3e}")

# Initial condition
n_periods = 1
f_0 = np.cos(2 * n_periods * np.pi * x)


def f_ana(t: float) -> np.ndarray:
    """
    Retunrs the analytical solution:
    f(x, t) = cos(2πx) * exp(-4π²t)
    """
    return np.exp(-(2 * n_periods * np.pi)**2 * t) * f_0

dt = 1.436e-04

A temperature update function is created.

[2]:
def update(f):
    """Met à jour la solution numérique en calculant le laplacien 1D"""
    f_moins = np.concatenate((f[1:2], f[:-1]))
    f_plus = np.concatenate((f[1:], f[-2:-1]))
    laplacien = (f_moins - 2 * f + f_plus) / dx**2
    f += dt * laplacien

We define a time generator.

[3]:
def time_generator(N):
    """
    Générateur de temps qui s'arrête à N ou quand t vaut
    1 / (2 * n_periodes * np.pi)**2
    """
    for i in range(N):
        t = i * dt
        if t > 1 / (2 * n_periods * np.pi)**2:
            return
        else:
            yield t

Initialize the figure and run the animation.

[4]:
%matplotlib notebook

fig = plt.figure()
N = 5000

f = f_0.copy()
line, = plt.plot(x, f, label="numerical")
ana, = plt.plot(x, f_ana(0.), '+', label=r'$\cos(2\pi x)e^{-4\pi^2 t}$')
ax = fig.get_axes()[0]
ax.legend()
ax.set_xlabel("x")


def animate(t):

    # Flux is zero at the edges, so the integral must remain constant
    # We calculate the integral using the midpoint method
    integral = (f[1:-1].sum() + 0.5 * (f[0] + f[-1])) * dx
    error = np.linalg.norm(f - f_ana(t), ord=2)
    title = f"{t = :.3e} {integral = :.3e} {error = :.3e}"
    fig.suptitle(title)

    update(f)

    line.set_ydata(f)
    ana.set_ydata(f_ana(t))

    return line, ana


ani = animation.FuncAnimation(fig, animate, frames=time_generator(N),
                              interval=1, blit=True, repeat=False,
                              cache_frame_data=False)

from IPython.display import HTML
HTML(ani.to_jshtml())
[4]: