codex/past-exam-of-the-mathematics-course-of-the-university-of-cambridge/2004/iii/paper-79-collimating-interface.py

past-exam-of-the-mathematics-course-of-the-university-of-cambridge/2004/iii/paper-79-collimating-interface.py
"""Original ray/front plot; tested Python 3.14 with root NumPy/Matplotlib pins.
Only output is a same-basename opaque PNG in caller CWD. Preserves MPLCONFIGDIR.
"""
from pathlib import Path
import numpy as np
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt


def geometry(radius):
    # z0=1, alpha=3, alpha_f=1; solid rho=2, beta=1.5; fluid rho=1.
    alpha, alpha_f, z0 = 3.0, 1.0, 1.0
    s = z0 * (alpha - alpha_f)
    d2 = alpha ** 2 - alpha_f ** 2
    D = np.sqrt(s ** 2 + d2 * np.asarray(radius) ** 2)
    Z = (alpha * s + alpha_f * D) / d2
    r = (alpha * D + alpha_f * s) / d2
    return Z, r, D


def displacement_coefficient(radius):
    alpha, alpha_f, beta, rho, rho_f = 3.0, 1.0, 1.5, 2.0, 1.0
    _, r, D = geometry(radius)
    denom_angle = np.sqrt(4 + alpha ** 2 * radius ** 2)
    sinP = alpha * radius / denom_angle
    cosP = 2 / denom_angle
    sinS = beta / alpha * sinP
    cosS = np.sqrt(1 - sinS ** 2)
    cosF = D / denom_angle
    C = 1 - 2 * sinS ** 2
    E = C ** 2 + (beta / alpha) ** 2 * (2 * sinP * cosP) * (2 * sinS * cosS)
    T = 2 * rho * alpha_f * C * cosP / (rho_f * alpha_f * cosP + rho * alpha * E * cosF)
    return T / (alpha_f * r)


def main():
    fig, axes = plt.subplots(1, 2, figsize=(11.0, 4.5), layout='constrained', facecolor='white')
    x = np.linspace(-4, 4, 701)
    Z, _, _ = geometry(np.abs(x))
    ax = axes[0]
    ax.fill_between(x, 0, Z, color='C1', alpha=0.08)
    ax.fill_between(x, Z, 3.55, color='C0', alpha=0.06)
    ax.plot(x, Z, color='black', label='Solid–fluid interface')
    for hit in [-3.5, -2, -0.8, 0, 0.8, 2, 3.5]:
        height, _, _ = geometry(abs(hit))
        ax.plot([0, hit], [0, height], color='C1', linewidth=1)
        ax.plot([hit, hit], [height, 3.45], color='C0', linewidth=1)
        ax.annotate('', xy=(hit, 3.35), xytext=(hit, 3.05),
                    arrowprops={'arrowstyle': '->', 'color': 'C0'})
    ax.scatter([0], [0], color='black', zorder=4)
    ax.axhline(3.0, linestyle='--', color='0.45', label='Equal arrival time')
    ax.text(-3.7, 0.85, 'Solid', fontsize=10)
    ax.text(-3.7, 3.3, 'Fluid', fontsize=10)
    ax.set(xlabel=r'Meridional coordinate / $z_0$', ylabel=r'Height / $z_0$',
           title='Spherical incidence, parallel transmitted rays', xlim=(-4, 4), ylim=(-0.12, 3.55))
    ax.legend(loc='lower left', fontsize=8)
    R = np.geomspace(0.02, 1000, 1000)
    ax = axes[1]
    ax.loglog(R, displacement_coefficient(R), label='Exact leading-front transfer')
    far = R[R >= 3]
    ax.loglog(far, 8 / (9 * far ** 2), '--', label=r'Large-$R$ coefficient $8/(9R^2)$')
    ax.hlines(4 / 7, 0.02, 0.25, color='0.5', linestyle=':', label=r'$A_u(0)=4/7$')
    ax.set(xlabel=r'$R/z_0$', ylabel=r'Displacement coefficient $A_u$',
           title=r'$\alpha=3,\ \beta=1.5,\ \alpha_f=1,\ \rho/\rho_f=2$', xlim=(0.02, 1000))
    ax.grid(alpha=0.2, which='both')
    ax.legend(fontsize=8, loc='lower left')
    fig.savefig(Path(__file__).with_suffix('.png').name, dpi=140, facecolor='white', transparent=False)
    plt.close(fig)


if __name__ == '__main__':
    main()