codexbuffer-neutralization-inventory.py
buffer-neutralization-inventory.py
"""Show dominant neutralization inventories; write basename.png in cwd.
Tested with Python 3.14.4, NumPy 2.3.5, and Matplotlib 3.10.7.
Toy pKa = 5, initial volume 1 L, negligible added volume. Stoichiometric
inventories neglect tiny free-ion amounts and equilibration corrections;
pH also neglects activity coefficients. This is not exact speciation.
"""
from pathlib import Path
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
import numpy as np
def main():
before = np.array([0.10, 0.10])
hydroxide = 0.01
after = before + hydroxide * np.array([-1, 1])
ph = 5.0 + np.log10(after[1] / after[0])
fig, axes = plt.subplots(1, 2, figsize=(9.6, 4.8), sharey=True)
fig.patch.set_facecolor("white")
fig.subplots_adjust(left=0.085, right=0.97, bottom=0.24, top=0.72, wspace=0.18)
for ax, amounts, title in zip(axes, (before, after),
("Before addition: pH ≈ 5.00000", f"After neutralization: pH ≈ {ph:.5f}")):
bars = ax.bar([0, 1], amounts, width=0.58, color=["#b6531f", "#236d99"])
ax.bar_label(bars, labels=[f"{v:.2f} mol" for v in amounts], padding=6, fontsize=12,
bbox={"facecolor": "white", "edgecolor": "none", "pad": 1})
ax.set_xticks([0, 1], labels=[r"Weak acid $HA$", r"Conjugate base $A^-$"], fontsize=11)
ax.set_ylim(0, 0.14)
ax.set_yticks([0, 0.05, 0.10])
ax.set_title(title, fontsize=12, pad=13)
ax.axhline(0.10, color="#777777", ls="--", lw=1, zorder=0)
ax.grid(axis="y", alpha=0.2)
ax.set_axisbelow(True)
ax.spines[["top", "right"]].set_visible(False)
axes[0].set_ylabel("Approximate partner inventory (mol)", fontsize=11)
fig.suptitle(r"Toy buffer: $HA+OH^-\longrightarrow A^-+H_2O$", fontsize=15, y=0.97)
fig.text(0.5, 0.85, "Add 0.010 mol OH⁻ to 1 L; stipulated pKa = 5; negligible added volume", ha="center", fontsize=11)
fig.text(0.5, 0.12, "0.010 mol transfers from acid to base; total partner inventory stays 0.20 mol.", ha="center", fontsize=11)
fig.text(0.5, 0.065, "Dominant stoichiometry + ideal concentration approximation; not exact speciation.", ha="center", fontsize=10, color="#444444")
output = Path.cwd() / (Path(__file__).stem + ".png")
fig.savefig(output, dpi=120, facecolor="white", transparent=False)
plt.close(fig)
print(f"Wrote {output.name}; final inventories {after.tolist()}; calculated pH {ph:.12f}")
if __name__ == "__main__":
main()