codex/past-exam-of-the-mathematics-course-of-the-university-of-cambridge/2009/iii/paper-73-equilibrium.py

past-exam-of-the-mathematics-course-of-the-university-of-cambridge/2009/iii/paper-73-equilibrium.py
"""Stratified turbulence equilibrium curves.

Tested with Python 3.14, numpy 2.3.5 and matplotlib 3.10.7.
Write only the PNG basename to caller CWD; respect caller MPLCONFIGDIR.
"""
from pathlib import Path

import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
import numpy as np


def main():
    fig, ax = plt.subplots(figsize=(9, 5.2), dpi=100, facecolor="white")
    ax.set_facecolor("white")
    parameters = (0, .04, .16, .25, .36)
    colors = ("#555555", "#2873b5", "#2a945d", "#d47c1d", "#b33f59")
    for parameter, color in zip(parameters, colors):
        if parameter == 0:
            x = np.linspace(0, 1.55, 600)
            ax.plot(x, x, color=color, lw=2, label="A = 0: unstratified")
            ax.plot([1], [1], "o", color=color, ms=6)
            continue
        minimum = np.sqrt(parameter)
        roots = []
        if parameter <= .25:
            discriminant = np.sqrt(1-4*parameter)
            roots = [(1-discriminant)/2, (1+discriminant)/2]
        x = np.unique(np.r_[np.linspace(.018, 1.55, 900), minimum, roots])
        ratio = x + parameter/x
        regime = "critical" if parameter == .25 else ("supercritical" if parameter > .25 else "subcritical")
        ax.plot(x, ratio, color=color, lw=2,
                label=f"A = {parameter:.2f}: {regime}")
        ax.plot([minimum], [2*minimum], "s", color=color, ms=6)
        if roots:
            ax.plot(roots, np.ones(len(roots)), "o", color=color, ms=6)
    ax.axhline(1, color="#333333", ls="--", lw=1.2)
    ax.text(1.49, 1.055, "R = 1", ha="right", fontsize=11)
    ax.set_xlim(0, 1.55)
    ax.set_ylim(0, 2.35)
    ax.set_xlabel(r"Normalized turbulent speed $\widetilde q=q/(L_u C_u S)$", fontsize=12)
    ax.set_ylabel("Total sinks / production, R", fontsize=12)
    ax.set_title(r"Stratified turbulence: $R=\widetilde{q}+\mathcal{A}/\widetilde{q}$", fontsize=14)
    ax.text(.035, 2.20, "Squares: minima     Circles: equilibria", fontsize=10)
    ax.legend(loc="upper right", framealpha=1, facecolor="white", fontsize=10)
    ax.grid(alpha=.18)
    fig.tight_layout()
    fig.savefig(Path(__file__).with_suffix(".png").name,
                facecolor="white", transparent=False, dpi=100)
    plt.close(fig)


if __name__ == "__main__":
    main()