codex/past-exam-of-the-mathematics-course-of-the-university-of-cambridge/2021/iii/paper-316-dust-loci.py

past-exam-of-the-mathematics-course-of-the-university-of-cambridge/2021/iii/paper-316-dust-loci.py
#!/usr/bin/env python3
"""Generate paper-316-dust-loci.png."""

from pathlib import Path

import numpy as np
import matplotlib.pyplot as plt
from math import sqrt, tan, pi


mu=1.0
q=1.0
p=2*q

def comet_state(f):
    r=p/(1+np.cos(f))
    pos=np.array([r*np.cos(f),r*np.sin(f)],float)
    vel=np.sqrt(mu/p)*np.array([-np.sin(f),1+np.cos(f)],float)
    return pos,vel

def comet_time(f):
    D=np.tan(f/2)
    return np.sqrt(2*q**3/mu)*(D+D**3/3)

def propagate(pos,vel,beta,dt):
    if dt <= 0: return pos.copy(),vel.copy()
    y=np.r_[pos,vel].astype(float)
    n=max(300,int(1200*dt/(comet_time(pi/2)-comet_time(-pi/2))))
    h=dt/n
    def rhs(y):
        r=y[:2]; rr=np.linalg.norm(r)
        return np.r_[y[2:],-(1-beta)*mu*r/rr**3]
    for _ in range(n):
        k1=rhs(y); k2=rhs(y+h*k1/2); k3=rhs(y+h*k2/2); k4=rhs(y+h*k3)
        y += h*(k1+2*k2+2*k3+k4)/6
    return y[:2],y[2:]

tobs=comet_time(pi/2)
comet_obs,_=comet_state(pi/2)
fs=np.linspace(-pi/2,pi/2,220)
orb=np.array([comet_state(f)[0] for f in fs])
fig,axs=plt.subplots(1,2,figsize=(11,5),constrained_layout=True)
for ax in axs:
    ax.plot(orb[:,0],orb[:,1],color='#777',lw=1.4,label='comet orbit')
    ax.scatter([0],[0],s=100,color='#f4b942',edgecolor='#8c5f00',zorder=5,label='star')
    ax.scatter([comet_obs[0]],[comet_obs[1]],marker='*',s=120,color='black',zorder=6,label=r'comet at $f=\pi/2$')
    ax.set_aspect('equal'); ax.grid(alpha=.22); ax.set_xlabel('$x/q$'); ax.set_ylabel('$y/q$')

# Synchrone: release at pericentre
p0,v0=comet_state(0)
betas=np.linspace(0.08,1.55,160)
pts=np.array([propagate(p0,v0,b,tobs)[0] for b in betas])
axs[0].plot(pts[:,0],pts[:,1],color='#006bb6',lw=2.4,label='pericentre synchrone')
for b in [0.3,0.7,1.0,1.3,1.5]:
    pp=propagate(p0,v0,b,tobs)[0]
    axs[0].scatter(*pp,s=18,color='#006bb6')
    axs[0].annotate(rf'$\beta={b:g}$',pp,xytext=(4,4),textcoords='offset points',fontsize=8)
axs[0].set_title('Same release time: synchrone')

# Syndynes: fixed beta, release times vary
colors=['#2ca02c','#d62728','#7b2cbf']
for b,c in zip([0.7,1.0,1.3],colors):
    relfs=np.linspace(-pi/2,pi/2,180)
    line=[]
    for f0 in relfs:
        pp,vv=comet_state(f0)
        line.append(propagate(pp,vv,b,tobs-comet_time(f0))[0])
    line=np.array(line)
    axs[1].plot(line[:,0],line[:,1],lw=2.1,color=c,label=rf'$\beta={b:g}$')
axs[1].set_title(r'Same $\beta$: syndynes')
for ax in axs:
    ax.legend(fontsize=8,loc='best')
fig.suptitle('Zero-ejection-speed dust released from a parabolic comet',fontsize=13)
output = Path(Path(__file__).stem + ".png")
fig.savefig(output, dpi=100, facecolor="white")
plt.close(fig)