"""Recover turbine-exit heat inside the declared cold-pressure loop. New core pressure losses and optional warm-air bypass are explicit. No startup, installed mass or vehicle credit is implied by the steady resource comparison. """ from pathlib import Path import json,math,hashlib import numpy as np from CoolProp.CoolProp import PropsSI import cold_pressure_loop as B R=Path(__file__).resolve().parents[1] def cycle(effectiveness=.85,pressure_loss=1000.,air_bypass=True): cold=90.;hot=290.;source=300.;et=ec=.8;M=B.M;PL=B.PL;PH=B.PH;DP=B.DP;TA=B.TA base=B.cycle(coupling='shaft') h1=PropsSI('H','P',PL,'T',cold,'Nitrogen');s1=PropsSI('S','P',PL,'T',cold,'Nitrogen') p2=PH+DP+pressure_loss;p4=PL+DP+pressure_loss h2=h1+(PropsSI('H','P',p2,'S',s1,'Nitrogen')-h1)/ec h3=PropsSI('H','P',PH,'T',hot,'Nitrogen');s3=PropsSI('S','P',PH,'T',hot,'Nitrogen') h4=h3-et*(h3-PropsSI('H','P',p4,'S',s3,'Nitrogen')) t2=PropsSI('T','P',PH+DP,'H',h2,'Nitrogen');t4=PropsSI('T','P',PL+DP,'H',h4,'Nitrogen') qmax=M*max(0.,min(PropsSI('H','P',PH+DP,'T',t4,'Nitrogen')-h2,h4-PropsSI('H','P',PL+DP,'T',t2,'Nitrogen'))) recovered=effectiveness*qmax;h2r=h2+recovered/M;h4r=h4-recovered/M wc=M*(h2-h1);wt=M*(h3-h4);qh=M*(h3-h2r);qc=M*(h4r-h1) assert qh>0 and qc>0 ma=B.MAIR*qh/base['heat_supplied_J'] if air_bypass else B.MAIR assert 0=0 and entropy>=0 def conductance(h,d): assert min(d)>0 a,b=d[:-1],d[1:];lm=np.divide(b-a,np.log(b/a),out=(a+b)/2,where=abs(b-a)>1e-9) return float(sum(M*np.diff(h)/B.DUTY/lm)) hs=np.linspace(h2r,h3,161);tc=np.array([PropsSI('T','P',PH,'H',h,'Nitrogen') for h in hs]);th=source_out+(hs-h2r)/(h3-h2r)*(source_in-source_out);warm=conductance(hs,th-tc) hc=np.linspace(h1,h4r,161);tcr=np.array([PropsSI('T','P',PL,'H',h,'Nitrogen') for h in hc]);cold_ua=conductance(hc,tcr-B.tb) regen=0.;pinch=None if recovered>0: hr=np.linspace(h2,h2r,161);trc=np.array([PropsSI('T','P',PH+DP,'H',h,'Nitrogen') for h in hr]);trh=np.array([PropsSI('T','P',PL+DP,'H',h4r+h-h2,'Nitrogen') for h in hr]);regen=conductance(hr,trh-trc);pinch=float(min(trh-trc)) if effectiveness==0 and pressure_loss==0: assert abs(net-base['net_steady_DC_J'])<1e-7 and abs(boil-base['coolant_nitrogen_boiloff_kg'])<1e-9 return {'recuperator_effectiveness_target':effectiveness,'recuperator_pressure_loss_each_side_Pa_target':pressure_loss,'warm_air_bypass_enabled':air_bypass, 'net_steady_DC_J':net,'generator_DC_J':generated,'warm_air_blower_DC_J':fan,'compressor_motor_DC_J':motor,'controls_DC_J':controls, 'turbine_shaft_J':wt,'compressor_shaft_J':wc,'internally_recovered_heat_J':recovered,'heat_supplied_J':qh,'cold_bath_heat_received_J':qc, 'coolant_nitrogen_boiloff_kg':boil,'unspent_coolant_before_unknown_startup_kg':6-boil,'prepared_heat_source_air_kg':B.MAIR,'active_heat_source_air_kg':ma,'bypassed_air_retained_at_300K_kg':B.MAIR-ma, 'heat_source_final_K':source_out,'compressed_gas_before_recuperator_K':t2,'compressed_gas_after_recuperator_K':PropsSI('T','P',PH+DP,'H',h2r,'Nitrogen'),'turbine_gas_before_recuperator_K':t4,'turbine_gas_after_recuperator_K':PropsSI('T','P',PL+DP,'H',h4r,'Nitrogen'),'warm_core_required_UA_W_K':warm,'cold_core_required_UA_W_K':cold_ua,'recuperator_required_UA_W_K':regen,'recuperator_minimum_pinch_K':pinch, 'total_resource_availability_used_J':resources,'exergy_destroyed_J':destroyed,'total_entropy_generation_J_K':entropy,'complete_first_law_residual_J':residual,'exergy_entropy_identity_residual_J':identity, 'net_gain_from_base_J':net-base['net_steady_DC_J'],'coolant_reduction_from_base_fraction':1-boil/base['coolant_nitrogen_boiloff_kg']} def main(): rows=[cycle(e,p,b) for e in [0.,.5,.7,.85,.9] for p in [0.,500.,1000.,2000.] for b in [False,True]] selected=cycle();base=B.cycle(coupling='shaft');fixed=cycle(air_bypass=False) out={'physical_measurements':0,'classification':'STEADY_RECUPERATOR_OPPORTUNITY_NOT_CLOSED_HARDWARE','selected':selected,'base_common_shaft_loop':base,'same_full_airflow_control':fixed,'cases':rows, 'comparison':'Same prepared 6 kg liquid nitrogen and 30 kg air at 300 K available. The bypass mode processes less of the prepared air and retains the rest unchanged. It matches the baseline active-stream outlet temperature. The recuperator has an explicit pressure loss on each nitrogen side.', 'limits':['Machine efficiencies, generator/motor and shaft targets are inherited from the pressure-loop screen.','Recuperator effectiveness, pressure loss and required conductance are targets, not a core geometry or measured map.','Startup, working-gas inventory, wetted-metal transients and installed mass remain unqualified.','The extra core is not free hardware. No driving-energy or vehicle-performance credit is assigned.']} (R/'output/recuperated-pressure.json').write_text(json.dumps(out,indent=2)+'\n') print(json.dumps({'selected':selected,'fixed_airflow_net_J':fixed['net_steady_DC_J'],'case_count':len(rows)},indent=2)) if __name__=='__main__':main()