codexbiochemical-competing-reaction-routes.py
biochemical-competing-reaction-routes.py
"""Illustrate selective reaction routes with chosen first-order kinetics.
Python 3.14.4; Matplotlib 3.10.7 and NumPy 2.3.5 as in pyproject.toml.
Labels are hypothetical species of equal elemental composition, not structures.
Run in _media; the opaque white PNG is written to cwd with this basename.
"""
from pathlib import Path
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
from matplotlib.patches import FancyArrowPatch, FancyBboxPatch
import numpy as np
def main():
ink, blue, orange = "#253947", "#167294", "#b35e22"
fig = plt.figure(figsize=(9.0, 5.4), facecolor="white")
fig.text(0.5, 0.945, "Choosing a route through chemistry", ha="center",
fontsize=17, color=ink, weight="bold")
fig.text(0.5, 0.887, "Illustrative first-order model; all rate constants chosen",
ha="center", fontsize=11, color=ink)
network = fig.add_axes((0.03, 0.25, 0.42, 0.57))
network.set(xlim=(0, 10), ylim=(0, 8))
network.axis("off")
for x, y, label, color in [(1.5, 4, "S", ink), (8, 6.5, "P", blue),
(8, 1.5, "Q", orange)]:
network.add_patch(FancyBboxPatch((x - 0.7, y - 0.7), 1.4, 1.4,
boxstyle="round,pad=0.15", facecolor="white",
edgecolor=color, linewidth=2))
network.text(x, y, label, ha="center", va="center", fontsize=20,
color=color, weight="bold")
for endpoint, color in [((7.1, 6.2), blue), ((7.1, 1.8), orange)]:
network.add_patch(FancyArrowPatch((2.4, 4), endpoint,
arrowstyle="-|>", mutation_scale=18,
linewidth=2.5, color=color))
network.text(4.65, 6.1, r"$k_P = 0.08$ or $0.32$", ha="center",
fontsize=11, color=blue)
network.text(4.65, 1.75, r"$k_Q = 0.02$", ha="center", fontsize=11,
color=orange)
network.text(4.8, 0.1, "Effective rates in min⁻¹", ha="center", fontsize=10,
color=ink)
ax = fig.add_axes((0.56, 0.30, 0.40, 0.49), facecolor="white")
kp = np.array([0.08, 0.32])
kq = 0.02
p = 100 * kp / (kp + kq)
q = 100 - p
positions = np.array([1, 0])
ax.barh(positions, p, height=0.48, color=blue, label="P")
ax.barh(positions, q, left=p, height=0.48, color=orange, label="Q")
for y, pv, qv in zip(positions, p, q):
ax.text(pv / 2, y, f"P: {pv:.1f}", ha="center", va="center",
color="white", fontsize=12, weight="bold")
ax.text(pv + qv / 2, y + 0.34, f"Q: {qv:.1f}", ha="center",
va="center", color=orange, fontsize=10)
ax.plot([pv + qv / 2] * 2, [y + 0.27, y + 0.24], color=orange)
ax.set(xlim=(0, 104), ylim=(-0.5, 1.7), yticks=[1, 0],
yticklabels=["Original", "P route ×4"], xticks=[0, 20, 40, 60, 80, 100],
xlabel="Limiting product amount (µmol)")
ax.set_title("From 100 µmol of S", fontsize=12, color=ink, pad=15)
ax.tick_params(labelsize=10)
ax.spines[["top", "right", "left"]].set_visible(False)
ax.tick_params(axis="y", length=0)
fig.text(0.5, 0.15, "Desired fraction = kP / (kP + kQ): 80% → 94.1%",
ha="center", color=ink, fontsize=12,
bbox={"facecolor": "#edf6f8", "edgecolor": blue, "pad": 7})
fig.text(0.5, 0.055, "Speeding both routes equally leaves the split unchanged. No experimental data.",
ha="center", color=ink, fontsize=10)
fig.savefig(Path(__file__).with_suffix(".png").name, dpi=140,
facecolor="white", transparent=False)
plt.close(fig)
print(f"Limiting desired fractions: {p[0]/100:.12g}, {p[1]/100:.12g}")
if __name__ == "__main__":
main()