from pathlib import Path
import json,numpy as np
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
root=Path.cwd();out=root/'figures';data=root/'data';out.mkdir(exist_ok=True);data.mkdir(exist_ok=True)
plt.rcParams.update({'font.family':'serif','font.size':12,'axes.spines.top':False,'axes.spines.right':False,'svg.fonttype':'none'})
r=np.logspace(-1,1,1000)
fig,ax=plt.subplots(figsize=(8,4.5),layout='constrained')
for z,col,ls in [(0.02,'#0072B2','-'),(.1,'#D55E00','--'),(.3,'#009E73',':')]:
 h=1/np.sqrt((1-r*r)**2+(2*z*r)**2);ax.loglog(r,h,color=col,ls=ls,label=f'ζ = {z:g}')
ax.axvline(1,color='#777',lw=.8);ax.set(xlabel='Frequency ratio f / fₙ',ylabel='Displacement / static deflection',title='Force-excited linear oscillator');ax.grid(alpha=.2,which='both');ax.legend();fig.savefig(out/'resonance.svg');plt.close(fig)
f=np.linspace(0,350,3501);A=np.where((f>=20)&(f<=120),.01,0);B=np.where((f>=220)&(f<=320),.01,0)
fig,ax=plt.subplots(figsize=(8,3.8),layout='constrained');ax.step(f,A,where='mid',color='#0072B2',label='Example A: 20–120 Hz');ax.step(f,B,where='mid',color='#D55E00',ls='--',label='Example B: 220–320 Hz');ax.set(xlabel='Frequency (Hz)',ylabel='Acceleration PSD (g²/Hz)',ylim=(0,.014),title='Two hypothetical profiles, each 1.00 g RMS');ax.legend();ax.grid(alpha=.2);fig.savefig(out/'equal-rms.svg');plt.close(fig)
f=np.logspace(1,2.5,150);d=2*9.80665/(2*np.pi*f)**2*1000
fig,ax=plt.subplots(figsize=(8,3.8),layout='constrained');ax.loglog(f,d,color='#0072B2');ax.scatter([10,100],[2*9.80665/(2*np.pi*x)**2*1000 for x in [10,100]],color='#D55E00');ax.set(xlabel='Frequency (Hz)',ylabel='Peak-to-peak displacement (mm)',title='Calculated displacement for 1 g peak sine acceleration');ax.grid(alpha=.2,which='both');fig.savefig(out/'displacement.svg');plt.close(fig)
values={'example_type':'Hypothetical teaching calculations, not test requirements','flat_psd_g2_per_hz':.01,'bandwidth_hz':100,'rms_g':float(np.sqrt(.01*100)),'sweep_10_to_640_at_2_oct_per_min_minutes':float(np.log2(640/10)/2),'sine_1g_peak_displacement_mm':{str(x):{'peak':9.80665/(2*np.pi*x)**2*1000,'peak_to_peak':2*9.80665/(2*np.pi*x)**2*1000} for x in [10,100]},'fft_bin_spacing_hz':2048/4096}
assert values['rms_g']==1 and values['sweep_10_to_640_at_2_oct_per_min_minutes']==3
assert np.isclose(values['sine_1g_peak_displacement_mm']['10']['peak']/values['sine_1g_peak_displacement_mm']['100']['peak'],100)
(data/'examples.json').write_text(json.dumps(values,indent=2));print(json.dumps(values,indent=2))
