codex/chain-growth-active-site-bookkeeping.py

chain-growth-active-site-bookkeeping.py
"""Draw stipulated one-site chain counts; Python 3.14, matplotlib 3.10.7.

Run from _media; the PNG basename matches this generator.
"""
from pathlib import Path

import matplotlib.pyplot as plt
from matplotlib.patches import FancyArrowPatch, Rectangle


BLUE = "#176aa3"
GREY = "#6a737d"
ORANGE = "#d67520"


def chain(ax, x, y, n, active=True):
    for i in range(n):
        ax.add_patch(Rectangle((x + i * 0.30, y - 0.15), 0.24, 0.30,
                               facecolor=BLUE if active else GREY, edgecolor="white"))
    if active:
        ax.text(x + n * 0.30, y, "★", color=ORANGE, fontsize=19, va="center")


def arrow(ax, x1, x2, y):
    ax.add_patch(FancyArrowPatch((x1, y), (x2, y), arrowstyle="-|>",
                                mutation_scale=17, color="#343e48", linewidth=1.7))


def main():
    fig, ax = plt.subplots(figsize=(10, 6.5), dpi=140, facecolor="white")
    ax.set(xlim=(0, 10), ylim=(0, 6.5))
    ax.axis("off")
    ax.text(0.1, 6.12, "Chain growth: units, molecules, and active sites", fontsize=19,
            weight="bold")
    ax.text(0.1, 5.72, "One active site per growing chain • schematic counts, not kinetic data",
            fontsize=11, color=GREY)
    rows = [(4.7, "Initiation", "+1", "+1", "+1"),
            (3.4, "Propagation", "0", "0", "+1"),
            (2.1, "Termination: join", "−1", "−2", "0"),
            (0.9, "Termination: separate", "0", "−2", "0")]
    for x, title in [(7.45, "ΔC"), (8.30, "ΔA"), (9.15, "ΔS")]:
        ax.text(x, 5.17, title, ha="center", fontsize=15, weight="bold")
    for y, title, dc, da, ds in rows:
        ax.text(0.1, y + 0.40, title, fontsize=12, weight="bold")
        for x, value in [(7.45, dc), (8.30, da), (9.15, ds)]:
            ax.text(x, y, value, ha="center", va="center", fontsize=17)
        arrow(ax, 4.15, 4.95, y)
    ax.text(1.05, 4.7, "$I^*$", fontsize=18, va="center", color=ORANGE)
    ax.text(1.8, 4.7, "+", fontsize=18, va="center")
    chain(ax, 2.35, 4.7, 1, False)
    ax.text(2.47, 4.30, "free M", fontsize=10, ha="center")
    chain(ax, 5.25, 4.7, 1)
    chain(ax, 1.05, 3.4, 3)
    ax.text(2.40, 3.4, "+", fontsize=18, va="center")
    chain(ax, 2.9, 3.4, 1, False)
    chain(ax, 5.25, 3.4, 4)
    for y in [2.1, 0.9]:
        chain(ax, 1.05, y, 2)
        ax.text(2.20, y, "+", fontsize=18, va="center")
        chain(ax, 2.70, y, 3)
    chain(ax, 5.25, 2.1, 5, False)
    chain(ax, 5.05, 0.9, 2, False)
    ax.text(5.90, 0.9, "+", fontsize=18, va="center")
    chain(ax, 6.2, 0.9, 3, False)
    ax.text(0.1, 0.25, "C: polymer molecules     A: polymer-bound active sites     S: incorporated units",
            fontsize=11)
    fig.subplots_adjust(left=0.02, right=0.99, top=0.99, bottom=0.01)
    fig.savefig(Path(__file__).stem + ".png", facecolor="white")
    plt.close(fig)


if __name__ == "__main__":
    main()