codexpast-exam-of-the-soft-condensed-matter-course-of-the-university-of-cambridge2015paper-1-aggregate-distribution.py
past-exam-of-the-soft-condensed-matter-course-of-the-university-of-cambridge/2015/paper-1-aggregate-distribution.py
"""Original finite-micelle mass-action distributions.
Python 3.14, numpy, matplotlib. Every energy and concentration is an
illustrative dimensionless choice, not inferred exam data. Set mu_1^0=0,
mu_alpha^0/kT=-8+[(alpha-40)^2/(2*4^2)+offset]/alpha for alpha>=2.
The offset makes the aggregate and monomer molecular populations equal
at x_1=exp(-8), using the finite displayed size range.
"""
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
import numpy as np
alpha = np.arange(2, 101)
centre, width = 40., 4.
reference = np.exp(-8.)
penalty = (alpha-centre)**2/(2*width**2)
offset = np.log(np.sum(alpha*np.exp(-penalty))/reference)
fig, axes = plt.subplots(1, 2, figsize=(10, 3.8), constrained_layout=True,
facecolor="white", sharey=True)
for ax, factor, title in zip(axes, [0.5, 1.04],
["Below the crossover", "Above the crossover"]):
x1 = factor*reference
xa = alpha*np.exp(alpha*(np.log(x1)+8)-penalty-offset)
total = x1+np.sum(xa)
ax.semilogy(alpha, xa, lw=2.2, color="#1764a0", label="In aggregates")
ax.vlines(1, 1e-20, x1, color="#c55412", lw=2)
ax.scatter([1], [x1], color="#c55412", zorder=3, label="Monomers")
ax.set(xlim=(-1, 85), ylim=(1e-20, 2e-3),
xlabel=r"Aggregation number $\alpha$", title=title)
fraction = np.sum(xa)/total
displayed = f"{100*fraction:.1f}%" if fraction >= .001 else f"{100*fraction:.2e}%"
ax.text(.96, .75, f"Aggregated molecules:\n{displayed}",
transform=ax.transAxes, ha="right", fontsize=10,
bbox={"facecolor": "white", "alpha": .85, "edgecolor": "none"})
ax.grid(alpha=.17)
ax.spines[["top", "right"]].set_visible(False)
axes[0].set_ylabel(r"Molecular fraction $x_\alpha$ (log scale)")
axes[0].legend(loc="lower left", fontsize=9)
fig.suptitle("Illustrative finite-size mass-action model; no measured surfactant parameters",
fontsize=11)
fig.savefig("paper-1-aggregate-distribution.png", dpi=160, facecolor="white")
plt.close(fig)