gas1.TPX = T1,P1,q rho1 = gas1.density # Find CJ speed cj_speed = CJspeed(P1, T1, q, mech) # CJ state gas = PostShock_eq(cj_speed,P1, T1, q, mech) print('CJ computation for '+mech+' with composition ',q) P2 = gas.P T2 = gas.T q2 = gas.X rho2 = gas.density w2 = rho1/rho2*cj_speed u2= cj_speed-w2 Umin = soundspeed_eq(gas) print('CJ speed '+str(cj_speed)+' (m/s)'); print('CJ State'); print(' Pressure '+str(P2)+' (Pa)'); print(' Particle velocity '+str(u2)+' (m/s)'); # reflected shock from CJ detonation gas3 = ct.Solution(mech); [p3,Ur,gas3] = reflected_eq(gas1,gas,gas3,cj_speed); v3 = 1./gas.density print('Reflected shock (equilibrium) computation for ',mech,' with composition ',q) print(' Reflected wave speed '+str(Ur)+' (m/s)') print(' Reflected shock pressure '+str(gas3.P)+' (Pa)') # Bounds for reflected shock speed Umax = Ur+u2 print(' Maximum reflected shock speed '+str(Umax)+' (m/s)')
# SET TYPE OF EXPANSION COMPUTATION # Frozen: EQ = false Equilibrium EQ = true EQ = False ## # set the initial state and compute properties P1 = 100000. T1 = 300. q = 'O2:0.2095 N2:0.7808 CO2:0.0004 Ar:0.0093' mech = 'airNASA9ions.cti' gas = ct.Solution(mech) gas.TPX = T1, P1, q if EQ: # use this for equilibrium expansion gas.equilibrate('TP') a1 = soundspeed_eq(gas) else: # use this for frozen expansion a1 = soundspeed_fr(gas) x1 = gas.X rho1 = gas.density v1 = 1 / rho1 s1 = gas.entropy_mass h1 = gas.enthalpy_mass ## # Set freestream velocity .01% above sound speed U = a1 * 1.0001 mu_min = np.arcsin(a1 / U) # Mach angle # check to see if flow speed is supersonic if U < a1:
# Find CJ speed cj_speed = CJspeed(P1, T1, q, mech) # Evaluate gas state gas = PostShock_eq(cj_speed,P1, T1, q, mech) # Evaluate properties of gas object T2 = gas.T P2 = gas.P R2 = gas.density V2 = 1/R2 S2 = gas.entropy_mass w2 = gas1.density*cj_speed/R2 u2 = cj_speed - w2 x2 = gas.X a2_eq = soundspeed_eq(gas) a2_fr = soundspeed_fr(gas) gamma2_fr = a2_fr**2*R2/P2 gamma2_eq = a2_eq**2*R2/P2 # Print out set_printoptions(precision=4) print('CJ computation for '+mech+' with composition '+q) print('CJ Parameters') print(' UCJ '+str(cj_speed)+' (m/s)') print(' Pressure '+str(P2)+' (Pa)') print(' Temperature '+str(T2)+' (K)') print(' Density '+str(R2)+' (kg/m3)') print(' Entropy '+str(S2)+' (J/kg-K)') print(' w2 (wave frame) '+str(w2)+' (m/s)') print(' u2 (lab frame) '+str(u2)+' (m/s)')
# mech = Cantera mechanism File name # calculate composition based on percentage of propane HC_comp = propane_comp O_comp = (100 - HC_comp) * 0.21 N2_comp = (100 - HC_comp) * 0.79 # initial pressures and temperatures P1 = initpress[IC] * 1000 T1 = inittemp[IC] q = {'C3H8': HC_comp, 'O2': O_comp, 'N2': N2_comp} mech = 'gri30.cti' # make solution and initialize to starting conditions, store density gas_initial = ct.Solution(mech) gas_initial.TPX = T1, P1, q rho_1 = gas_initial.density csound = soundspeed_eq(gas_initial) # compute CJ speed, print, and add to speed array [cj_speed, R2, plot_data] = CJspeed(P1, T1, q, mech, fullOutput=True) # compute equilibrium CJ state parameters gas = PostShock_eq(cj_speed, P1, T1, q, mech) ae = soundspeed_eq(gas) af = soundspeed_fr(gas) rho_2 = gas.density gammae = ae**2 * rho_2 / gas.P gammaf = af**2 * rho_2 / gas.P w2 = cj_speed * rho_1 / rho_2
a3 = [soundspeed_fr(driver_gas)] u3 = [0] elif CASE_DRIVER=='uv': ## Set initial state for driven section - combustion driver shock tube # using constant volume state approximation for reacting driver, use equilibrium expansion EQ_EXP = True P_driver_fill = P1; T_driver_fill = T1; q4 = 'C2H2:1.0 O2:2.5' driver_mech = 'gri30_highT.cti' driver_gas = ct.Solution(driver_mech) driver_gas.TPX = T_driver_fill,P_driver_fill,q4 driver_gas.equilibrate('UV') P4 = driver_gas.P T4 = driver_gas.T a3 = [soundspeed_eq(driver_gas)] u3 = [0] elif CASE_DRIVER=='cj': ## Detonation driver with CJ wave moving away from diaphragm EQ_EXP = True P_driver_fill = P1; T_driver_fill = T1; q4 = 'C2H2:1.0 O2:2.5' driver_mech = 'gri30_highT.cti' driver_gas = ct.Solution(driver_mech) driver_gas.TPX = T_driver_fill,P_driver_fill,q4 rho4 = driver_gas.density cj_speed = CJspeed(P_driver_fill, T_driver_fill, q4, driver_mech) driver_gas = PostShock_eq(cj_speed,P_driver_fill, T_driver_fill, q4, driver_mech) P4 = driver_gas.P T4 = driver_gas.T
# User can adjust the increment and endpoint to get a smooth output curve and reliable integral. # Could also try a logarithmic range for some cases. for v in np.linspace(v1,v2,num=1000): rho.append(1/v) x = gas.X # update mole fractions based on last gas state # workaround to avoid unsized object error when only one species in a .cti file # (flagged to be fixed in future Cantera version) if gas.n_species > 1: gas.SVX = s1,1/rho[-1],x else: gas.SV = s1,1/rho[-1] if EQ: # required for equilibrium expansion gas.equilibrate('SV') a.append(soundspeed_eq(gas)) else: # use this for frozen expansion a.append(soundspeed_fr(gas)) h.append(gas.enthalpy_mass) u.append(np.sqrt(2*(h0-h[-1]))) M.append(u[-1]/a[-1]) mu.append(np.arcsin(1/M[-1])) T.append(gas.T) P.append(gas.P) # convert lists to Numpy arrays to allow vectorized operations M = np.asarray(M); rho = np.asarray(rho); P = np.asarray(P)
print('Case '+str(i+1)+' Fuel concentration: '+str(x[ifuel]*100)+'%') print('Molefractions') print(' fuel ('+fuel+'): '+str(x[ifuel])) print(' oxidizer ('+oxidizer+'): '+ str(x[ioxidizer])) print(' diluent ('+diluent+'): '+ str(x[idiluent])) print(' Phi = '+str(phi[-1])) gas.TPX = T1,P1,x init_rho = gas.density init_af = soundspeed_fr(gas) # Constant Pressure Explosion State gas.equilibrate('HP') hp_rho.append(gas.density) hp_exp.append(init_rho/hp_rho[-1]) hp_T.append(gas.T) hp_ae.append(soundspeed_eq(gas)) # Constant Volume Explosion State gas.TPX = T1,P1,x gas.equilibrate('UV') uv_P.append(gas.P) uv_T.append(gas.T) uv_ae.append(soundspeed_eq(gas)) # CJ speed gas.TPX = T1,P1,x Ucj.append(CJspeed(P1, T1, x, mech)) # vN state gas1 = PostShock_fr(Ucj[-1], P1, T1, x, mech) vn_T.append(gas1.T)
Tc = 300; Pc = 1e6; for helium in range(1,21): q = 'O2:1 H2:2.001 He:'+str(helium) gas.TPX = Tc,Pc,q print('Computing chamber conditions and isentropic quasi 1-d flow for '+q+' using '+mech) # compute equilibrium properties for constant pressure burn gas.equilibrate('HP') qc = gas.X # Isentropic expansion from stagnation state to low pressure Tt = gas.T St = gas.entropy_mass Ht = gas.enthalpy_mass Pt = gas.P Rt = gas.density ct = soundspeed_eq(gas) gammat_eq = ct**2*Rt/Pt print('Total pressure '+str(Pt)+' (Pa)') print('Total temperature '+str(Tt)+' (K)') print('Total density '+str(Rt)+' (kg/m3)') print('Total entropy '+str(St)+' (J/kg-K)') print('Total sound speed (equilibrium) '+str(ct)+' (m/s)') print('gamma2 (equilibrium) '+str(gammat_eq)+' (m/s)') # ambient pressure for impulse computation Pa = 0.0 print('Computing isentropic expansion') pp = Pt P = []; R = []; T = []; c = []; M = []; h_eq = []; u_eq = []; Isp_eq = []; h_fr = []; u_fr = []; Isp_fr = [];
# stoichiometric H2-air detonation at nominal atmospheric conditions P1 = 100000. T1 = 295 q = 'H2:2 O2:1 N2:3.76' mech = 'Mevel2017.cti' gas_initial = ct.Solution(mech) gas_initial.TPX = T1, P1, q rho_1 = gas_initial.density # compute CJ speed [cj_speed, R2, plot_data] = CJspeed(P1, T1, q, mech, fullOutput=True) # compute equilibrium CJ state parameters gas = PostShock_eq(cj_speed, P1, T1, q, mech) ae = soundspeed_eq(gas) af = soundspeed_fr(gas) rho_2 = gas.density gammae = ae**2 * rho_2 / gas.P gammaf = af**2 * rho_2 / gas.P w2 = cj_speed * rho_1 / rho_2 u2 = cj_speed - w2 print('CJ computation for ' + mech + ' with composition ' + q) print('Initial conditions: P1 = %.3e Pa & T1 = %.2f K' % (P1, T1)) print('CJ Speed %.1f m/s' % cj_speed) print('CJ State') print(' Pressure %.3e Pa' % gas.P) print(' Temperature %.1f K' % gas.T) print(' Density %.3f kg/m3' % gas.density) print(' Entropy %.3e J/K' % gas.entropy_mass) print(' w2 (wave frame) %.1f m/s' % w2)
print(' Density '+str(rho1)+' (kg/m3)') print(' Sound speed (frozen) '+str(a1_fr)+' (m/s)') print(' Enthalpy '+str(h1)+' (J/kg)') print(' Entropy '+str(s1)+' (J/kg K)') print(' gamma (frozen) '+str(gamma1_fr)+' ') ## # Find CJ speed U_CJ = CJspeed(P1, T1, q, mech) # Evaluate CJ gas state gas = PostShock_eq(U_CJ,P1, T1, q, mech) x2 = gas.X P2 = gas.P T2 = gas.T rho2 = gas.density a2_eq = soundspeed_eq(gas) s2 = gas.entropy_mass h2 = gas.enthalpy_mass w2 = rho1*U_CJ/rho2 u2 = U_CJ-w2 gamma2_eq = a2_eq**2*rho2/P2 print('State 2 - CJ ') print(' CJ speed '+str(U_CJ)+' (m/s)') print(' Pressure '+str(P2)+' (Pa)') print(' Temperature '+str(T2)+' (K)') print(' Density '+str(rho2)+' (kg/m3)') print(' Enthalpy '+str(h2)+' (J/kg)') print(' Entropy '+str(s2)+' (J/kg K)') print(' w2 (wave frame) '+str(w2)+' (m/s)') print(' u2 (lab frame) '+str(u2)+' (m/s)')
gas.equilibrate('SV') P.append(gas.P) R.append(gas.density) T.append(gas.T) # species X = gas.X xH.append(X[iH]) xO.append(X[iO]) xOH.append(X[iOH]) xH2.append(X[iH2]) xO2.append(X[iO2]) xH2O.append(X[iH2O]) # gamma computed from Gruneisen coefficient grun_eq.append(gruneisen_eq(gas)) grun_fr.append(gruneisen_fr(gas)) a_eq.append(soundspeed_eq(gas)) a_fr.append(soundspeed_fr(gas)) # logarithmic slope of isentropes in P-V coordinates kappa_fr.append(a_fr[-1]**2*R[-1]/P[-1]) kappa_eq.append(a_eq[-1]**2*R[-1]/P[-1]) # logarithmic slope of isentropes in T-V coordinates # equilibrium gas.SV = S2,1.01*V gas.equilibrate('SV') T_2 = gas.T gas.SV = S2,0.99*V gas.equilibrate('SV') T_1 = gas.T tvdvdt_eq.append(-V*(T_2-T_1)/(0.02*V)/T[-1]) # frozen gas.SVX = S2,V*1.01,X2
print(' Density ' + str(rho1) + ' (kg/m3)') print(' Sound speed (frozen) ' + str(a1_fr) + ' (m/s)') print(' Enthalpy ' + str(h1) + ' (J/kg)') print(' Entropy ' + str(s1) + ' (J/kg K)') print(' gamma (frozen) ' + str(gamma1_fr) + ' ') ## # Find CJ speed U_CJ = CJspeed(P1, T1, q, mech) # Evaluate CJ gas state gas = PostShock_eq(U_CJ, P1, T1, q, mech) x2 = gas.X P2 = gas.P T2 = gas.T rho2 = gas.density a2_eq = soundspeed_eq(gas) s2 = gas.entropy_mass h2 = gas.enthalpy_mass w2 = rho1 * U_CJ / rho2 u2 = U_CJ - w2 gamma2_eq = a2_eq**2 * rho2 / P2 print('State 2 - CJ ') print(' CJ speed ' + str(U_CJ) + ' (m/s)') print(' Pressure ' + str(P2) + ' (Pa)') print(' Temperature ' + str(T2) + ' (K)') print(' Density ' + str(rho2) + ' (kg/m3)') print(' Enthalpy ' + str(h2) + ' (J/kg)') print(' Entropy ' + str(s2) + ' (J/kg K)') print(' w2 (wave frame) ' + str(w2) + ' (m/s)') print(' u2 (lab frame) ' + str(u2) + ' (m/s)')
print('Computing CJ state and isentrope for ' + q + ' using ' + mech) # compute CJ speed cj_speed = CJspeed(P1, T1, q, mech) # compute equilibrium CJ state gas = PostShock_eq(cj_speed, P1, T1, q, mech) T2 = gas.T P2 = gas.P D2 = gas.density V2 = 1. / D2 S2 = gas.entropy_mass w2 = D1 * cj_speed / D2 u2 = cj_speed - w2 a2_eq = soundspeed_eq(gas) a2_fr = soundspeed_fr(gas) gamma2_fr = a2_fr * a2_fr * D2 / P2 gamma2_eq = a2_eq * a2_eq * D2 / P2 print('CJ speed = %.2f (m/s)' % (cj_speed)) print('CJ State') print(' Pressure %.2f (Pa) ' % (P2)) print(' Temperature %.2f (K) ' % (T2)) print(' Density %.3f (kg/m3) ' % (D2)) print(' Entropy %.3f (J/kg-K) ' % (S2)) print(' w2 (frozen) %.2f (m/s)' % (w2)) print(' u2 (frozen) %.2f (m/s)' % (u2)) print(' a2 (frozen) %.2f (m/s)' % (a2_fr)) print(' a2 (equilibrium) %.2f (m/s)' % (a2_eq)) print(' gamma2 (frozen) %.4f ' % (gamma2_fr))
PRpa = (P1 - r1**2 * U1**2 * (v2 - v1)) PR.append(PRpa / ct.one_atm) # Pressure in atmospheres print('Rayleigh Line Array Created') ## Compute product Hugoniot # Use CV combust as initial state gas.TPX = T1, P1, q gas.equilibrate('UV') Ta = gas.T va = 1 / gas.density PH2 = [gas.P / ct.one_atm] vH2 = [va] Grun = [gruneisen_eq(gas)] gamma = [(soundspeed_eq(gas))**2 / (vH2[0] * gas.P)] denom = [1 + Grun[0] * (vH2[0] - v1) / (2 * vH2[0])] i = 0 vb = va while vb > vmin: i = i + 1 vb = va - i * .01 # use new volume and previous temperature to find next state fval = fsolve(hug_eq, Ta, args=(vb, h1, P1, v1, gas)) gas.TD = fval, 1 / vb PH2.append(gas.P / ct.one_atm) vH2.append(vb) Grun.append(gruneisen_eq(gas)) gamma.append((soundspeed_eq(gas))**2 / (vH2[-1] * gas.P))
# User can adjust the increment and endpoint to get a smooth output curve and reliable integral. # Could also try a logarithmic range for some cases. for v in np.linspace(v1, v2, num=1000): rho.append(1 / v) x = gas.X # update mole fractions based on last gas state # workaround to avoid unsized object error when only one species in a .cti file # (flagged to be fixed in future Cantera version) if gas.n_species > 1: gas.SVX = s1, 1 / rho[-1], x else: gas.SV = s1, 1 / rho[-1] if EQ: # required for equilibrium expansion gas.equilibrate('SV') a.append(soundspeed_eq(gas)) else: # use this for frozen expansion a.append(soundspeed_fr(gas)) h.append(gas.enthalpy_mass) u.append(np.sqrt(2 * (h0 - h[-1]))) M.append(u[-1] / a[-1]) mu.append(np.arcsin(1 / M[-1])) T.append(gas.T) P.append(gas.P) # convert lists to Numpy arrays to allow vectorized operations M = np.asarray(M) rho = np.asarray(rho) P = np.asarray(P)
print(' Pressure '+str(P1)+' (Pa)') print(' Temperature '+str(T1)+' (K)') print(' Density '+str(rho1)+' (kg/m3)') print(' gamma2 (based on frozen sound speed) '+str(gamma1_fr)+' (m/s)') print(' Specific heat at constant pressure '+str(gas1.cp_mass)+' (J/kg K)') if transport: print(' viscosity '+str(mu1)+' (kg/m s)') print(' viscosity (kinematic)'+str(nu1)+' (m2/s)') print(' thermal conductivity '+str(kcond1)+' (W/m K)') print(' thermal diffusivity '+str(kdiff1)+' (m2/s)') print(' Prandtl number '+str(Pr)) ## Postshock (Frozen) gas2 = PostShock_fr(speed,P1,T1,q,mech) af2 = soundspeed_fr(gas2) ae2 = soundspeed_eq(gas2) P2 = gas2.P T2 = gas2.T S2 = gas2.entropy_mass rho2 = gas2.density gamma2_fr = af2**2*rho2/P2 gamma2_eq = ae2**2*rho2/P2 w2 = rho1*speed/rho2 u2 = speed - w2 if transport: mu = gas2.viscosity nu = mu/rho2 kcond = gas2.thermal_conductivity kdiff = kcond/(rho2*gas2.cp_mass) Pr = mu*gas2.cp_mass/kcond