codexconsecutive-reaction-intermediate-time-profiles.py
consecutive-reaction-intermediate-time-profiles.py
"""Exact illustrative consecutive kinetics; Python 3.14, numpy 2.3.5, matplotlib 3.10.7.
Run from _media; writes consecutive-reaction-intermediate-time-profiles.png there.
All parameters are chosen, not measured. No numerical ODE solver is needed.
"""
from pathlib import Path
import matplotlib.pyplot as plt
import numpy as np
def main():
plt.rcParams.update({"font.size": 11, "axes.spines.top": False,
"axes.spines.right": False, "figure.facecolor": "white"})
k = 0.20 # s^-1; both steps
a0 = 1.00 # mmol L^-1
t = np.linspace(0.0, 30.0, 601)
a = a0 * np.exp(-k * t)
intermediate = a0 * k * t * np.exp(-k * t)
product = a0 - a - intermediate
direct = a0 - a
assert np.allclose(a + intermediate + product, a0)
assert np.all(product >= 0.0)
fig = plt.figure(figsize=(9.4, 5.4), layout="constrained")
gs = fig.add_gridspec(2, 1, height_ratios=[0.8, 3.6])
network = fig.add_subplot(gs[0])
network.set_axis_off()
network.set_xlim(0, 1)
network.set_ylim(0, 1)
for x, label, colour in [(0.13, "A", "#2864a5"),
(0.49, "I", "#b66015"),
(0.85, "P", "#24764d")]:
network.text(x, 0.42, label, fontsize=20, color=colour,
ha="center", va="center",
bbox={"boxstyle": "round,pad=0.35", "fc": "white", "ec": colour})
for left, right in [(0.20, 0.42), (0.56, 0.78)]:
network.annotate("", (right, 0.42), (left, 0.42),
arrowprops={"arrowstyle": "->", "lw": 2, "color": "#444444"})
network.text((left + right) / 2, 0.85, r"$k=0.20\ \mathrm{s^{-1}}$",
ha="center", va="center", fontsize=11)
ax = fig.add_subplot(gs[1])
ax.plot(t, a, color="#2864a5", lw=2.4, label="A: same in both models")
ax.plot(t, intermediate, color="#b66015", lw=2.6, label="I: consecutive model")
ax.plot(t, product, color="#24764d", lw=2.6, label="P: consecutive model")
ax.plot(t, direct, color="#777777", lw=1.9, linestyle="--", label="P: direct A → P model")
peak_t = 1.0 / k
peak_i = a0 / np.e
ax.plot(peak_t, peak_i, "o", color="#b66015", ms=6)
ax.annotate("I peaks at 5.00 s\n[A] = [I] = 0.367879",
xy=(peak_t, peak_i), xytext=(10.0, 0.40),
arrowprops={"arrowstyle": "->", "color": "#b66015"},
color="#84460e", fontsize=10)
ax.set(xlim=(0, 30), ylim=(0, 1.05), xlabel="Time (s)",
ylabel=r"Concentration ($\mathrm{mmol\,L^{-1}}$)")
ax.grid(alpha=0.18)
ax.legend(loc="center right", bbox_to_anchor=(1, 0.29), frameon=True,
facecolor="white", framealpha=0.96, fontsize=10)
fig.suptitle("An intermediate creates a delay in product formation", fontsize=15)
fig.savefig(Path(__file__).stem + ".png", dpi=120, facecolor="white", transparent=False)
plt.close(fig)
if __name__ == "__main__":
main()