codexlinear-drag-terminal-velocity.py
linear-drag-terminal-velocity.py
"""Generate linear-drag-terminal-velocity.png in the working directory.
Uses only the repository's NumPy and Matplotlib dependencies.
Tested with Python 3.14.4, NumPy 2.3.5, Matplotlib 3.10.7.
Run from _media: python ../linear-drag-terminal-velocity.py
"""
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
import numpy as np
def main():
mass, friction, gravity = 1.0, 2.0, 9.8
tau, terminal = mass / friction, mass * gravity / friction
t = np.linspace(0, 3, 601)
velocity = terminal * (1 - np.exp(-t / tau))
acceleration = gravity * np.exp(-t / tau)
plt.rcParams.update({"font.size": 12, "savefig.facecolor": "white"})
fig, axes = plt.subplots(1, 2, figsize=(10.8, 5.5), facecolor="white")
fig.subplots_adjust(left=0.08, right=0.97, bottom=0.20, top=0.76, wspace=0.28)
fig.suptitle("Linear drag: motion persists as acceleration fades", fontsize=18, y=0.97)
fig.text(0.5, 0.88, "From rest; downward positive; M = 1 kg, b = 2 kg/s, g = 9.8 m/s²", ha="center", fontsize=13)
v1, a1 = terminal * (1 - np.exp(-1 / tau)), gravity * np.exp(-1 / tau)
axes[0].plot(t, velocity, color="#2368ac", lw=2.7)
axes[0].axhline(terminal, color="#666666", ls="--", lw=1.4)
axes[0].text(1.6, terminal - 0.5, r"$v_T=4.9$ m/s", fontsize=12)
axes[1].plot(t, acceleration, color="#bf5224", lw=2.7)
for ax, value, color, label, text_xy in (
(axes[0], v1, "#2368ac", f"1 s: {v1:.3f} m/s", (1.2, 2.9)),
(axes[1], a1, "#bf5224", f"1 s: {a1:.3f} m/s²", (1.1, 4.8)),
):
ax.axvline(tau, color="#8a8a8a", ls=":", lw=1.2)
ax.scatter(1, value, color=color, edgecolors="white", s=65, zorder=4)
ax.annotate(label, xy=(1, value), xytext=text_xy, color=color,
arrowprops={"arrowstyle": "-", "color": color, "lw": 1.1})
ax.set(xlim=(0, 3), xlabel="Time (s)", xticks=[0, 0.5, 1, 2, 3])
ax.grid(alpha=0.18)
ax.spines[["top", "right"]].set_visible(False)
ax.set_facecolor("white")
axes[0].set(ylim=(0, 5.4), ylabel="Downward velocity (m/s)")
axes[1].set(ylim=(0, 10.5), ylabel="Downward acceleration (m/s²)")
axes[0].set_title(r"$v(t)=v_T(1-e^{-t/\tau})$", fontsize=14, pad=13)
axes[1].set_title(r"$a(t)=g e^{-t/\tau}$", fontsize=14, pad=13)
fig.text(0.5, 0.07, "Dotted lines: τ = 0.5 s. Illustrative constant linear drag model; buoyancy neglected.",
ha="center", fontsize=11)
fig.savefig("linear-drag-terminal-velocity.png", dpi=120, facecolor="white", transparent=False)
plt.close(fig)
if __name__ == "__main__":
main()