"""Analytical energy accounting with assigned hypothetical inputs."""
import csv,json,math
from pathlib import Path
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
P=Path(__file__).resolve().parent

def generator(rpm,torque,efficiency):
 if not all(math.isfinite(v) for v in [rpm,torque,efficiency]) or rpm<0 or torque<0 or not 0<=efficiency<=1:raise ValueError('Finite nonnegative speed/torque and efficiency in [0,1] required')
 omega=rpm*2*math.pi/60;pin=torque*omega
 return dict(rpm=rpm,torque_Nm=torque,efficiency=efficiency,omega_rad_s=omega,input_W=pin,electrical_W=efficiency*pin,loss_W=(1-efficiency)*pin)

def heating(mass,power):
 if not all(math.isfinite(v) for v in [mass,power]) or mass<0 or power<0:raise ValueError('Finite nonnegative mass and absorbed power required')
 sensible=mass*800*100;latent=mass*200000;total=sensible+latent
 return dict(mass_kg=mass,absorbed_power_W=power,sensible_J=sensible,latent_J=latent,total_J=total,time_s=total/power if power>0 else (0 if total==0 else None))
g=[generator(rpm,1,eta) for eta in [0,.5,.8,1] for rpm in range(0,601,5)]
h=[heating(i/20,power) for power in [100,500,1000] for i in range(101)]
for name,rows in [('generator.csv',g),('heating.csv',h)]:
 with (P/name).open('w',newline='') as f:
  w=csv.DictWriter(f,fieldnames=rows[0]);w.writeheader();w.writerows(rows)
# Independent revolutions/work-per-turn derivation and explicit heat integrals.
err=0
for r in g:
 independently=2*math.pi*r['torque_Nm']*(r['rpm']/60)
 err=max(err,abs(independently-r['input_W']))
 assert math.isclose(r['input_W'],r['electrical_W']+r['loss_W'],abs_tol=1e-12)
 assert min(r['input_W'],r['electrical_W'],r['loss_W'])>=0
for r in h:
 integrated=sum([r['mass_kg']*800]*100)+r['mass_kg']*200000
 assert math.isclose(integrated,r['total_J'],abs_tol=1e-8)
 assert math.isclose(r['time_s']*r['absorbed_power_W'],r['total_J'],abs_tol=1e-8)
assert heating(1,0)['time_s'] is None and heating(0,0)['time_s']==0
assert all(generator(0,1,e)['input_W']==0 for e in [0,1])
assert generator(600,1,0)['electrical_W']==0 and generator(600,1,1)['loss_W']==0
bad=0
for fn,args in [(generator,(-1,1,.8)),(generator,(100,-1,.8)),(generator,(100,1,1.1)),(generator,(100,1,-.1)),(generator,(float('nan'),1,.8)),(heating,(-1,100)),(heating,(1,-1)),(heating,(1,float('inf')))]:
 try:fn(*args)
 except ValueError:bad+=1
assert bad==8
plt.rcParams.update({'font.family':'DejaVu Sans','font.size':10,'axes.spines.top':False,'axes.spines.right':False,'svg.fonttype':'none','pdf.fonttype':42})
fig,ax=plt.subplots(1,2,figsize=(11,4.8))
rr=[r for r in g if r['efficiency']==.8]
for k,label,c in [('input_W','Mechanical input','#555555'),('electrical_W','Electrical output','#007f93'),('loss_W','Heat loss','#b04a87')]:ax[0].plot([r['rpm'] for r in rr],[r[k] for r in rr],label=label,color=c,lw=2)
ax[0].set(xlabel='Shaft speed (rpm)',ylabel='Power (W)',title='A  |  Generator: assigned torque 1 N m, efficiency 80%');ax[0].legend(fontsize=9)
rr=[r for r in h if r['absorbed_power_W']==500]
x=[r['mass_kg'] for r in rr]
ax[1].stackplot(x,[r['sensible_J']/1000 for r in rr],[r['latent_J']/1000 for r in rr],labels=['Heating to transition','Completing phase change'],colors=['#4882ad','#9663ac'],alpha=.9)
ax[1].set(xlabel='Hypothetical inert material mass (kg)',ylabel='Required ideal energy (kJ)',title='B  |  Assigned phase-change energy budget');ax[1].legend(loc='upper left',fontsize=9)
for a in ax:a.grid(alpha=.18);a.set_xlim(left=0);a.set_ylim(bottom=0)
fig.suptitle('Energy enters through a source; useful output has a budget',fontsize=15)
fig.text(.5,.055,'All parameters are hypothetical. Heating: c = 800 J/(kg K), temperature rise = 100 K, latent heat = 200 kJ/kg.',ha='center',fontsize=9)
fig.text(.5,.015,'Steady generator balance; constant material properties and no thermal loss in heating. No machine or rock-processing performance measured.',ha='center',fontsize=9)
fig.tight_layout(rect=[0,.10,1,.93])
for ext in ['png','svg','pdf']:fig.savefig(P/f'energy-budget.{ext}',dpi=200)
meta={'title':'Ideal generator and phase-change energy ledgers','status':'Analytical hypothetical teaching model, not measured performance','parameters':{'generator_torque_Nm':1,'efficiencies':[0,.5,.8,1],'rpm_range':[0,600],'rpm_step':5,'specific_heat_J_kg_K':800,'initial_temperature_C':20,'transition_temperature_C':120,'latent_heat_J_kg':200000,'mass_kg_range':[0,5],'mass_step_kg':.05,'absorbed_power_W':[100,500,1000]},'equations':{'omega':'rpm*2*pi/60','mechanical_input':'torque*omega','electrical_output':'efficiency*mechanical_input','loss':'(1-efficiency)*mechanical_input','sensible_heat':'mass*c*(transition_temperature-initial_temperature)','latent_heat':'mass*latent_specific_heat','ideal_completion_time':'(sensible_heat+latent_heat)/absorbed_power'},'rows':{'generator':len(g),'heating':len(h)},'examples':{'generator_600rpm':generator(600,1,.8),'heating_1kg_500W':heating(1,500)},'limits':['The assigned generator efficiency is constant and not derived from machine physics. No startup, storage, field excitation supply or speed-dependent losses are resolved.','The mechanical power ledger describes nonnegative steady magnitudes, not signed motor/generator reversal.','Heating assumes a hypothetical inert nonreacting material, constant specific heat, a single sharp transition, and completion of that phase change. No real rock properties are claimed.','Absorbed heating power is power delivered to the sample. Wall-plug power, coupling losses, container heat capacity and environmental losses are excluded.','The heating calculation stops at the specified endpoint. It is not a temperature-time simulation beyond phase completion.','No excavation geometry, gas generation, melt transport, cooling, material performance, cost or emissions result is computed.','For positive energy and zero absorbed power there is no finite completion time; null is returned. Zero mass requires zero energy and zero time.'],'checks':{'all_pass':True,'generator_work_per_revolution_max_difference_W':err,'row_conservation_checked':len(g),'heat_integral_and_power_time_checked':len(h),'zero_power_and_zero_mass_checked':True,'invalid_cases_rejected':bad},'sources':[{'title':'NASA: The Dynamo Process','url':'https://pwg.gsfc.nasa.gov/Education/dynamos.htm','scope':'Mechanical source of generator energy; no efficiency value adopted'}]}
(P/'study.json').write_text(json.dumps(meta,indent=2)+'\n')
print(json.dumps(meta['checks']))
