codex/step-growth-molecular-joining-count.py

step-growth-molecular-joining-count.py
"""Draw an original AB step-growth count; run from _media.

Tested with Python 3.14, matplotlib 3.10.7, numpy 2.3.5.
"""

from pathlib import Path

import matplotlib.pyplot as plt
from matplotlib.patches import Circle


def draw_chain(ax, x, y, length, new_join=None):
    spacing = 0.53
    for j in range(length - 1):
        ax.plot([x + j * spacing, x + (j + 1) * spacing], [y, y],
                color="#178249" if j == new_join else "#425568",
                lw=4 if j == new_join else 2.5, zorder=1)
    for j in range(length):
        ax.add_patch(Circle((x + j * spacing, y), 0.16,
                            facecolor="#eaf0f7", edgecolor="#425568", lw=1.4, zorder=2))
    ax.text(x - 0.27, y, "A", color="#b34b15", ha="center", va="center", fontsize=12)
    ax.text(x + (length - 1) * spacing + 0.27, y, "B", color="#285da8",
            ha="center", va="center", fontsize=12)


def main():
    plt.rcParams.update({"font.size": 12, "font.family": "DejaVu Sans"})
    fig, ax = plt.subplots(figsize=(10.4, 6.3), facecolor="white")
    ax.set(xlim=(-0.4, 10.1), ylim=(-0.55, 5.8))
    ax.axis("off")
    fig.suptitle("Step growth: joining chains reduces the molecule count", fontsize=17, y=0.98)
    rows = [([1] * 8, 4.65, "Eight AB feed molecules", 0),
            ([2, 3, 1, 1, 1], 2.85, "Three joins already made", 3),
            ([5, 1, 1, 1], 1.05, "Join the two-unit and three-unit chains", 4)]
    for lengths, y, label, joins in rows:
        ax.text(-0.2, y + 0.57, label, fontsize=13, weight="bold")
        x = 0.15
        for i, length in enumerate(lengths):
            draw_chain(ax, x, y, length, new_join=1 if joins == 4 and i == 0 else None)
            x += (length - 1) * 0.53 + 0.92
        n = len(lengths)
        assert sum(lengths) == 8 and 8 - n == joins
        remaining = 8 - joins
        ax.text(7.5, y + 0.22, f"N = {n},   joins = {joins}", fontsize=12)
        ax.text(7.5, y - 0.13, f"A left = B left = {remaining}", fontsize=12)
        ax.text(7.5, y - 0.49, rf"$p={joins}/8,\quad X_n=8/{n}$", fontsize=13)
    for y in [3.75, 1.95]:
        ax.annotate("", xy=(6.75, y - 0.3), xytext=(6.75, y + 0.2),
                    arrowprops={"arrowstyle": "->", "color": "#425568", "lw": 1.8})
    ax.text(-0.15, -0.02, "Circle = one feed-derived unit; line = joining bond; letters = unreacted groups",
            fontsize=11.5)
    ax.text(-0.15, -0.43, "Green bond: 2 + 3 → 5 units. One join removes one molecule and one A/B pair.",
            fontsize=11.5, color="#17683c")
    fig.subplots_adjust(left=0.04, right=0.98, top=0.9, bottom=0.03)
    fig.savefig(Path.cwd() / (Path(__file__).stem + ".png"), dpi=120,
                facecolor="white", transparent=False)
    plt.close(fig)


if __name__ == "__main__":
    main()