codex/past-exam-of-the-physics-1-paper-of-the-university-of-oxford/2021-potential-and-force.py

past-exam-of-the-physics-1-paper-of-the-university-of-oxford/2021-potential-and-force.py
"""Plot Oxford Physics 1 (2021), question 3. Python 3.14; NumPy, Matplotlib."""
from pathlib import Path

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


def main():
    plt.rcParams.update({'font.size': 12, 'figure.facecolor': 'white', 'axes.facecolor': 'white'})
    x = np.linspace(-5, 5, 2001)
    potential = -x / (1 + x**2)
    force = (1 - x**2) / (1 + x**2)**2
    fig, axes = plt.subplots(1, 2, figsize=(10.8, 4.1), layout='constrained')
    for ax in axes:
        ax.axhline(0, color='0.35', linewidth=0.8)
        ax.axvline(0, color='0.35', linewidth=0.8)
        ax.set(xlim=(-5, 5), xlabel=r'$x/A$, where $A=|a|>0$')
        ax.grid(alpha=0.18)
    ax = axes[0]
    ax.plot(x, potential, color='#2166ac', linewidth=2.3)
    ax.set(title=r'Potential energy ($k>0$)', ylabel=r'$AU/k$')
    ax.scatter([-1, 1], [0.5, -0.5], c=['#b2182b', '#1b7837'], s=48, zorder=3)
    ax.annotate('Unstable equilibrium', (-1, 0.5), xytext=(-4.7, 0.63),
                arrowprops={'arrowstyle': '->', 'color': '#b2182b'}, color='#b2182b')
    ax.annotate('Stable equilibrium', (1, -0.5), xytext=(0.5, -0.73),
                arrowprops={'arrowstyle': '->', 'color': '#1b7837'}, color='#1b7837')
    ax.set(ylim=(-0.85, 0.85), yticks=[-0.5, 0, 0.5])
    ax = axes[1]
    ax.plot(x, force, color='#762a83', linewidth=2.3)
    ax.scatter([-1, 1], [0, 0], c=['#b2182b', '#1b7837'], s=40, zorder=3)
    ax.scatter([-np.sqrt(3), 0, np.sqrt(3)], [-1/8, 1, -1/8], color='#762a83', s=27, zorder=3)
    ax.annotate(r'$F(0)=k/A^2$', (0, 1), xytext=(0.7, 1.02))
    ax.annotate(r'Minima at $x=\pm\sqrt{3}A$', (-np.sqrt(3), -1/8), xytext=(-4.7, -0.42),
                arrowprops={'arrowstyle': '->', 'color': '#762a83'})
    ax.set(title=r'Force: $F=-dU/dx$', ylabel=r'$A^2F/k$', ylim=(-0.55, 1.25), yticks=[-0.125, 0, 0.5, 1])
    output = Path.cwd() / Path(__file__).with_suffix('.png').name
    fig.savefig(output, dpi=110, facecolor='white', transparent=False,
                metadata={'Software': 'Oxford Physics 1 (2021) figure generator'})
    plt.close(fig)


if __name__ == '__main__':
    main()