codexcentrifuge-radius-speed-relative-acceleration.py
centrifuge-radius-speed-relative-acceleration.py
"""Theoretical relative acceleration curves for stipulated rotor radii.
Tested with Python 3.14, matplotlib 3.10.7 and numpy 2.3.5.
Run from _media; writes the matching PNG to the current directory.
"""
from pathlib import Path
import matplotlib.pyplot as plt
import numpy as np
STANDARD_GRAVITY = 9.80665 # m/s^2, conventional reference value
def relative_acceleration(radius_cm, rpm):
return (np.asarray(radius_cm) / 100) * (2 * np.pi * np.asarray(rpm) / 60) ** 2 / STANDARD_GRAVITY
def main():
plt.rcParams.update({"font.size": 11, "font.family": "DejaVu Sans"})
fig, ax = plt.subplots(figsize=(7.5, 4.7), dpi=120)
fig.patch.set_facecolor("white")
rpm = np.linspace(0, 6000, 301)
for radius_cm, color in [(5, "#2563a6"), (10, "#be4933"), (15, "#258363")]:
ax.plot(rpm, relative_acceleration(radius_cm, rpm), color=color, lw=2.3,
label=f"r = {radius_cm} cm")
if radius_cm in (5, 10):
value = float(relative_acceleration(radius_cm, 3000))
ax.scatter([3000], [value], color=color, s=35, zorder=5)
ax.annotate(f"{value:.0f} × g", (3000, value), (3210, value - 80), color=color)
ax.axvline(3000, color="#64748b", linestyle="--", linewidth=1)
ax.set(xlabel="Rotational speed (rpm)",
ylabel=r"Relative centrifugal force, $a/g_0$ (multiples of g)",
xlim=(0, 6000), ylim=(0, 6500))
ax.set_title("Same rpm, different acceleration", loc="left", weight="bold", pad=12)
ax.text(0.04, 0.92, r"$a/g_0 = r(2\pi n/60)^2/g_0$", transform=ax.transAxes)
ax.legend(loc="upper left", bbox_to_anchor=(0.02, 0.86), frameon=False)
ax.grid(alpha=0.2)
ax.spines[["top", "right"]].set_visible(False)
fig.text(0.5, 0.025, "Stipulated radii; theoretical curves, not instrument measurements or operating limits",
ha="center", fontsize=9)
fig.subplots_adjust(left=0.13, bottom=0.16, top=0.88, right=0.97)
fig.savefig(Path.cwd() / (Path(__file__).stem + ".png"), facecolor="white", transparent=False)
plt.close(fig)
if __name__ == "__main__":
main()