codex/biochemical-atp-phosphate-transfer.py

biochemical-atp-phosphate-transfer.py
"""Draw symbolic phosphate transfer and its separate free-energy accounting.

Python 3.14.4; Matplotlib 3.10.7 as in pyproject.toml.
This is an illustrative grouped catalytic cycle, not a molecular structure.
All energy changes are chosen, consistently defined reference values.
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


def main():
    ink, blue, orange = "#253947", "#167294", "#b35e22"
    fig = plt.figure(figsize=(9.0, 6.1), facecolor="white")
    fig.text(0.5, 0.945, "ATP couples phosphate transfer to sugar", ha="center",
             fontsize=17, color=ink, weight="bold")
    fig.text(0.5, 0.895, "S and S–P label hypothetical sugar states; no molecular structure drawn",
             ha="center", fontsize=10.5, color=ink)
    ax = fig.add_axes((0.045, 0.48, 0.91, 0.35))
    ax.set(xlim=(0, 10), ylim=(0, 4))
    ax.axis("off")
    for x, label in [(1.6, "Free enzyme E"),
                     (5, "Bound reactants\nE · S · ATP"),
                     (8.4, "Bound products\nE · S–P · ADP")]:
        ax.add_patch(FancyBboxPatch((x - 1.3, 1.5), 2.6, 1.05,
                     boxstyle="round,pad=0.12", facecolor="#edf6f8",
                     edgecolor=blue, linewidth=1.5))
        ax.text(x, 2.04, label, ha="center", va="center", fontsize=11,
                color=ink)
    for start, end in [((3, 2), (3.6, 2)), ((6.4, 2), (7, 2))]:
        ax.add_patch(FancyArrowPatch(start, end, arrowstyle="-|>",
                     mutation_scale=16, linewidth=2, color=blue))
    ax.text(3.3, 2.96, "S + ATP\nbind", ha="center", fontsize=10, color=ink)
    ax.text(6.7, 2.96, "Group\ntransfer", ha="center", fontsize=10, color=ink)
    ax.add_patch(FancyArrowPatch((8.4, 1.36), (1.6, 1.36),
                 connectionstyle="arc3,rad=-0.17", arrowstyle="-|>",
                 mutation_scale=16, linewidth=2, color=blue))
    ax.text(5, 0.16, "Release S–P + ADP; regenerate E", ha="center",
            fontsize=11, color=ink)
    fig.text(0.5, 0.445, "Net: S + ATP → S–P + ADP", ha="center",
             fontsize=13, color=blue, weight="bold")
    fig.text(0.5, 0.37, "Separate thermodynamic accounting (chosen ΔG°′ in kJ/mol)",
             ha="center", fontsize=11.5, color=ink, weight="bold")
    for y, equation, value, color in [
        (0.30, "S + Pi → S–P + H₂O", "+12", orange),
        (0.235, "ATP + H₂O → ADP + Pi", "−32", blue),
        (0.15, "S + ATP → S–P + ADP", "−20", blue),
    ]:
        fig.text(0.23, y, equation, fontsize=12, color=ink)
        fig.text(0.78, y, value, ha="right", fontsize=13, color=color,
                 weight="bold")
    fig.add_artist(plt.Line2D([0.22, 0.79], [0.206, 0.206],
                   transform=fig.transFigure, color="#8ca1ad", linewidth=1))
    fig.text(0.5, 0.052, "Water and Pi cancel in the sum; the sum does not specify microscopic steps.",
             ha="center", fontsize=10, color=ink)
    fig.savefig(Path(__file__).with_suffix(".png").name, dpi=140,
                facecolor="white", transparent=False)
    plt.close(fig)
    print("Chosen reference changes: +12 − 32 = −20 kJ/mol")


if __name__ == "__main__":
    main()