codexpast-exam-of-the-mathematics-course-of-the-year-2-of-the-natural-sciences-course-of-the-university-of-cambridge2001paper-1-time-stepping-stability.py
past-exam-of-the-mathematics-course-of-the-year-2-of-the-natural-sciences-course-of-the-university-of-cambridge/2001/paper-1-time-stepping-stability.py
"""Stability regions for decay y'=-Lambda*y, z=h*Lambda.
Python 3.14, NumPy 2.3, Matplotlib 3.10. Write PNG into cwd.
"""
from pathlib import Path
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
import numpy as np
def main():
plt.rcParams.update({"font.size": 10, "figure.facecolor": "white", "axes.facecolor": "white"})
real = np.linspace(-3.5, 4.5, 650)
imag = np.linspace(-3.5, 3.5, 600)
xx, yy = np.meshgrid(real, imag)
z = xx+1j*yy
# The second panel uses the equivalent nonsingular condition |1+z| >= 1.
levels = [np.abs(1-z), -np.abs(1+z), np.abs(1-z+z*z/2)]
limits = [1, -1, 1]
titles = ["Forward Euler", "Backward Euler", "Heun predictor-corrector"]
formulas = [r"$|1-z|\leq1$", r"$|1+z|\geq1$", r"$|1-z+z^2/2|\leq1$"]
fig, axes = plt.subplots(1, 3, figsize=(10.8, 4.1), dpi=110, layout="constrained")
for ax, values, limit, title, formula in zip(axes, levels, limits, titles, formulas):
ax.contourf(xx, yy, values, levels=[-100, limit], colors=["#c8e1ed"])
ax.contour(xx, yy, values, levels=[limit], colors=["#216b96"], linewidths=1.5)
ax.axvline(0, color="0.4", lw=0.75, ls="--")
ax.axhline(0, color="0.4", lw=0.75)
ax.set(xlim=(-3.5, 4.5), ylim=(-3.5, 3.5), xlabel=r"Re $z$", ylabel=r"Im $z$",
title=title+"\n"+formula)
ax.set_aspect("equal")
ax.set_xticks([-2, 0, 2, 4])
ax.set_yticks([-2, 0, 2])
ax.grid(alpha=0.12)
fig.suptitle(r"Shaded: bounded amplification for $y'=-\Lambda y$, $z=h\Lambda$", fontsize=12)
fig.savefig(Path(__file__).with_suffix(".png").name, facecolor="white", transparent=False)
plt.close(fig)
if __name__ == "__main__":
main()