"""Ideal pressure-matched, nonrelativistic carried-propellant reference; no engine measurements."""
from pathlib import Path
import sys
sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
import csv,json
import numpy as np
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
from svg_accessibility import describe_svg
ROOT=Path(__file__).resolve().parent
T=1.0
ETA=0.6
v=np.unique(np.concatenate([np.geomspace(100,50000,161),[1000.,10000.]]))
mdot=T/v
pjet=0.5*T*v
pin=pjet/ETA
rows=[dict(thrust_N=T,exhaust_speed_m_s=float(x),propellant_kg_s=float(m),jet_power_W=float(j),assigned_efficiency=ETA,illustrative_input_power_W=float(i)) for x,m,j,i in zip(v,mdot,pjet,pin)]
with (ROOT/'exhaust-sweep.csv').open('w',newline='',encoding='utf-8') as f:
 w=csv.DictWriter(f,fieldnames=list(rows[0]));w.writeheader();w.writerows(rows)
methods={'title':'At the same thrust, faster exhaust uses less propellant and more jet power','kind':'Deterministic analytical teaching calculation; no measured engine data','thrust_N':T,'speed_range_m_s':[100,50000],'speed_points':len(v),'assigned_efficiency':ETA,'efficiency_status':'Illustrative electrical/input-to-directed-jet kinetic power conversion, not measured hardware efficiency','frame':'Instantaneous rocket frame; propellant carried onboard; no incoming atmospheric flow','formulas':{'mass_flow':'T/v','directed_jet_kinetic_power':'0.5*mdot*v^2 = 0.5*T*v','illustrative_input_power':'Pjet/eta'},'assumptions':['Steady one-dimensional pressure-matched ideal exhaust. Exit pressure equals environment pressure; pressure thrust excluded.','Nonrelativistic speed, monodirectional single exhaust speed.','Propellant initially at rest in the instantaneous vehicle frame.','Thrust fixed independently for comparison, not predicted by a physical engine.'],'exclusions':['No thermal/internal energy, nozzle/plume divergence, un-ionized flow, ionization cost model or engine architecture.','No onboard source mass, structural mass, lifetime, mission delta-v, acceleration, travel duration or endurance calculation.','Not an atmospheric ionic-wind or hovering model.','Assigned efficiency merely converts directed jet power to an illustrative input; it does not establish a complete engine/system energy budget.'],'uncertainty':'No confidence intervals: assigned parameters and exact analytic curves; not a measurement study.','examples':[row for row in rows if row['exhaust_speed_m_s'] in [1000,10000]],'references':[{'url':'https://www1.grc.nasa.gov/beginners-guide-to-aeronautics/thrust-force/','supports':'Momentum flux, rocket equation with pressure correction; pressure-matched specialization.'},{'url':'https://www.nasa.gov/wp-content/uploads/2015/08/ionpropfact_sheet_ps-01628.pdf','supports':'Ionized carried propellant accelerated electrically and neutralization; application context, not selected parameter validation.'}],'reference_checked_date':'2026-09-28'}
(ROOT/'methods.json').write_text(json.dumps(methods,indent=2)+'\n',encoding='utf-8')
plt.rcParams.update({'font.size':11,'axes.spines.top':False,'axes.spines.right':False,'svg.fonttype':'none'})
fig,ax=plt.subplots(1,2,figsize=(11,4.8),layout='constrained')
ax[0].loglog(v,mdot*1000,color='#245da8',lw=2.3)
ax[0].set(xlabel='Exhaust speed relative to vehicle (m/s)',ylabel='Propellant flow (g/s)',title='A  Less carried propellant per second')
ax[1].loglog(v,pjet/1000,color='#245da8',lw=2.3,label='Directed jet kinetic power')
ax[1].loglog(v,pin/1000,color='#b44b19',lw=2,ls='--',label='Illustrative input: assigned η = 0.60')
ax[1].set(xlabel='Exhaust speed relative to vehicle (m/s)',ylabel='Power (kW)',title='B  More power at the same thrust')
ax[1].legend(loc='upper left',fontsize=9)
for a in ax:
 a.grid(True,which='major',alpha=.22);a.set_xlim(100,50000)
 a.set_xticks([100,1000,10000,50000],labels=['100','1,000','10,000','50,000'])
for speed in [1000,10000]:
 ax[0].plot(speed,T/speed*1000,'o',color='#245da8');ax[1].plot(speed,.5*T*speed/1000,'o',color='#245da8')
fig.suptitle('Ideal carried-propellant exhaust · fixed thrust 1 N',fontsize=15)
fig.savefig(ROOT/'exhaust-tradeoff.svg');describe_svg(ROOT/'exhaust-tradeoff.svg','Ideal exhaust trade-off at fixed one-newton thrust','Two logarithmic panels compare carried propellant flow and directed jet kinetic power as exhaust speed increases from 100 to 50,000 metres per second. An illustrative 60 percent conversion assumption is shown separately; this is an analytical teaching model, not measured engine performance.');fig.savefig(ROOT/'exhaust-tradeoff.pdf');fig.savefig(ROOT/'exhaust-tradeoff.png',dpi=140);plt.close(fig)
print(json.dumps({'rows':len(rows),'examples':methods['examples']}))

# Mobile export: viewBox width 288 pt, so 16 pt text renders >=17.7 CSS px at 320 CSS px.
# Ordinary-number ticks avoid miniature logarithmic exponent glyphs.
with plt.rc_context({'font.size':16,'axes.titlesize':17,'axes.labelsize':16,'xtick.labelsize':16,'ytick.labelsize':16}):
 fig,ax=plt.subplots(2,1,figsize=(4,11),layout='constrained')
 ax[0].loglog(v,mdot*1000,color='#245da8',lw=2.5)
 ax[0].set(title='A  Less propellant',ylabel='Propellant flow (g/s)')
 ax[0].set_yticks([.01,.1,1,10],labels=['0.01','0.1','1','10'])
 ax[1].loglog(v,pjet/1000,color='#245da8',lw=2.5,label='Jet kinetic power')
 ax[1].loglog(v,pin/1000,color='#b44b19',lw=2.5,ls='--',label='Input: assigned\\nη = 0.60'.replace('\\n','\n'))
 ax[1].set(title='B  More power',ylabel='Power (kW)')
 ax[1].set_yticks([.1,1,10],labels=['0.1','1','10'])
 ax[1].legend(loc='upper left',fontsize=16,framealpha=.96,handlelength=1)
 for a in ax:
  a.set_xlim(100,50000);a.grid(True,which='major',alpha=.25)
  a.set_xticks([100,1000,10000],labels=['100','1,000','10,000'])
  a.set_xlabel('Exhaust speed (m/s)\nrelative to vehicle')
 for speed in [1000,10000]:
  ax[0].plot(speed,T/speed*1000,'o',color='#245da8');ax[1].plot(speed,.5*T*speed/1000,'o',color='#245da8')
 fig.suptitle('Ideal exhaust · 1 N thrust\nSame data, stacked panels',fontsize=17)
 fig.savefig(ROOT/'exhaust-tradeoff-mobile.svg');describe_svg(ROOT/'exhaust-tradeoff-mobile.svg','Ideal exhaust trade-off at fixed one-newton thrust','Two stacked panels compare propellant flow and directed jet kinetic power as exhaust speed increases. The dashed input-power line uses an illustrative 60 percent conversion assumption. Same analytical data as the desktop figure; no measured engine data.');fig.savefig(ROOT/'exhaust-tradeoff-mobile.png',dpi=80);plt.close(fig)
methods['figure_variants']={'desktop':'exhaust-tradeoff.svg','mobile':'exhaust-tradeoff-mobile.svg','mobile_design':'Stacked panels; SVG width288pt, minimum text16pt gives17.78CSSpx at320CSSpx image width; ordinary-number ticks, same163rows and formulas.','mobile_usage':'Display at320CSSpx or wider; this minimum refers to actual image content width, not viewport width.'}
(ROOT/'methods.json').write_text(json.dumps(methods,indent=2)+'\n',encoding='utf-8')
