codexoil-drop-charge-jump-series.py
oil-drop-charge-jump-series.py
"""Original simulated charge-state readings; Python 3.14, NumPy 2.3.5, Matplotlib 3.10.7.
Run from _media; emits oil-drop-charge-jump-series.png in the working directory.
No historical measurements or SI elementary-charge estimate are used.
"""
from pathlib import Path
import matplotlib.pyplot as plt
import numpy as np
def main():
means = np.array([2.03, 2.96, 5.02, 3.99])
offsets = np.array([-0.18, 0.10, -0.04, 0.22, -0.12, 0.02])
assert np.isclose(offsets.sum(), 0)
sigma = 0.08 # Stipulated uncertainty of each state mean, not an estimate.
plt.rcParams.update({"font.size": 11, "axes.spines.top": False,
"axes.spines.right": False, "figure.facecolor": "white"})
fig, (ax, jumps) = plt.subplots(2, 1, figsize=(9, 6.4),
gridspec_kw={"height_ratios": [2.4, 1.2]})
colors = ["#1864ab", "#087f5b", "#9c36b5", "#d9480f"]
for j, (mean, color) in enumerate(zip(means, colors)):
x = np.arange(1, 7) + 7 * j
readings = mean + np.roll(offsets, j)
assert np.isclose(readings.mean(), mean)
ax.plot(x, readings, "o-", color=color, ms=4, lw=0.7)
ax.hlines(mean, x[0] - 0.3, x[-1] + 0.3, color=color, lw=1.8)
ax.fill_between([x[0] - 0.3, x[-1] + 0.3], mean - sigma,
mean + sigma, color=color, alpha=0.12)
ax.text(x.mean(), mean + 0.38, f"State {j + 1}: mean {mean:.2f}",
ha="center", color=color, fontsize=10)
ax.set(xlim=(0, 29), ylim=(1.5, 5.8), ylabel=r"Inferred $|q|/Q_*$",
xlabel="Acquisition order (not elapsed time)")
ax.grid(axis="y", alpha=0.18)
ax.text(0.02, 0.96, "Chosen noisy readings; shaded mean ± 0.08",
transform=ax.transAxes, va="top", fontsize=10)
delta = np.diff(means)
jumps.axhline(0, color="0.6", lw=0.8)
jumps.errorbar([1, 2, 3], delta, yerr=np.sqrt(2) * sigma, fmt="o",
color="#343a40", capsize=5)
for j, d in enumerate(delta):
jumps.text(j + 1, d + 0.25, f"{d:+.2f}", ha="center")
jumps.set(xticks=[1, 2, 3], xticklabels=["1 → 2", "2 → 3", "3 → 4"],
xlim=(0.5, 3.5), ylim=(-1.5, 2.75),
ylabel=r"Change in $|q|/Q_*$", xlabel="Transition between state means")
jumps.grid(axis="y", alpha=0.18)
fig.suptitle("Simulated charge states of one oil drop", fontsize=16)
fig.text(0.5, 0.015, r"Arbitrary reference charge $Q_*$; no historical data or numerical estimate of $e$",
ha="center", fontsize=10)
fig.tight_layout(rect=(0, 0.04, 1, 0.95), h_pad=1.5)
fig.savefig(Path(__file__).stem + ".png", dpi=130, facecolor="white")
plt.close(fig)
if __name__ == "__main__":
main()