codex/electrolysis-copper-process.py

electrolysis-copper-process.py
"""Original copper-transfer schematic; writes its PNG basename to CWD.

Tested with Python 3.14.4, matplotlib 3.10.7, numpy 2.3.5.
Run from _media with MPLBACKEND=Agg and a writable MPLCONFIGDIR.
"""
from pathlib import Path
import matplotlib.pyplot as plt
from matplotlib.patches import Rectangle, FancyArrowPatch

plt.rcParams.update({"font.family": "DejaVu Sans", "font.size": 11})
fig, ax = plt.subplots(figsize=(10, 6.4), dpi=120, facecolor="white")
ax.set(xlim=(0, 10), ylim=(0, 6.4))
ax.axis("off")
ink, blue, copper = "#243544", "#237b9b", "#bb7447"

def text(x, y, s, **kwargs):
    ax.text(x, y, s, ha="center", va="center", color=ink, **kwargs)

def arrow(start, end, color=blue):
    ax.add_patch(FancyArrowPatch(start, end, arrowstyle="-|>",
                                mutation_scale=16, lw=2, color=color))

text(5, 6.12, "Copper electrolysis: reaction identities fix electrode names", fontsize=15, weight="bold")
text(5, 5.82, "MODEL SCHEMATIC • arrows show transport roles, not measured paths", fontsize=10)
ax.add_patch(Rectangle((3.6, 4.85), 2.8, .62, fc="#f1f4f7", ec=ink, lw=1.5))
text(5, 5.16, "Regulated DC source")
text(3.83, 4.98, "+", weight="bold")
text(6.16, 4.98, "−", weight="bold")
ax.plot([2.45, 2.45, 3.6], [3.7, 5.16, 5.16], color=ink, lw=1.8)
ax.plot([6.4, 7.55, 7.55], [5.16, 5.16, 3.7], color=ink, lw=1.8)
arrow((2.12, 4.06), (2.12, 4.93))
text(1.3, 4.56, "Electrons\nto source", fontsize=10)
arrow((7.9, 4.93), (7.9, 4.06))
text(8.76, 4.56, "Electrons\nfrom source", fontsize=10)
ax.add_patch(Rectangle((1.55, 1.88), 6.9, 1.77, fc="#e8f4fa", ec="none"))
ax.plot([1.55, 1.55, 8.45, 8.45], [3.85, 1.88, 1.88, 3.85], color=ink, lw=1.8)
for x in (2.22, 7.32):
    ax.add_patch(Rectangle((x, 2.1), .46, 1.65, fc=copper, ec=ink, lw=1.3))
text(3.55, 3.31, "Positive anode\nOXIDATION", fontsize=11, weight="bold")
text(6.4, 3.31, "Negative cathode\nREDUCTION", fontsize=11, weight="bold")
text(5, 2.76, r"Aqueous $\mathrm{CuSO_4}$ electrolyte", fontsize=12)
arrow((3.35, 2.35), (6.65, 2.35))
text(5, 2.11, "Net conventional current carried by ions", fontsize=10)
text(2.6, 1.51, r"$\mathrm{Cu}\ \longrightarrow\ \mathrm{Cu}^{2+}+2e^-$", fontsize=14)
text(7.38, 1.51, r"$\mathrm{Cu}^{2+}+2e^-\ \longrightarrow\ \mathrm{Cu}$", fontsize=14)
text(2.6, 1.13, "Dry mass decreases", weight="bold")
text(7.38, 1.13, "Dry mass increases", weight="bold")
ax.add_patch(Rectangle((1.05, .15), 7.9, .63, fc="#f5f7f9", ec="#c8d1da"))
text(5, .49, r"Readouts: $Q=\int I(t)\,dt$ and before/after dry masses", fontsize=12)
fig.subplots_adjust(left=.015, right=.985, bottom=.015, top=.985)
fig.savefig(Path(__file__).with_suffix(".png").name, facecolor="white", transparent=False)
plt.close(fig)