codexwave-equation-countertravelling-pulse-components.py
wave-equation-countertravelling-pulse-components.py
"""Synthetic wave solutions; tested with Python 3.14, matplotlib 3.10.7, numpy 2.3.5.
Run from _media; writes the matching PNG basename in the current directory.
"""
from pathlib import Path
import matplotlib.pyplot as plt
import numpy as np
def bump(x):
"""C-infinity compact pulse, support [-1, 1], peak one."""
x = np.asarray(x, dtype=float)
y = np.zeros_like(x)
inside = np.abs(x) < 1
y[inside] = np.exp(1 - 1 / (1 - x[inside] ** 2))
return y
def main():
plt.rcParams.update({"font.size": 11, "axes.spines.top": False,
"axes.spines.right": False, "figure.facecolor": "white",
"axes.facecolor": "white"})
x = np.linspace(-3.2, 3.2, 1601)
fig, axes = plt.subplots(3, 2, figsize=(10.8, 7.4), sharex=True, sharey=True)
for row, t in enumerate((0, 0.5, 1.5)):
for col in (0, 1):
ax = axes[row, col]
right = (0.5 if col == 0 else 1) * bump(x - t)
left = 0.5 * bump(x + t) if col == 0 else np.zeros_like(x)
ax.plot(x, right + left, color="#242424", lw=2.5, label="Total displacement")
ax.plot(x, left, color="#cf4b39", lw=2, linestyle=":", label="Left component G(x+t)")
ax.plot(x, right, color="#1976a3", lw=1.8, linestyle="--", label="Right component F(x−t)")
ax.set_ylim(-0.06, 1.14)
ax.set_xlim(-3.2, 3.2)
ax.set_yticks((0, 0.5, 1))
ax.grid(alpha=0.2)
ax.text(0.025, 0.86, f"t = {t:g}", transform=ax.transAxes,
bbox={"facecolor": "white", "edgecolor": "none"})
if col == 0:
ax.set_ylabel("Displacement u / A")
if row == 2:
ax.set_xlabel("Position x / a")
if col == 0:
ax.annotate("Left", (-1.5, 0.5), (-2.5, 0.85),
arrowprops={"arrowstyle": "->", "color": "#cf4b39"}, color="#af392c")
ax.annotate("Right", (1.5, 0.5), (2, 0.85),
arrowprops={"arrowstyle": "->", "color": "#1976a3"}, color="#1976a3")
else:
ax.annotate("Right", (1.5, 1), (2.2, 0.65),
arrowprops={"arrowstyle": "->", "color": "#1976a3"}, color="#1976a3")
axes[0, 0].set_title("Release from rest: g = 0\nF = f / 2, G = f / 2", pad=10)
axes[0, 1].set_title("Launch to the right: g = −c f′\nF = f, G = 0", pad=10)
handles, labels = axes[0, 0].get_legend_handles_labels()
fig.legend(handles, labels, loc="lower center", ncol=3, frameon=False, fontsize=10,
bbox_to_anchor=(0.5, 0.045))
fig.suptitle("Two directions of travel from the same initial shape", fontsize=16, y=0.985)
fig.text(0.5, 0.016, "Synthetic analytical solutions: f(x) = b(x), c = a = A = 1; time in units a / c.",
ha="center", fontsize=10, color="#555555")
fig.subplots_adjust(left=0.08, right=0.98, bottom=0.15, top=0.84, hspace=0.24, wspace=0.15)
fig.savefig(Path.cwd() / (Path(__file__).stem + ".png"), dpi=110, facecolor="white", transparent=False)
plt.close(fig)
if __name__ == "__main__":
main()