codexion-charge-bookkeeping.py
ion-charge-bookkeeping.py
"""Count Na, Na+, Cl, and Cl- particles; write basename.png to CWD.
Tested with Python 3.14.4, NumPy 2.3.5, Matplotlib 3.10.7.
Dots are inventories, not orbital positions; neutron counts are omitted.
"""
from pathlib import Path
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
import numpy as np
def main():
cases = [(r"$\mathrm{Na}$: neutral atom", 11, 11),
(r"$\mathrm{Na}^{+}$: cation", 11, 10),
(r"$\mathrm{Cl}$: neutral atom", 17, 17),
(r"$\mathrm{Cl}^{-}$: anion", 17, 18)]
fig, axes = plt.subplots(2, 2, figsize=(9.6, 6.6))
fig.patch.set_facecolor("white")
fig.subplots_adjust(left=0.05, right=0.97, bottom=0.13, top=0.82,
wspace=0.20, hspace=0.50)
for ax, (title, protons, electrons) in zip(axes.flat, cases):
for count, y, color, name in [(protons, 2.8, "#b74632", "Protons (+e)"),
(electrons, 1.0, "#236d99", "Electrons (−e)")]:
k = np.arange(count)
ax.scatter(k % 9, y - (k // 9) * 0.45, s=115, c=color,
edgecolors="white", linewidths=0.6)
ax.text(-1.05, y - 0.2, f"{name}\n{count}", ha="right", va="center",
fontsize=11, color=color)
charge = protons - electrons
result = "0" if charge == 0 else ("+e" if charge > 0 else "−e")
ax.text(3.8, -0.65, f"Net charge: ({protons} − {electrons})e = {result}",
fontsize=12, ha="center", weight="bold")
ax.set_title(title, fontsize=14, pad=12)
ax.set_xlim(-4.4, 8.7)
ax.set_ylim(-0.95, 3.25)
ax.axis("off")
fig.suptitle("Ion charge is a particle-count imbalance", fontsize=17, y=0.96)
fig.text(0.5, 0.895, "The proton count stays fixed; one electron is removed or added.",
ha="center", fontsize=12)
fig.text(0.5, 0.045, "Each dot counts one particle. Counting rows are not atomic structure.",
ha="center", fontsize=11, color="#444444")
fig.savefig(Path.cwd() / (Path(__file__).stem + ".png"), dpi=100,
facecolor="white", transparent=False)
plt.close(fig)
if __name__ == "__main__":
main()