codexlinear-ode-superposition.py
linear-ode-superposition.py
"""Compare homogeneous superposition with a fixed forced solution family.
Tested with Python 3.14.4, NumPy 2.3.5 and Matplotlib 3.10.7.
Run from the desired output directory; writes linear-ode-superposition.png.
All curves use exact formulas; t and y are dimensionless.
"""
from pathlib import Path
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
import numpy as np
def main():
t = np.linspace(0.0, 2.5, 501)
decay = np.exp(-2.0 * t)
blue, green, orange, red = "#2166ac", "#258568", "#c36b13", "#b2182b"
plt.rcParams.update({"font.size": 11, "axes.spines.top": False,
"axes.spines.right": False})
fig, axes = plt.subplots(1, 2, figsize=(11.2, 4.9), dpi=120,
facecolor="white", constrained_layout=True)
ax = axes[0]
for curve, color, label in (
(decay, blue, r"$y_1=e^{-2t}$"),
(2 * decay, orange, r"$y_2=2e^{-2t}$"),
(3 * decay, green, r"$y_1+y_2=3e^{-2t}$"),
):
ax.plot(t, curve, color=color, lw=2.6, label=label)
ax.scatter([0], [curve[0]], color=color, s=24, zorder=4)
ax.set(title=r"Homogeneous: $y'+2y=0$", ylim=(-0.08, 3.15))
ax.legend(loc="upper right", framealpha=1)
ax.text(0.97, 0.58, "All three curves solve\nthe same equation.",
transform=ax.transAxes, ha="right", va="center", fontsize=10)
ax = axes[1]
u0, u1, u2 = 0.5 - 0.5 * decay, 0.5 + 0.5 * decay, 0.5 + 1.5 * decay
ax.fill_between(t, 0.5, u2, color=orange, alpha=0.12)
for curve, color, label in (
(u0, blue, r"$u_0$: $y(0)=0$"),
(u1, green, r"$u_1=(u_0+u_2)/2$: $y(0)=1$"),
(u2, orange, r"$u_2$: $y(0)=2$"),
):
ax.plot(t, curve, lw=2.6, color=color, label=label)
ax.scatter([0], [curve[0]], color=color, s=24, zorder=4)
ax.axhline(0.5, color="#555555", lw=1.6, ls=":",
label=r"particular solution $y_p=1/2$")
ax.plot(t, u0 + u2, color=red, lw=2.5, ls="--",
label=r"$u_0+u_2$: forcing $2$, not $1$")
ax.set(title=r"Fixed forcing: $y'+2y=1$", ylim=(-0.08, 2.45))
ax.legend(loc="upper right", fontsize=9.2, framealpha=1)
sample = 0.75
top = 0.5 + 1.5 * np.exp(-2 * sample)
ax.annotate("", xy=(sample, top), xytext=(sample, 0.5),
arrowprops={"arrowstyle": "<->", "color": orange, "lw": 1.4})
ax.text(1.06, 0.76, r"shaded deviation: $\frac{3}{2}e^{-2t}$",
fontsize=9.5, color="#92500e")
for ax in axes:
ax.set(xlabel="t (dimensionless)", ylabel="y (dimensionless)",
xlim=(-0.04, 2.5))
ax.set_facecolor("white")
ax.grid(alpha=0.16)
fig.savefig(Path.cwd() / "linear-ode-superposition.png",
facecolor="white", transparent=False)
plt.close(fig)
if __name__ == "__main__":
main()