codexangular-rate-versus-speed.py
angular-rate-versus-speed.py
"""Generate angular-rate-versus-speed.png in the current working directory.
Use: make _media/angular-rate-versus-speed.png
Tested with Python 3.14.4, NumPy 2.3.5, Matplotlib 3.10.7.
Computed nonrelativistic transverse speeds for total proper motion 100 mas/year;
parallaxes use the existing parsec small-angle distance relation.
"""
import matplotlib.pyplot as plt
import numpy as np
def main():
plt.rcParams.update({"font.size": 13, "savefig.facecolor": "white"})
conversion, mu_arcsec = 4.74047, 0.100
distance = np.linspace(0, 110, 301)
fig, ax = plt.subplots(figsize=(9.5, 5.8), facecolor="white")
fig.subplots_adjust(left=0.12, right=0.96, bottom=0.18, top=0.80)
fig.suptitle("The same angular rate can mean very different speeds", fontsize=18, y=0.97)
fig.text(0.5, 0.895, r"Total proper motion fixed at 100 mas/year: $v_\perp = 4.74047\times0.100\times d$ km/s",
ha="center", fontsize=13)
ax.plot(distance, conversion * mu_arcsec * distance, lw=2.8, color="#2368ac")
for d, label_xy, color in ((10, (25, 5.5), "#453778"),
(20, (38, 23), "#b85b15"),
(100, (64, 46), "#2368ac")):
speed = conversion * mu_arcsec * d
p_mas = 1000 / d
ax.vlines(d, 0, speed, color=color, lw=1.2, ls=":")
ax.scatter(d, speed, s=85, color=color, edgecolors="white", zorder=4)
label = f"{d} pc → {speed:.2f} km/s\nParallax: {p_mas:.0f} mas"
ax.annotate(label, xy=(d, speed), xytext=label_xy, fontsize=12, color=color,
va="center", bbox={"facecolor": "white", "edgecolor": "none", "pad": 3},
arrowprops={"arrowstyle": "-", "color": color, "lw": 1.2})
ax.set(xlabel="Distance (pc)", ylabel="Transverse speed (km/s)",
xlim=(0, 110), ylim=(0, 58), xticks=[0, 10, 20, 40, 60, 80, 100],
yticks=np.arange(0, 60, 10))
ax.grid(alpha=0.20)
ax.set_facecolor("white")
ax.spines[["top", "right"]].set_visible(False)
fig.text(0.5, 0.045, "Computed examples; only the velocity across the line of sight is shown.",
ha="center", fontsize=11)
fig.savefig("angular-rate-versus-speed.png", dpi=120, facecolor="white", transparent=False)
plt.close(fig)
if __name__ == "__main__":
main()