codextorsion-balance-period-calibration.py
torsion-balance-period-calibration.py
"""Original undamped restoring-torque calibration; output PNG basename to CWD.
Tested with Python 3.14.4, NumPy 2.3.5, Matplotlib 3.10.7.
No gravitational-torque gradient, damping or rod inertia is included.
"""
from pathlib import Path
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
import numpy as np
def main():
m, L, kappa = .010, .10, 1e-8
inertia = 2 * m * L**2
period = 2 * np.pi * np.sqrt(inertia / kappa)
plt.rcParams.update({"font.size": 11, "figure.facecolor": "white"})
fig, (motion, calibration) = plt.subplots(1, 2, figsize=(10.4, 5.1), dpi=120)
fig.subplots_adjust(left=.085, right=.97, bottom=.24, top=.77, wspace=.32)
fig.suptitle("Period calibrates the fiber's restoring torque", fontsize=16, y=.96)
fig.text(.5, .85, r"$I\ddot\phi=-\kappa\phi$ "
r"$T=2\pi\sqrt{I/\kappa}$ "
r"$\kappa=4\pi^2 I/T^2$", ha="center", fontsize=14)
time = np.linspace(0, 2.3 * period, 1000)
amplitude = .002 # A small perturbation about equilibrium, in radians.
phi = amplitude * np.cos(2 * np.pi * time / period)
motion.plot(time / 60, phi * 1000, color="#245f9b", lw=2)
motion.axhline(0, color="#666666", lw=.8)
motion.axvline(period / 60, color="#245f9b", ls=":", lw=1)
motion.annotate("", (period / 60, 2.55), (0, 2.55),
arrowprops={"arrowstyle": "<->", "color": "#245f9b"})
motion.text(period / 120, 2.8, f"T = {period / 60:.2f} min",
color="#245f9b", ha="center", fontsize=11)
motion.set(xlabel="Time (min)",
ylabel=r"Departure from equilibrium $\phi$ (mrad)",
ylim=(-2.6, 3.3), xlim=(0, time[-1] / 60),
title="Small undamped oscillation")
spring_constants = np.linspace(.25e-8, 4e-8, 400)
periods = 2 * np.pi * np.sqrt(inertia / spring_constants)
calibration.plot(spring_constants / 1e-8, periods / 60,
color="#187b58", lw=2)
calibration.plot(1, period / 60, "o", color="#b95d16", ms=7)
calibration.annotate(f"Toy point\n{period:.1f} s", (1, period / 60),
(1.65, 21), fontsize=11, color="#b95d16",
arrowprops={"arrowstyle": "->", "color": "#b95d16"})
calibration.set(xlabel=r"Torsion constant $\kappa$ ($10^{-8}$ N m/rad)",
ylabel="Undamped period T (min)",
title="Fixed moment of inertia", xlim=(.2, 4.1), ylim=(5, 32))
for ax in (motion, calibration):
ax.set_facecolor("white")
ax.spines[["top", "right"]].set_visible(False)
ax.grid(alpha=.18)
fig.text(.5, .11, r"Toy inertia: $I=2mL^2=2.0\times10^{-4}$ kg m$^2$; "
r"$\kappa=10^{-8}$ N m/rad.", ha="center", fontsize=11)
fig.text(.5, .045, "Restoring-torque calibration model; no damping or changing source torque.",
ha="center", fontsize=10, color="#555555")
fig.savefig(Path.cwd() / (Path(__file__).stem + ".png"),
facecolor="white", transparent=False)
plt.close(fig)
if __name__ == "__main__":
main()