codex/reduced-mass-center-of-mass-relative-motion.py

reduced-mass-center-of-mass-relative-motion.py
"""Draw a two-particle coordinate split; Python 3.14, matplotlib 3.10.7, numpy 2.3.5.

Run from _media; writes the matching PNG basename to the current directory.
All curves use stipulated dimensionless parameters, not experimental data.
"""

from pathlib import Path

import matplotlib.pyplot as plt
import numpy as np


def main():
    plt.rcParams.update({"font.size": 11, "axes.spines.top": False,
                         "axes.spines.right": False, "figure.facecolor": "white",
                         "axes.facecolor": "white"})
    blue, orange, green = "#1765a0", "#b84b17", "#21804a"
    fig = plt.figure(figsize=(10, 6), layout="constrained")
    grid = fig.add_gridspec(2, 2, height_ratios=(0.9, 1.4))
    geometry = fig.add_subplot(grid[0, :])
    geometry.set(xlim=(-1.12, 0.75), ylim=(-0.7, 0.78))
    geometry.axis("off")
    geometry.set_title("One pair, two coordinates: centre R and separation r", pad=10)
    geometry.plot([-0.75, 0.25], [0, 0], color="#707070", lw=2, zorder=1)
    geometry.scatter([-0.75, 0.25], [0, 0], s=[400, 750], c=[blue, orange], zorder=3)
    geometry.scatter([0], [0], marker="x", color=green, s=90, lw=3, zorder=4)
    geometry.text(-0.75, 0.3, r"$m_1$ at $x_1=R-3r/4$", ha="center", color=blue)
    geometry.text(0.25, 0.3, r"$m_2=3m_1$ at $x_2=R+r/4$", ha="center", color=orange)
    geometry.text(0, -0.22, r"$R$", ha="center", color=green)
    geometry.annotate("", xy=(0.25, -0.48), xytext=(-0.75, -0.48),
                      arrowprops={"arrowstyle": "->", "lw": 2, "color": "#333333"})
    geometry.text(-0.25, -0.65, r"$r=x_2-x_1$  (arrow from body 1 to body 2)", ha="center")
    tau = np.linspace(0, 4 * np.pi, 500)
    extension = 0.3 * np.cos(tau)
    center = 0.08 * tau
    x1 = center - 0.75 * (1 + extension)
    x2 = center + 0.25 * (1 + extension)
    lab = fig.add_subplot(grid[1, 0])
    lab.plot(tau, x1, color=blue, lw=2, label=r"$x_1/\ell$")
    lab.plot(tau, x2, color=orange, lw=2, label=r"$x_2/\ell$")
    lab.plot(tau, center, color=green, lw=2, ls="--", label=r"$R/\ell$")
    lab.set(title="Laboratory positions: drift + vibration", ylabel="Position / equilibrium length")
    lab.legend(loc="upper left", fontsize=10)
    internal = fig.add_subplot(grid[1, 1])
    internal.plot(tau, -0.75 * extension, color=blue, lw=2, label=r"$u_1=-3s/4$")
    internal.plot(tau, 0.25 * extension, color=orange, lw=2, label=r"$u_2=s/4$")
    internal.axhline(0, color="#777777", lw=1)
    internal.set(title="Internal displacements: translation removed",
                 ylabel=r"Displacement from equilibrium / $\ell$", ylim=(-0.31, 0.31))
    internal.legend(loc="upper left", fontsize=10)
    for ax in (lab, internal):
        ax.set(xlabel=r"Phase time $\tau=\omega t$", xlim=(0, 4 * np.pi))
        ax.set_xticks([0, np.pi, 2 * np.pi, 3 * np.pi, 4 * np.pi],
                      ["0", r"$\pi$", r"$2\pi$", r"$3\pi$", r"$4\pi$"])
        ax.grid(alpha=0.2)
    fig.savefig(Path.cwd() / (Path(__file__).stem + ".png"), dpi=110,
                facecolor="white", transparent=False)
    plt.close(fig)


if __name__ == "__main__":
    main()