codexcatalyst-alternative-reaction-path.py
catalyst-alternative-reaction-path.py
"""Generate catalyst-alternative-reaction-path.png in the current directory.
Original teaching schematic, not a measured molecular path. Tested with Python
3.14.4, NumPy 2.3.5, and Matplotlib 3.10.7. Run from the repository's _media.
"""
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
import numpy as np
def profile(q, peak):
"""Cosine joins give exact plateaus and peaks with continuous first slopes."""
energy = np.full_like(q, -20.0)
energy[q <= 0.12] = 0.0
rising = (q > 0.12) & (q <= 0.48)
energy[rising] = peak * (1 - np.cos(np.pi * (q[rising] - 0.12) / 0.36)) / 2
falling = (q > 0.48) & (q < 0.88)
energy[falling] = peak - (peak + 20) * (
1 - np.cos(np.pi * (q[falling] - 0.48) / 0.40)
) / 2
return energy
def main():
plt.rcParams.update({"font.size": 11, "figure.facecolor": "white"})
q = np.unique(np.r_[np.linspace(0, 1, 801), 0.12, 0.48, 0.88])
fig, ax = plt.subplots(figsize=(8, 4.7), dpi=120)
colors = ("#b64928", "#176d9c")
for peak, color, label in (
(80, colors[0], "Without catalyst: forward 80, reverse 100"),
(50, colors[1], "With catalyst: forward 50, reverse 70"),
):
ax.plot(q, profile(q, peak), lw=2.8, color=color, label=label)
ax.scatter([0.48], [peak], color=color, s=28, zorder=4)
ax.text(0.49, peak + 4, f"Transition-state level: {peak}", color=color)
for level in (0, -20):
ax.axhline(level, color="#b8b8b8", lw=0.8, ls="--", zorder=0)
ax.text(0.01, 4, "A: 0", ha="left")
ax.text(0.94, -27, "B: −20", ha="center")
ax.annotate("", xy=(1.04, 0), xytext=(1.04, -20),
arrowprops={"arrowstyle": "<->", "color": "#444444"})
ax.text(1.06, -10, "Same\nΔG° = −20", va="center", fontsize=10)
ax.set(xlim=(-0.03, 1.23), ylim=(-35, 97),
xlabel="Schematic reaction coordinate (not time)",
ylabel="Standard molar Gibbs free energy / kJ mol⁻¹")
ax.set_xticks([])
ax.spines[["top", "right"]].set_visible(False)
ax.set_title("Same endpoints, lower free-energy barrier", pad=24, weight="bold")
ax.text(0.5, 1.02, "Stipulated levels at 300 K; barriers in kJ mol⁻¹",
transform=ax.transAxes, ha="center", fontsize=10)
ax.legend(loc="upper center", bbox_to_anchor=(0.5, -0.16), frameon=False,
fontsize=10)
fig.subplots_adjust(left=0.12, right=0.98, top=0.82, bottom=0.25)
fig.savefig("catalyst-alternative-reaction-path.png", facecolor="white")
plt.close(fig)
if __name__ == "__main__":
main()