def test_setInitialTime(self): self.makeReactors(P1=10*ct.OneAtm, X1='AR:1.0', X2='O2:1.0') self.net.rtol = 1e-12 valve = ct.Valve(self.r1, self.r2) mdot = lambda dP: 5e-3 * np.sqrt(dP) if dP > 0 else 0.0 valve.setValveCoeff(mdot) t0 = 0.0 tf = t0 + 0.5 self.net.advance(tf) self.assertNear(self.net.time, tf) p1a = self.r1.thermo.P p2a = self.r2.thermo.P self.makeReactors(P1=10*ct.OneAtm, X1='AR:1.0', X2='O2:1.0') self.net.rtol = 1e-12 valve = ct.Valve(self.r1, self.r2) mdot = lambda dP: 5e-3 * np.sqrt(dP) if dP > 0 else 0.0 valve.setValveCoeff(mdot) t0 = 0.2 self.net.setInitialTime(t0) tf = t0 + 0.5 self.net.advance(tf) self.assertNear(self.net.time, tf) p1b = self.r1.thermo.P p2b = self.r2.thermo.P self.assertNear(p1a, p1b) self.assertNear(p2a, p2b)
def test_valve_errors(self): self.make_reactors() res = ct.Reservoir() with self.assertRaises(ct.CanteraError): # Must assign contents of both reactors before creating Valve v = ct.Valve(self.r1, res) v = ct.Valve(self.r1, self.r2) with self.assertRaises(ct.CanteraError): # inlet and outlet cannot be reassigned v._install(self.r2, self.r1)
def runSinglePSR(T, P, moleFraction, factor, reactionIndex, targetSpc): start = time.time() # create the gas mixture gas = ct.Solution('HXD15_Battin_mech.xml') gas.TPX = T, P, moleFraction # create an upstream reservoir that will supply the reactor. The temperature, # pressure, and composition of the upstream reservoir are set to those of the # 'gas' object at the time the reservoir is created. upstream = ct.Reservoir(gas) # Now create the reactor object with the same initial state cstr = ct.IdealGasReactor(gas) # Set its volume to 80 cm^3. In this problem, the reactor volume is fixed, so # the initial volume is the volume at all later times. cstr.volume = 80.0*1.0e-6 # Connect the upstream reservoir to the reactor with a mass flow controller # (constant mdot). Set the mass flow rate. vdot = 40* 1.0e-6 # m^3/s mdot = gas.density * vdot # kg/s mfc = ct.MassFlowController(upstream, cstr, mdot=mdot) # now create a downstream reservoir to exhaust into. downstream = ct.Reservoir(gas) # connect the reactor to the downstream reservoir with a valve, and set the # coefficient sufficiently large to keep the reactor pressure close to the # downstream pressure. v = ct.Valve(cstr, downstream, K=1.0e-9) # create the network network = ct.ReactorNet([cstr]) # modify the A factor of the given reaction gas.set_multiplier(factor,reactionIndex-1) # now integrate in time t = 0.0 dt = 0.1 print "\n\n\n**************************\n***** Solving case %d *****\n"%(reactionIndex) while t < 30.0: print t t += dt network.advance(t) results = [] for spc in targetSpc: results.append(cstr.thermo[spc].X[0]*1.0e6) end = time.time() print 'Execution time is:' print end - start return [gas.reaction_equation(reactionIndex-1)] + results
def setup(self, T0, P0, mdot_fuel, mdot_ox): self.gas = ct.Solution('gri30.xml') # fuel inlet self.gas.TPX = T0, P0, "CH4:1.0" self.fuel_in = ct.Reservoir(self.gas) # oxidizer inlet self.gas.TPX = T0, P0, "N2:3.76, O2:1.0" self.oxidizer_in = ct.Reservoir(self.gas) # reactor, initially filled with N2 self.gas.TPX = T0, P0, "N2:1.0" self.combustor = ct.IdealGasReactor(self.gas) self.combustor.volume = 1.0 # outlet self.exhaust = ct.Reservoir(self.gas) # connect the reactor to the reservoirs self.fuel_mfc = ct.MassFlowController(self.fuel_in, self.combustor) self.fuel_mfc.set_mass_flow_rate(mdot_fuel) self.oxidizer_mfc = ct.MassFlowController(self.oxidizer_in, self.combustor) self.oxidizer_mfc.set_mass_flow_rate(mdot_ox) self.valve = ct.Valve(self.combustor, self.exhaust) self.valve.set_valve_coeff(1.0) self.net = ct.ReactorNet() self.net.add_reactor(self.combustor) self.net.max_err_test_fails = 10
def test_valve2(self): # Similar to test_valve1, but by disabling the energy equation # (constant T) we can compare with an analytical solution for # the mass of each reactor as a function of time self.makeReactors(P1=10*ct.OneAtm) self.r1.energyEnabled = False self.r2.energyEnabled = False valve = ct.Valve(self.r1, self.r2) k = 2e-5 valve.setValveCoeff(k) self.assertFalse(self.r1.energyEnabled) self.assertFalse(self.r2.energyEnabled) m1a = self.r1.thermo.density * self.r1.volume m2a = self.r2.thermo.density * self.r2.volume P1a = self.r1.thermo.P P2a = self.r2.thermo.P A = k * P1a * (1 + m2a/m1a) B = k * (P1a/m1a + P2a/m2a) for t in np.linspace(1e-5, 0.5): self.net.advance(t) m1 = self.r1.thermo.density * self.r1.volume m2 = self.r2.thermo.density * self.r2.volume self.assertNear(m2, (m2a - A/B) * np.exp(-B * t) + A/B) self.assertNear(m1a+m2a, m1+m2)
def test_valve3(self): # This case specifies a non-linear relationship between pressure drop # and flow rate. self.make_reactors(P1=10*ct.one_atm, X1='AR:0.5, O2:0.5', X2='O2:1.0') self.net.rtol = 1e-12 self.net.atol = 1e-20 valve = ct.Valve(self.r1, self.r2) mdot = lambda dP: 5e-3 * np.sqrt(dP) if dP > 0 else 0.0 valve.set_valve_coeff(mdot) Y1 = self.r1.Y kO2 = self.gas1.species_index('O2') kAr = self.gas1.species_index('AR') def speciesMass(k): return self.r1.Y[k] * self.r1.mass + self.r2.Y[k] * self.r2.mass mO2 = speciesMass(kO2) mAr = speciesMass(kAr) t = 0 while t < 1.0: t = self.net.step() p1 = self.r1.thermo.P p2 = self.r2.thermo.P self.assertNear(mdot(p1-p2), valve.mdot(t)) self.assertArrayNear(Y1, self.r1.Y) self.assertNear(speciesMass(kAr), mAr) self.assertNear(speciesMass(kO2), mO2)
def test_valve1(self): self.makeReactors(P1=10*ct.OneAtm, X1='AR:1.0', X2='O2:1.0') valve = ct.Valve(self.r1, self.r2) k = 2e-5 valve.setValveCoeff(k) self.assertEqual(self.r1.outlets, self.r2.inlets) self.assertTrue(self.r1.energyEnabled) self.assertTrue(self.r2.energyEnabled) self.assertTrue((self.r1.thermo.P - self.r2.thermo.P) * k, valve.mdot(0)) m1a = self.r1.thermo.density * self.r1.volume m2a = self.r2.thermo.density * self.r2.volume Y1a = self.r1.thermo.Y Y2a = self.r2.thermo.Y self.net.advance(0.1) m1b = self.r1.thermo.density * self.r1.volume m2b = self.r2.thermo.density * self.r2.volume self.assertTrue((self.r1.thermo.P - self.r2.thermo.P) * k, valve.mdot(0.1)) self.assertNear(m1a+m2a, m1b+m2b) Y1b = self.r1.thermo.Y Y2b = self.r2.thermo.Y self.assertArrayNear(m1a*Y1a + m2a*Y2a, m1b*Y1b + m2b*Y2b)
def run_single(self): gas=self.processor.solution reactorPressure=gas.P self.reactorPressure=self.processor.solution.P pressureValveCoefficient=self.pvalveCoefficient maxPressureRiseAllowed=self.maxPrise print(maxPressureRiseAllowed,self.reactorPressure,pressureValveCoefficient) #Build the system components for JSR fuelAirMixtureTank=ct.Reservoir(self.processor.solution) exhaust=ct.Reservoir(self.processor.solution) stirredReactor=ct.IdealGasReactor(self.processor.solution,energy=self.energycon,volume=self.reactor_volume) massFlowController=ct.MassFlowController(upstream=fuelAirMixtureTank, downstream=stirredReactor,mdot=stirredReactor.mass/self.residence_time) pressureRegulator=ct.Valve(upstream=stirredReactor,downstream=exhaust,K=pressureValveCoefficient) reactorNetwork=ct.ReactorNet([stirredReactor]) if bool(self.observables) and self.kineticSens==1: for i in range(gas.n_reactions): stirredReactor.add_sensitivity_reaction(i) if self.kineticSens and bool(self.observables)==False: #except: print('Please supply a non-empty list of observables for sensitivity analysis or set kinetic_sens=0') if self.physicalSens==1 and bool(self.observables)==False:
def setUp(self): self.gas = ct.Solution('h2o2.xml') # create a reservoir for the fuel inlet, and set to pure methane. self.gas.TPX = 300.0, ct.one_atm, 'H2:1.0' fuel_in = ct.Reservoir(self.gas) fuel_mw = self.gas.mean_molecular_weight # Oxidizer inlet self.gas.TPX = 300.0, ct.one_atm, 'O2:1.0, AR:3.0' oxidizer_in = ct.Reservoir(self.gas) oxidizer_mw = self.gas.mean_molecular_weight # to ignite the fuel/air mixture, we'll introduce a pulse of radicals. # The steady-state behavior is independent of how we do this, so we'll # just use a stream of pure atomic hydrogen. self.gas.TPX = 300.0, ct.one_atm, 'H:1.0' self.igniter = ct.Reservoir(self.gas) # create the combustor, and fill it in initially with a diluent self.gas.TPX = 300.0, ct.one_atm, 'AR:1.0' self.combustor = ct.IdealGasReactor(self.gas) # create a reservoir for the exhaust self.exhaust = ct.Reservoir(self.gas) # compute fuel and air mass flow rates factor = 0.1 oxidizer_mdot = 4 * factor * oxidizer_mw fuel_mdot = factor * fuel_mw # The igniter will use a time-dependent igniter mass flow rate. def igniter_mdot(t, t0=0.1, fwhm=0.05, amplitude=0.1): return amplitude * math.exp( -(t - t0)**2 * 4 * math.log(2) / fwhm**2) # create and install the mass flow controllers. Controllers # m1 and m2 provide constant mass flow rates, and m3 provides # a short Gaussian pulse only to ignite the mixture m1 = ct.MassFlowController(fuel_in, self.combustor, mdot=fuel_mdot) m2 = ct.MassFlowController(oxidizer_in, self.combustor, mdot=oxidizer_mdot) m3 = ct.MassFlowController(self.igniter, self.combustor, mdot=igniter_mdot) # put a valve on the exhaust line to regulate the pressure self.v = ct.Valve(self.combustor, self.exhaust, K=1.0) # the simulation only contains one reactor self.sim = ct.ReactorNet([self.combustor])
def test_valve3(self): # This case specifies a non-linear relationship between pressure drop # and flow rate. self.makeReactors(P1=10*ct.OneAtm, X1='AR:1.0', X2='O2:1.0') valve = ct.Valve(self.r1, self.r2) mdot = lambda dP: 5e-3 * np.sqrt(dP) if dP > 0 else 0.0 valve.setValveCoeff(mdot) t = 0 while t < 1.0: t = self.net.step(1.0) p1 = self.r1.thermo.P p2 = self.r2.thermo.P self.assertNear(mdot(p1-p2), valve.mdot(t))
def psr_ss(soln_in, soln_out, p, T0, T, X0, X, tau): #sys.stdout.flush() """ find the steady state of a PSR :param mech: mechanism :param p: pressure (Pa) :param T0: temperature (K) of the inlet flow :param T: initial temperature (K) of the reactor :param tau: residence time (s) of the reactor :return reactor: """ vol = 1.0 # unit: m3 K = 1.0 t_end = tau * 100.0 #print 't_end = '+str(t_end) soln_out.TPX = T, p, X soln_in.TPX = T0, p, X0 inlet = ct.Reservoir(soln_in) reactor = ct.IdealGasReactor(soln_out) reactor.volume = vol vdot = vol/tau mdot = soln_out.density * vdot mfc = ct.MassFlowController(inlet, reactor, mdot=mdot) exhaust = ct.Reservoir(soln_out) valve = ct.Valve(reactor, exhaust, K=K) network = ct.ReactorNet([reactor]) try: network.advance(t_end) except RuntimeError as e: print '@'*10+'\nct.exceptions = \n'+str(e) #sys.exit() return None return soln_out
def run(self,): gas=processor.solution.P reactorPressure=gas pressureValveCoefficient=pvalveCoefficient maxPressureRiseAllowed=maxPrise #Build the system components for JSR fuelAirMixtureTank=ct.Reservoir(gas) exhaust=ct.Reservoir(gas) stirredReactor=ct.IdealGasReactor(gas,energy=energycon,volume=volume) massFlowController=ct.MassFlowController(upstream=fuelAirMixtureTank, downstream=stirredReactor,mdot=stirredReactor.mass/restime) pressureRegulator=ct.Valve(upstream=stirredReactor,downstream=exhaust,K=pressureValveCoefficient) reactorNetwork=ct.ReactorNet([stirredReactor]) if bool(self.moleFractionObservables) and self.kineticSens==1: for i in range(gas.n_reactions): stirredReactor.add_sensitivity_reaction(i) if self.kineticSens and bool(self.moleFractionObservables)==False: except:
# Connect the upstream reservoir to the reactor with a mass flow controller # (constant mdot). Set the mass flow rate to 1.25 sccm. sccm = 1.25 vdot = sccm * 1.0e-6 / 60.0 * ( (ct.one_atm / gas.P) * (gas.T / 273.15)) # m^3/s mdot = gas.density * vdot # kg/s mfc = ct.MassFlowController(upstream, cstr, mdot=mdot) # now create a downstream reservoir to exhaust into. downstream = ct.Reservoir(gas) # connect the reactor to the downstream reservoir with a valve, and set the # coefficient sufficiently large to keep the reactor pressure close to the # downstream pressure of 60 Torr. v = ct.Valve(cstr, downstream, K=1.0e-9) # create the network network = ct.ReactorNet([cstr]) # In[3]: # now integrate in time t = 0.0 dt = 0.1 states = ct.SolutionArray(gas, extra=['t']) while t < 300.0: t += dt network.advance(t) states.append(cstr.thermo.state, t=t)
r = ct.IdealGasReactor(gas) # define inlet state gas.TPX = T_inlet, p_inlet, comp_inlet inlet = ct.Reservoir(gas) # define injector state (gaseous!) gas.TPX = T_injector, p_injector, comp_injector injector = ct.Reservoir(gas) # define outlet pressure (temperature and composition don't matter) gas.TPX = T_ambient, p_outlet, comp_ambient outlet = ct.Reservoir(gas) # define ambient pressure (temperature and composition don't matter) gas.TPX = T_ambient, p_ambient, comp_ambient ambient_air = ct.Reservoir(gas) # set up connecting devices inlet_valve = ct.Valve(inlet, r) injector_mfc = ct.MassFlowController(injector, r) outlet_valve = ct.Valve(r, outlet) piston = ct.Wall(ambient_air, r) # convert time to crank angle def crank_angle(t): return np.remainder(2 * np.pi * f * t, 4 * np.pi) # set up IC engine parameters V_oT = V_H / (epsilon - 1.) A_piston = .25 * np.pi * d_piston ** 2 stroke = V_H / A_piston r.volume = V_oT piston.area = A_piston def piston_speed(t):
##################################################################### # load reaction mechanism gas = ct.Solution(reaction_mechanism, phase_name) # define initial state and set up reactor gas.TPX = T_inlet, p_inlet, comp_inlet cyl = ct.IdealGasReactor(gas) cyl.volume = V_oT # define inlet state gas.TPX = T_inlet, p_inlet, comp_inlet inlet = ct.Reservoir(gas) # inlet valve inlet_valve = ct.Valve(inlet, cyl) inlet_delta = np.mod(inlet_close - inlet_open, 4 * np.pi) inlet_valve.valve_coeff = inlet_valve_coeff inlet_valve.set_time_function( lambda t: np.mod(crank_angle(t) - inlet_open, 4 * np.pi) < inlet_delta) # define injector state (gaseous!) gas.TPX = T_injector, p_injector, comp_injector injector = ct.Reservoir(gas) # injector is modeled as a mass flow controller injector_mfc = ct.MassFlowController(injector, cyl) injector_delta = np.mod(injector_close - injector_open, 4 * np.pi) injector_t_open = (injector_close - injector_open) / 2. / np.pi / f injector_mfc.mass_flow_coeff = injector_mass / injector_t_open injector_mfc.set_time_function(lambda t: np.mod(
# Igniter modeled as pulsed radicals of Hydrogen fwhm=0.2 amp=0.1 t0=1 igniter_mdot= lambda t:amp*math.exp(-(t-t0)**2*4*math.log(2)/fwhm**2) ignite_pipe=ct.MassFlowController(igniter,combustor,mdot=igniter_mdot) # The premixed reactants have to enter the combustor mixer2combustor=ct.MassFlowController(mixer2,combustor) #exhaust valve v2=ct.Valve(combustor, exhaust2, K=100) #reactor Network sim=ct.ReactorNet([mixer2, combustor]) #time stepping t_init2=0 temp=numpy.zeros(100) comp=numpy.zeros(100) tfinal = 6.0 tnow = 0.0 Tprev = combustor.T
def JSR_isothermal_stdst(Temps, gas, Pressure, concentrations, residenceTime, reactorVolume, pressureValveCoefficient=0.01, maxsimulationTime=1000): stirredReactor_list = [] gas_List = [] for i in np.arange(len(Temps)): # Inlet gas conditions reactorTemperature = Temps[i] #Kelvin reactorPressure = Pressure #in atm. gas.TPX = reactorTemperature, reactorPressure, concentrations # Reactor parameters # Instrument parameters # This is the "conductance" of the pressure valve and will determine its efficiency in # holding the reactor pressure to the desired conditions. pressureValveCoefficient = pressureValveCoefficient # This parameter will allow you to decide if the valve's conductance is acceptable. If there # is a pressure rise in the reactor beyond this tolerance, you will get a warning maxPressureRiseAllowed = 0.001 # Simulation termination criterion maxSimulationTime = maxsimulationTime # seconds fuelAirMixtureTank = ct.Reservoir(gas) exhaust = ct.Reservoir(gas) stirredReactor = ct.IdealGasReactor(gas, energy='off', volume=reactorVolume) massFlowController = ct.MassFlowController(upstream=fuelAirMixtureTank, downstream=stirredReactor, mdot=stirredReactor.mass / residenceTime) pressureRegulator = ct.Valve(upstream=stirredReactor, downstream=exhaust, K=pressureValveCoefficient) reactorNetwork = ct.ReactorNet([stirredReactor]) # now compile a list of all variables for which we will store data columnNames = [ stirredReactor.component_name(item) for item in range(stirredReactor.n_vars) ] columnNames = ['pressure'] + columnNames # use the above list to create a DataFrame timeHistory = pd.DataFrame(columns=columnNames) # Start the stopwatch tic = time.time() # Set simulation start time to zero t = 0 counter = 1 while t < maxSimulationTime: t = reactorNetwork.step() # We will store only every 10th value. Remember, we have 1200+ species, so there will be # 1200 columns for us to work with if (counter % 10 == 0): #Extract the state of the reactor state = np.hstack([ stirredReactor.thermo.P, stirredReactor.mass, stirredReactor.volume, stirredReactor.T, stirredReactor.thermo.X ]) #Update the dataframe timeHistory.loc[t] = state counter += 1 # Stop the stopwatch toc = time.time() print('Simulation Took {:3.2f}s to compute, with {} steps'.format( toc - tic, counter)) #state = np.hstack([stirredReactor.thermo.P, stirredReactor.mass, #stirredReactor.volume, stirredReactor.T, stirredReactor.thermo.X]) #for j in np.arange(len(speciesName)): #component_final_X[i,j] = stirredReactor.get_state()[stirredReactor.component_index(speciesName[j])] #component_final_X[i,j] = state[stirredReactor.component_index(speciesName[j])+1] # We now check to see if the pressure rise during the simulation, a.k.a the pressure valve # was okay pressureDifferential = timeHistory['pressure'].max( ) - timeHistory['pressure'].min() if (abs(pressureDifferential / reactorPressure) > maxPressureRiseAllowed): print( "WARNING: Non-trivial pressure rise in the reactor. Adjust K value in valve" ) stirredReactor_list.append(stirredReactor) #%matplotlib notebook #plt.style.use('ggplot') #plt.style.use('seaborn-pastel') #plt.rcParams['axes.labelsize'] = 18 #plt.rcParams['xtick.labelsize'] = 14 #plt.rcParams['ytick.labelsize'] = 14 #plt.rcParams['figure.autolayout'] = True #plt.figure() #plt.semilogx(timeHistory.index, timeHistory['B2CO'],'-o') #plt.xlabel('Time (s)') #plt.ylabel(r'Mole Fraction : $X_{CO}$'); return stirredReactor_list, gas
def Engine(fname): import sys import numpy as np import cantera as ct import matplotlib.pyplot as plt import csv import time # from scipy.interpolate import interp1d import math from openpyxl import load_workbook def volume(t): theta = t * omega + IVC * np.pi / 180 #theta in radian; TDC corresponds to theta=0 v = V_min * (1 + ((r_c - 1) / 2) * (L / a + 1 - np.cos(theta) - np.sqrt(pow(L / a, 2) - pow(np.sin(theta), 2)))) return v def surfA(t): area = np.pi * math.pow(D, 2) / 4 SA = 2 * area + np.pi * D * volume(t) / area return SA def vel(t): #Note - the only input to the passed through function is time theta = t * omega + IVC * np.pi / 180 #TDC corresponds to theta=0 z = np.sqrt(pow(L / a, 2) - pow(np.sin(theta), 2)) v = omega * (V_min / A_p) * ( (r_c - 1) / 2) * np.sin(theta) * (1 + np.cos(theta) / z) return v def qfluxB( t): #Note - the only input to the passed through function is time rho = m_t / volume(t) T = (unb.mass * unb.thermo.T + bur.mass * bur.thermo.T) / (unb.mass + bur.mass) k = k_0 * pow(T, n_k) #W/m-K, for air mu = mu_0 * pow(T, n_mu) #kg/m-s, for air Re = rho * MPS * D / mu Nu = Nu_0 * pow(Re, n_Nu) h = Nu * k / D area = surfA(t) * bur.volume / volume(t) q = h * area * (bur.thermo.T - T_w) * HeatOn return q def qfluxU( t): #Note - the only input to the passed through function is time rho = m_t / volume(t) T = (unb.mass * unb.thermo.T + bur.mass * bur.thermo.T) / (unb.mass + bur.mass) k = k_0 * pow(T, n_k) #W/m-K, for air mu = mu_0 * pow(T, n_mu) #kg/m-s, for air Re = rho * MPS * D / mu Nu = Nu_0 * pow(Re, n_Nu) h = Nu * k / D area = surfA(t) * unb.volume / volume(t) q = h * area * (unb.thermo.T - T_w) / A_p * HeatOn return q def mflow( t): #Note - the only input to the passed through function is time theta = t * omega + IVC * np.pi / 180 #TDC corresponds to theta=0 if (theta >= theta_0r) and (theta <= theta_99r): # dxb=1/Deltathetar dxb = b * (m + 1) / Deltathetar * pow( ((theta - theta_0r) / Deltathetar), m) * math.exp(-b * pow( ((theta - theta_0r) / Deltathetar), m + 1)) else: dxb = 0 mf = burnflag * m_t * omega * dxb #mass flow unb to bur return mf ## MAIN - read inputs wb = load_workbook(fname) ws = wb.active r_c = ws['A1'].value # compression ratio L = ws['A2'].value # con rod length [m] D = ws['A3'].value # bore [m] stroke = ws['A4'].value # stroke [m] IVC = ws['A5'].value # IVC crank angle where TDC = 0 EVO = ws['A6'].value # EVO crank angle where TDC = 0 CrevOn = ws['A7'].value # Flag to determine whether to use crevice model HeatOn = ws[ 'A8'].value # Flag to determine whether to use heat transfer model crevfrac = ws['A9'].value # crevice volume fraction of minimum volume RPM = ws['A10'].value # engine speed b = ws['A11'].value # Wiebe b parameter m = ws['A12'].value # Wiebe m parameter theta_0 = ws['A13'].value # Wiebe combustion start parameter Deltatheta = ws['A14'].value # Wiebe combustion duration parameter eta_c = ws['A15'].value # combustion efficiency T_IVC = ws['A16'].value # IVC temperature [K] P_IVC = ws['A17'].value # IVC pressure [bar] T_w = ws['A18'].value # wall temperature [K] RON = ws['A19'].value # fuel RON MON = ws['A20'].value # fuel MON Phi = ws['A21'].value # equivalence ratio burnflag = ws[ 'A22'].value # fraction of mass transferred out of unburned zone k_0 = ws['A23'].value # thermal conductivity [W/m-K] n_k = ws['A24'].value # thermal conductivity temperature exponent mu_0 = ws['A25'].value # dynamic viscosity [kg/m-s] n_mu = ws['A26'].value # viscosity temperature exponent Nu_0 = ws['A27'].value # Nusselt number correlation scaling constant n_Nu = ws[ 'A28'].value # Nusselt number correlation Reynolds number exponent Chemflag = ws['A29'].value # End gas chemistry flag; 1=on Y_EGR = ws['A30'].value # EGR mass fraction Y_f_ref = ws['A31'].value # mass fraction of FUEL to the reformer Phi_ref = ws['A32'].value # reformer equivalence ratio Ref_comp = ws[ 'A33'].value # reformer composition: 0=PCI; 1=equilibriu; 2=ideal mechanism = ws['A34'].value # kinetic mechanism ## Engine geometry calculations a = stroke / 2 # crank radius A_p = np.pi * D * D / 4 # piston area [m^2] V_disp = A_p * stroke # displacement volume [m^3] V_min = V_disp / (r_c - 1) # TDC volume [m^3] V_max = V_min + V_disp # BDC volume [m^3] MPS = 2 * stroke * RPM / 60 #mean piston speed [m/s] ## Engine operating conditions theta_0r = theta_0 * np.pi / 180 #start of combustion in radians Deltathetar = Deltatheta * np.pi / 180 #combustion duration in radians theta_99r = theta_0r + Deltathetar * math.pow( -math.log(0.01) / b, 1 / m) #99% of mass burn for Wiebe omega = RPM * 2 * np.pi / 60 #rotation rate in radian per second based on RPM T_0 = T_IVC # K P_0 = 1e5 * P_IVC # Pa ct.suppress_thermo_warnings() env = ct.Solution('air.xml') env.TP = T_w, P_0 gas = ct.Solution(mechanism) x_init = np.zeros(gas.X.shape) c4 = ws['C41'].value #0.01 #n-butane c5 = ws['C42'].value #0.0 #iso-pentane c5_2 = ws['C43'].value #0.04 #n-pentane ic8 = ws['C44'].value #0.93 #Iso-octane c6 = ws['C45'].value #0.0 #added for 1-hexene nc7 = ws['C46'].value #0.0 #n-heptane c7_2 = ws['C47'].value #0.0 #tolune tmb = ws['C48'].value #0.02 #1,2,4 Trimethyl benzene eth = ws['C49'].value #0 #Ethanol x_init[gas.species_index('C4H10')] = c4 x_init[gas.species_index('IC5H12')] = c5 x_init[gas.species_index('NC5H12')] = c5_2 x_init[gas.species_index('C6H12-1')] = c6 x_init[gas.species_index('NC7H16')] = nc7 x_init[gas.species_index('C6H5CH3')] = c7_2 x_init[gas.species_index('IC8')] = ic8 x_init[gas.species_index('T124MBZ')] = tmb x_init[gas.species_index('C2H5OH')] = eth # x_init[gas.species_index('NO')]=150e-6 #150 ppm NO ## Determine global composition nC = (4 * c4) + (5 * c5) + (5 * c5_2) + (6 * c6) + (7 * nc7) + ( 8 * ic8) + (9 * tmb) + (2 * eth) + (7 * c7_2) nH = (10 * c4) + (12 * c5) + (12 * c5_2) + (12 * c6) + (16 * nc7) + ( 18 * ic8) + (12 * tmb) + (6 * eth) + (8 * c7_2) nO = 1 * eth x_init[gas.species_index('O2')] = (nC + nH / 4 - nO / 2) / Phi x_init[gas.species_index('N2')] = 3.76 * (nC + nH / 4 - nO / 2) / Phi gas.TPX = 300, 1e5, x_init y_global = gas.Y ## Determine EGR composition keep = np.zeros(gas.X.shape) keep[gas.species_index('N2')] = 1 keep[gas.species_index('O2')] = 1 keep[gas.species_index('CO2')] = 1 keep[gas.species_index('CO')] = 1 keep[gas.species_index('H2O')] = 1 keep[gas.species_index('H2')] = 1 gas.TPY = 300, 1e5, y_global gas.equilibrate('HP') #find equilibrium concentration y_EGR = gas.Y * keep # y_EGR[7:86]=0; #set minor species to zero; based on MECHANISM gas.Y = y_EGR #use Cantera to renormalize mass fraction y_EGR = gas.Y y_eng = 1 / (1 + Y_EGR) * y_global + +Y_EGR / (1 + Y_EGR) * y_EGR gas.TPX = 298, 1e5, x_init delta_uf = gas.standard_int_energies_RT * (ct.gas_constant * 298) / gas.molecular_weights tmax = (EVO - IVC) / 6 / RPM # time for one revolution step = 8 * (EVO - IVC) tim = np.linspace(tmax / step, tmax, step) theta = IVC + omega * tim * 180 / np.pi ## Cantera reactor setup gas.TPY = T_0, P_0, y_eng #reactants # gas.set_multiplier(1) #option to make reactants inert unb = ct.IdealGasReactor(gas) if Chemflag == 0: unb.chemistry_enabled = False gas.TPY = T_0, P_0, y_eng #products gas.equilibrate( 'HP' ) #make the products hot so that mass will burn when it moves into products bur = ct.IdealGasReactor(gas) r3 = ct.Reservoir(env) #outside world gas.TPY = T_w, P_0, y_eng crev = ct.IdealGasReactor(gas) #crevices crev.chemistry_enabled = False #no reaction in crevices piston = ct.Wall(unb, r3, velocity=vel, A=A_p, Q=qfluxU) flame = ct.Wall( unb, bur, K=0.01, A=A_p) #expansion rate K set to lowest value possible to have const P topland = ct.Wall( crev, r3, U=1e3) #heat xfer rate set to keep close to wall temperature head = ct.Wall(bur, r3, A=1, Q=qfluxB) mfc = ct.MassFlowController(unb, bur, mdot=mflow) V1 = ct.Valve(unb, crev) V1.set_valve_coeff(1e-6 * CrevOn) V2 = ct.Valve(crev, bur) V2.set_valve_coeff(1e-6 * CrevOn) sim = ct.ReactorNet([unb, bur, crev]) sim.atol = 1e-12 sim.rtol = 1e-6 initvol = 1e-3 unb.volume = (1 - initvol) * volume(0) bur.volume = initvol * volume(0) crev.volume = crevfrac * V_min m_t = unb.mass + bur.mass + crev.mass vol = np.zeros(step) T = np.zeros(step) vol2 = np.zeros(step) P = np.zeros(step) outdat = np.zeros((step, 13)) SpecMatrix = np.zeros((step, gas.X.shape[0])) #burnflag=0.82 #flag to stop combustion if unburned mass gets too low # y_old=unb.thermo.Y for i in range(step): #step): if math.fmod(i, 32) == 0: print(i / step) sim.advance(tim[i]) # burnflag=unb.mass/m_t # if unb.mass/m_t < 1-eta_c: # burnflag=0 outdat[i, 0] = IVC + tim[i] * RPM / 60 * 360 outdat[i, 1] = bur.thermo.P / 1e5 outdat[i, 2] = bur.thermo.T outdat[i, 3] = bur.mass / m_t outdat[i, 4] = qfluxB(tim[i]) outdat[i, 5] = unb.thermo.T outdat[i, 6] = qfluxU(tim[i]) * A_p outdat[i, 7] = unb.mass / m_t outdat[i, 8] = crev.mass / m_t outdat[i, 9] = volume(sim.time) outdat[i, 10] = -(unb.kinetics.net_production_rates * unb.thermo.molecular_weights ).dot(delta_uf) * unb.volume * 60 / (RPM * 360) outdat[i, 11] = m_t outdat[i, 12] = ct.gas_constant / gas.mean_molecular_weight outdat[i, 13] = volume(tim[i]) SpecMatrix[i] = gas.X # outdat[i,10]=-(unb.thermo.Y-y_old).dot(delta_uf)/(tim[2]-tim[1])*unb.mass # y_old=unb.thermo.Y return outdat, SpecMatrix
maxPressureRiseAllowed = 0.01 maxSimulationTime = 100 # seconds fuelAirMixtureTank = ct.Reservoir(gas) exhaust = ct.Reservoir(gas) stirredReactor = ct.IdealGasReactor(gas, energy='on', volume=reactorVolume) massFlowController = ct.MassFlowController(upstream=fuelAirMixtureTank, downstream=stirredReactor, mdot=stirredReactor.mass / residenceTime) pressureRegulator = ct.Valve(upstream=stirredReactor, downstream=exhaust, K=pressureValveCoefficient) reactorNetwork = ct.ReactorNet([stirredReactor]) # now compile a list of all variables for which we will store data columnNames = [ stirredReactor.component_name(item) for item in range(stirredReactor.n_vars) ] columnNames = ['pressure'] + columnNames # use the above list to create a DataFrame timeHistory = pd.DataFrame(columns=columnNames) # Start the stopwatch tic = time.time()
def JSR_steadystate2(gas,resTime,volume,kinetic_sens=0,physical_sens=0,observables=[],physical_params=['T','P'],energycon='off',pressureValveCoefficient=0.01,maxsimulationTime=1000,initial_conditions_gas=0,tempsens=0): # Inlet gas conditions are passed into function in the "gas" object, which is a cantera object # Reactor parameters passed into function as resTime and volume. Residence time and volume of JSR #kinetic sens and physical sens are optional parameters which are set to zero by default. Set them to 1 to #calculate sensitivity based on kinetic or physical parameters. If these are set to 1 you must pass #an array of all observables to calculate sensitivities for simtype='jsr' reactorPressure=gas.P # This is the "conductance" of the pressure valve and will determine its efficiency in # holding the reactor pressure to the desired conditions. It is an optional parameter pressureValveCoefficient=pressureValveCoefficient # This parameter will allow you to decide if the valve's conductance is acceptable. If there # is a pressure rise in the reactor beyond this tolerance, you will get a warning maxPressureRiseAllowed = 0.001 # Simulation termination criterion #maxSimulationTime = maxsimulationTime # seconds. An optional parameter fuelAirMixtureTank = ct.Reservoir(gas) exhaust = ct.Reservoir(gas) if initial_conditions_gas==0: stirredReactor = ct.IdealGasReactor(gas, energy=energycon, volume=volume) mdot=stirredReactor.mass/resTime else: stirredReactor = ct.IdealGasReactor(initial_conditions_gas,energy=energycon,volume=volume) dummyReactor = ct.IdealGasReactor(gas,energy=energycon,volume=volume) mdot=dummyReactor.mass/resTime massFlowController = ct.MassFlowController(upstream=fuelAirMixtureTank,downstream=stirredReactor,mdot=mdot) pressureRegulator = ct.Valve(upstream=stirredReactor,downstream=exhaust,K=pressureValveCoefficient) reactorNetwork = ct.ReactorNet([stirredReactor]) #This block adds kinetic sensitivity parameters for all reactions if desired. if kinetic_sens==1 and bool(observables): for i in range(gas.n_reactions): stirredReactor.add_sensitivity_reaction(i) if kinetic_sens==1 and bool(observables)==False: print('Please supply a non-empty list of observables for sensitivity analysis or set kinetic_sens=0') #if physical_sens==1 and bool(observables): #print('Placeholder') if physical_sens==1 and bool(observables)==False: print('Please supply a non-empty list of observables for sensitivity analysis or set physical_sens=0') # now compile a list of all variables for which we will store data columnNames = [stirredReactor.component_name(item) for item in range(stirredReactor.n_vars)] columnNames = ['pressure'] + columnNames # use the above list to create a DataFrame timeHistory = pd.DataFrame(columns=columnNames) # Start the stopwatch tic = time.time() #Names=[] #for l in np.arange(stirredReactor.n_vars): #Names.append(stirredReactor.component_name(l)) #global b #Names = [stirredReactor.component_name(item) for item in range(stirredReactor.n_vars)] #state = np.hstack([stirredReactor.mass, #stirredReactor.volume, stirredReactor.T, stirredReactor.thermo.X]) #print(state) #b=pd.DataFrame(data=state).transpose() #b.columns=Names #Establish a matrix to hold sensitivities for kinetic parameters, along with tolerances if kinetic_sens==1 and bool(observables): #senscolumnNames = ['Reaction']+observables senscolumnNames = observables #sensArray = pd.DataFrame(columns=senscolumnNames) senstempArray = np.zeros((gas.n_reactions,len(observables))) reactorNetwork.rtol_sensitivity = 1.0e-6 reactorNetwork.atol_sensitivity = 1.0e-6 dk=0.01 if physical_sens==1 and bool(observables): #psenscolumnNames = ['Parameter'] + observables #psensArray = pd.DataFrame(columns=senscolumnNames) pIndex=[[gas.T],physical_params,observables] psenstempArray = np.zeros((len(observables),len(physical_params))) tempSol=[] conditions=gas.TPX for i in np.arange(len(physical_params)): if physical_params[i]=='T': gas.TPX=conditions[0]+dk,conditions[1],conditions[2] if physical_params[i]=='P': gas.TPX=conditions[0],conditions[1]+dk,conditions[2] tempMixTank=ct.Reservoir(gas) tempExhaust = ct.Reservoir(gas) tempReactor=ct.IdealGasReactor(gas,energy=energycon,volume=volume) tempMassFlowCt=ct.MassFlowController(upstream=tempMixTank,downstream=tempReactor,mdot=tempReactor.mass/resTime) tempPresReg=ct.Valve(upstream=tempReactor,downstream=tempExhaust,K=pressureValveCoefficient) tempNetwork=ct.ReactorNet([tempReactor]) tempNetwork.advance_to_steady_state() tempSol.append(tempReactor.get_state()) gas.TPX=conditions reactorNetwork.rtol=1e-10 resid=reactorNetwork.advance_to_steady_state(return_residuals=True) #print(resid) #print(reactorNetwork.rtol) final_pressure=stirredReactor.thermo.P global b b=reactorNetwork.sensitivities() if kinetic_sens==1 and bool(observables): for k in np.arange(len(observables)): for j in np.arange(gas.n_reactions): try: senstempArray[j,k]=reactorNetwork.sensitivity(observables[k],j) except: senstempArray[j,k]=-1 #sensArray['Reaction']=gas.reaction_equations() #sensArray[observables]=senstempArray.T #temp = sensArray.as_matrix() kIndex = [[gas.T],gas.reaction_equations(),senscolumnNames] if physical_sens==1 and bool(observables): for k in np.arange(len(observables)): for j in np.arange(len(['T','P'])): psenstempArray[j,k]=np.log10(stirredReactor.get_state()[stirredReactor.component_index(observables[k])])-np.log10(tempSol[j][stirredReactor.component_index(observables[k])]) psenstempArray[j,k]=np.divide(psenstempArray[j,k],np.log10(dk)) #state = np.hstack([stirredReactor.thermo.P, stirredReactor.mass, #stirredReactor.volume, stirredReactor.T, stirredReactor.thermo.X]) # Stop the stopwatch toc = time.time() print('Simulation Took {:3.2f}s to compute'.format(toc-tic)) columnNames = [] #Store solution to a solution array #for l in np.arange(stirredReactor.n_vars): #columnNames.append(stirredReactor.component_name(l)) columnNames=[stirredReactor.component_name(item) for item in range(stirredReactor.n_vars)] #state=stirredReactor.get_state() state=np.hstack([stirredReactor.mass, stirredReactor.volume, stirredReactor.T, stirredReactor.thermo.X]) data=pd.DataFrame(state).transpose() data.columns=columnNames # We now check to see if the pressure rise during the simulation, a.k.a the pressure valve # was okay pressureDifferential = timeHistory['pressure'].max()-timeHistory['pressure'].min() if(abs(pressureDifferential/reactorPressure) > maxPressureRiseAllowed): print("WARNING: Non-trivial pressure rise in the reactor. Adjust K value in valve") if kinetic_sens==1 and bool(observables) and physical_sens!=1: modelData=model_data(simtype,kinetic_sens=np.expand_dims(senstempArray,axis=0),Solution=data,Index=kIndex) modelData.add_final_pressure(final_pressure) modelData.add_solver_time(toc-tic) return modelData if physical_sens==1 and bool(observables) and kinetic_sens!=1: modelData=model_data(simtype,physical_sens=np.expand_dims(psenstempArray,axis=0),Solution=data,pIndex=pIndex) modelData.add_final_pressure(final_pressure) modelData.add_solver_time(toc-tic) return modelData if physical_sens==1 and bool(observables) and kinetic_sens==1: modelData=model_data(simtype,physical_sens=np.expand_dims(psenstempArray,axis=0),kinetic_sens=np.expand_dims(senstempArray,axis=0),Solution=data,Index=kIndex,pIndex=pIndex) modelData.add_final_pressure(final_pressure) modelData.add_solver_time(toc-tic) return modelData else: modelData=model_data(simtype,Solution=data) modelData.add_final_pressure(final_pressure) modelData.add_solver_time(toc-tic) return modelData
exhaust = ct.Reservoir(gas) exaustP = ct.one_atm #defining of necessary funciotns def kappa(gas): return gas.cp / gas.cv def critical_flow(gasin, gasinP, gasinT, gasinmw, k, gasoutP, area): R = ct.gas_constant / gasinmw return (area * gasinP * math.sqrt(k / (R * gasinT)) * (2 / (k + 1))**((k + 1) / (2 * (k - 1)))) / (gasinP - gasoutP) v1 = ct.Valve(fuel, combustion_chamber) v2 = ct.Valve(oxidizer, combustion_chamber) v3 = ct.Valve(combustion_chamber, exhaust) #igniter setup fwhm = 0.008 amplitude = 0.01 t0 = 0.05 igniter_mdot = lambda t: amplitude * math.exp(-(t - t0)**2 * 4 * math.log(2) / fwhm**2) m3 = ct.MassFlowController(igniter, combustion_chamber, mdot=igniter_mdot) states = ct.SolutionArray(gas, extra=['t', 'mass', 'v']) # the simulation only contains one reactor sim = ct.ReactorNet([combustion_chamber])
def run_single(self): gas=self.processor.solution reactorPressure=gas.P self.reactorPressure=self.processor.solution.P pressureValveCoefficient=self.pvalveCoefficient maxPressureRiseAllowed=self.maxPrise print(maxPressureRiseAllowed,self.reactorPressure,pressureValveCoefficient) #Build the system components for JSR pretic=time.time() if bool(self.observables) and self.kineticSens==1: ################################################################### #Block to create temp reactor network to pre-solve JSR without kinetic sens ct.suppress_thermo_warnings() tempgas=ct.Solution(self.processor.cti_path) tempgas.TPX=self.processor.solution.TPX tempfuelAirMixtureTank=ct.Reservoir(tempgas) tempexhaust=ct.Reservoir(tempgas) tempstirredReactor=ct.IdealGasReactor(tempgas,energy=self.energycon, volume=self.reactor_volume) tempmassFlowController=ct.MassFlowController(upstream=tempfuelAirMixtureTank, downstream=tempstirredReactor, mdot=tempstirredReactor.mass/self.residence_time) tempPressureRegulator=ct.Valve(upstream=tempstirredReactor,downstream=tempexhaust, K=pressureValveCoefficient) tempreactorNetwork=ct.ReactorNet([tempstirredReactor]) tempreactorNetwork.rtol = self.rtol tempreactorNetwork.atol = self.atol print(self.rtol,self.atol) tempreactorNetwork.advance_to_steady_state() ################################################################### #reactorNetwork.advance_to_steady_state() #reactorNetwork.reinitialize() elif self.kineticSens and bool(self.observables)==False: #except: print('Please supply a non-empty list of observables for sensitivity analysis or set kinetic_sens=0') pretoc=time.time() print('Presolving Took {:3.2f}s to compute'.format(pretoc-pretic)) fuelAirMixtureTank=ct.Reservoir(self.processor.solution) exhaust=ct.Reservoir(self.processor.solution) if bool(self.observables) and self.kineticSens==1: stirredReactor=ct.IdealGasReactor(tempgas,energy=self.energycon, volume=self.reactor_volume) else: stirredReactor=ct.IdealGasReactor(self.processor.solution,energy=self.energycon, volume=self.reactor_volume) #stirredReactor=ct.IdealGasReactor(self.processor.solution,energy=self.energycon, # volume=self.reactor_volume) massFlowController=ct.MassFlowController(upstream=fuelAirMixtureTank, downstream=stirredReactor, mdot=stirredReactor.mass/self.residence_time) pressureRegulator=ct.Valve(upstream=stirredReactor,downstream=exhaust,K=pressureValveCoefficient) reactorNetwork=ct.ReactorNet([stirredReactor]) if bool(self.observables) and self.kineticSens==1: for i in range(gas.n_reactions): stirredReactor.add_sensitivity_reaction(i) reactorNetwork.rtol_sensitivity=0.0000001 reactorNetwork.atol_sensitivity=0.00000001 print('Sens tols:'+str(reactorNetwork.atol_sensitivity)+', '+str(reactorNetwork.rtol_sensitivity)) # now compile a list of all variables for which we will store data columnNames = [stirredReactor.component_name(item) for item in range(stirredReactor.n_vars)] columnNames = ['pressure'] + columnNames # use the above list to create a DataFrame timeHistory = pd.DataFrame(columns=columnNames) # Start the stopwatch tic = time.time() reactorNetwork.rtol = self.rtol reactorNetwork.atol = self.atol #reactorNetwork.max_err_test_fails= 10000 #print(reactorNetwork.max_err_test_fails) if self.physicalSens==1 and bool(self.observables)==False: #except: print('Please supply a non-empty list of observables for sensitivity analysis or set physical_sens=0') #Establish a matrix to hold sensitivities for kinetic parameters, along with tolerances if self.kineticSens==1 and bool(self.observables): #senscolumnNames = ['Reaction']+observables senscolumnNames = self.observables #sensArray = pd.DataFrame(columns=senscolumnNames) #senstempArray = np.zeros((gas.n_reactions,len(observables))) dfs = [pd.DataFrame() for x in range(len(self.observables))] #tempArray = [np.zeros(self.processor.solution.n_reactions) for x in range(len(self.observables))] #stirredReactor.thermo.X=tempstirredReactor.thermo.X posttic=time.time() # for steps in range(10): # reactorNetwork.step() reactorNetwork.advance_to_steady_state() posttoc=time.time() print('Main Solver Took {:3.2f}s to compute'.format(posttoc-posttic)) final_pressure=stirredReactor.thermo.P sens=reactorNetwork.sensitivities() #print(sens[:,787]) #print(gas.species_names) #print(self.observables) #print(sens) if self.kineticSens==1 and bool(self.observables): #print((pd.DataFrame(sens[0,:])).transpose()) #test=pd.concat([pd.DataFrame(),pd.DataFrame(sens[0,:]).transpose()]) #print(test) for k in range(len(self.observables)): index=gas.species_names.index(self.observables[k]) dfs[k] = dfs[k].append(((pd.DataFrame(sens[index+3,:])).transpose()),ignore_index=True) #dfs[k]=pd.concat([dfs[k],pd.DataFrame(sens[k,:]).transpose()]) #dfs[k]=pd.DataFrame(sens[k,:]).transpose() #print(dfs) toc = time.time() print('Simulation Took {:3.2f}s to compute'.format(toc-tic)+' at T = '+str(stirredReactor.T)) #print(dfs[0]) columnNames = [] #Store solution to a solution array #for l in np.arange(stirredReactor.n_vars): #columnNames.append(stirredReactor.component_name(l)) columnNames=[stirredReactor.component_name(item) for item in range(stirredReactor.n_vars)] #state=stirredReactor.get_state() state=np.hstack([stirredReactor.mass, stirredReactor.volume, stirredReactor.T, stirredReactor.thermo.X]) data=pd.DataFrame(state).transpose() data.columns=columnNames pressureDifferential = timeHistory['pressure'].max()-timeHistory['pressure'].min() if(abs(pressureDifferential/self.reactorPressure) > maxPressureRiseAllowed): #except: print("WARNING: Non-trivial pressure rise in the reactor. Adjust K value in valve") if self.kineticSens==1: numpyMatrixsksens = [dfs[dataframe].values for dataframe in range(len(dfs))] self.kineticSensitivities = np.dstack(numpyMatrixsksens) #print(np.shape(self.kineticSensitivities)) self.solution=data return (self.solution,self.kineticSensitivities) else: self.solution=data return (self.solution,[])
def combustor(): # use reaction mechanism GRI-Mech 3.0 gas = ct.Solution('gri30.xml') # create a reservoir for the fuel inlet, and set to pure methane. fuelX = 'CH4:' + eCH4.get() + ',C2H6:' + eC2H6.get() + ',N2:' + eN2.get() gas.TPX = float(eFuelT.get()), ct.one_atm, fuelX fuel_in = ct.Reservoir(gas) fuel_mw = gas.mean_molecular_weight # use predefined function Air() for the air inlet air = ct.Solution('air.xml') air_in = ct.Reservoir(air) air_mw = air.mean_molecular_weight # to ignite the fuel/air mixture, we'll introduce a pulse of radicals. The # steady-state behavior is independent of how we do this, so we'll just use a # stream of pure atomic hydrogen. gas.TPX = float(eCombustorT.get()), ct.one_atm, 'H:1.0' igniter = ct.Reservoir(gas) # create the combustor, and fill it in initially with N2 gas.TPX = float(eCombustorT.get()), ct.one_atm, 'N2:1.0' combustor = ct.IdealGasReactor(gas) combustor.volume = 1.0 # create a reservoir for the exhaust exhaust = ct.Reservoir(gas) # lean combustion, phi = 0.5 equiv_ratio = 0.5 # compute fuel and air mass flow rates factor = 0.1 air_mdot = factor * 9.52 * air_mw fuel_mdot = factor * equiv_ratio * fuel_mw # create and install the mass flow controllers. Controllers m1 and m2 provide # constant mass flow rates, and m3 provides a short Gaussian pulse only to # ignite the mixture m1 = ct.MassFlowController(fuel_in, combustor, mdot=fuel_mdot) # note that this connects two reactors with different reaction mechanisms and # different numbers of species. Downstream and upstream species are matched by # name. m2 = ct.MassFlowController(air_in, combustor, mdot=air_mdot) # The igniter will use a Gaussian time-dependent mass flow rate. fwhm = 0.2 amplitude = 0.1 t0 = 1.0 igniter_mdot = lambda t: amplitude * math.exp(-(t - t0)**2 * 4 * math.log( 2) / fwhm**2) m3 = ct.MassFlowController(igniter, combustor, mdot=igniter_mdot) # put a valve on the exhaust line to regulate the pressure v = ct.Valve(combustor, exhaust, K=1.0) # the simulation only contains one reactor sim = ct.ReactorNet([combustor]) # take single steps to 6 s, writing the results to a CSV file for later # plotting. tfinal = 4.0 tnow = 0.0 Tprev = combustor.T tprev = tnow states = ct.SolutionArray(gas, extra=['t', 'tres']) while tnow < tfinal: tnow = sim.step() tres = combustor.mass / v.mdot(tnow) Tnow = combustor.T if abs(Tnow - Tprev) > 1.0 or tnow - tprev > 2e-2: tprev = tnow Tprev = Tnow states.append(gas.state, t=tnow, tres=tres) states.write_csv('combustor.csv', cols=('t', 'T', 'tres', 'X')) #matplotlib plt.figure() plt.plot(states.t, states.T) plt.xlabel('Time [s]') plt.ylabel('Temperature [K]') plt.title('Temperature') plt.plot() plt.savefig('Temperature.png') plt.figure() plt.plot(states.t, states.P) plt.xlabel('Time [s]') plt.ylabel('Pressure [Pa]') plt.title('Pressure') plt.plot() plt.savefig('Pressure.png') plt.figure() plt.plot(states.t, states.X) plt.xlabel('Time [s]') plt.ylabel('Concentration H20') plt.title('H20') plt.plot() plt.savefig('X.png') print('OK!')
T = 299.05 # temperature unit: K mix_comp = 'IC8H18:1, O2:12.5, AR:75' # mixture composition. Molar ratio. # Input the ic8 model mixture = ct.Solution('ic8mech.xml') # Set the state of the reactive mixture mixture.TPX = T, P, mix_comp air = ct.Solution('air.xml') mixture_unr = ct.Solution('ic8mech_unr.xml') # Set the state of the unreactive mixture mixture_unr.TPX = T, P, mix_comp r1 = ct.IdealGasReactor(mixture) # Set core zone r2 = ct.IdealGasReactor(mixture_unr) # Set boundary layer zone r3 = ct.IdealGasReactor(mixture_unr) # Set crevice zone v1 = ct.Valve(r1,r2) # Add a valve between the main combustion chamber and the boundary layer v2 = ct.Valve(r2,r3) # Add a valve between the boundary layer and the crevice Env = ct.Reservoir(air) # Set the environment w1 = ct.Wall(Env, r1) w2 = ct.Wall(Env, r2) w3 = ct.Wall(Env, r3) ###################################################################################################################### # v1.set_valve_coeff(5E-6) # Set mass flow rate for valve1 v2.set_valve_coeff(5E-6) # Set mass flow rate for valve2 # Set the geometry of the RCM dia_2 = 50.8E-3 # Diameter of the combustion chamber.
# note that this connects two reactors with different reaction mechanisms and # different numbers of species. Downstream and upstream species are matched by # name. m2 = ct.MassFlowController(air_in, combustor, mdot=air_mdot) # The igniter will use a Gaussian time-dependent mass flow rate. fwhm = 0.2 amplitude = 0.1 t0 = 1.0 igniter_mdot = lambda t: amplitude * math.exp(-(t - t0)**2 * 4 * math.log(2) / fwhm**2) m3 = ct.MassFlowController(igniter, combustor, mdot=igniter_mdot) # put a valve on the exhaust line to regulate the pressure v = ct.Valve(combustor, exhaust, K=1.0) # the simulation only contains one reactor sim = ct.ReactorNet([combustor]) # take single steps to 6 s, writing the results to a CSV file for later # plotting. tfinal = 6.0 tnow = 0.0 Tprev = combustor.T tprev = tnow states = ct.SolutionArray(gas, extra=['t', 'tres']) while tnow < tfinal: tnow = sim.step() tres = combustor.mass / v.mdot(tnow)
downstream = ct.Reservoir(gas_b) # Create a reactor for the mixer. A reactor is required instead of a # reservoir, since the state will change with time if the inlet mass flow # rates change or if there is chemistry occurring. gas_b.TPX = 300.0, ct.one_atm, 'O2:0.21, N2:0.78, AR:0.01' mixer = ct.Reactor(gas_b) # create two mass flow controllers connecting the upstream reservoirs to the # mixer, and set their mass flow rates to values corresponding to # stoichiometric combustion. mfc1 = ct.MassFlowController(res_a, mixer, mdot=rho_a * 2.5 / 0.21) mfc2 = ct.MassFlowController(res_b, mixer, mdot=rho_b * 1.0) # connect the mixer to the downstream reservoir with a valve. outlet = ct.Valve(mixer, downstream, K=10.0) sim = ct.ReactorNet([mixer]) # Since the mixer is a reactor, we need to integrate in time to reach steady # state. A few residence times should be enough. print('{:>14s} {:>14s} {:>14s} {:>14s} {:>14s}'.format( 't [s]', 'T [K]', 'h [J/kg]', 'P [Pa]', 'X_CH4')) t = 0.0 for n in range(30): tres = mixer.mass / (mfc1.mdot(t) + mfc2.mdot(t)) t += 0.5 * tres sim.advance(t) print('{:14.5g} {:14.5g} {:14.5g} {:14.5g} {:14.5g}'.format( t, mixer.T, mixer.thermo.h, mixer.thermo.P, mixer.thermo['CH4'].X[0]))
# create a reservoir for the reactor to exhaust into. The composition of # this reservoir is irrelevant. downstream = ct.Reservoir(gas, name='downstream') # use a 'Wall' object to implement the reacting surface in the reactor. # Since walls have to be installed between two reactors/reserviors, we'll # install it between the upstream reservoir and the reactor. The area is # set to the desired catalyst area in the reactor, and surface reactions # are included only on the side facing the reactor. w = ct.Wall(upstream, r, A=cat_area, kinetics=[None, surf]) # We need a valve between the reactor and the downstream reservoir. This # will determine the pressure in the reactor. Set K large enough that the # pressure difference is very small. v = ct.Valve(r, downstream, K=1e-4) # The mass flow rate into the reactor will be fixed by using a # MassFlowController object. m = ct.MassFlowController(upstream, r, mdot=mass_flow_rate) sim = ct.ReactorNet([r]) sim.max_err_test_fails = 12 # set relative and absolute tolerances on the simulation sim.rtol = 1.0e-9 sim.atol = 1.0e-21 T_start, rho_start, Y_start = r.thermo.TDY cov_start = surf.coverages V_start = r.volume
#energy='off' apaga la ecuación de energía al crear el reactor r1 = ct.IdealGasReactor(contents=gas, energy='off') #Miro el contenido del reactor en fracciones másicas r1.Y residence_time = 0.8e-4 def mdot_out(t): return r1.mass / residence_time #Poner una válvula a la entrada del reactor r1 para mantener P=cte y evitar que se #devuelva el flujo. Véase help(ct.Valve) #Nótese el uso del objeto upstream que es un tipo Reservoir devinido arriba inlet = ct.Valve(upstream, r1, K=100) #Pone un objeto MassFlowController a la salidad de r1 que mantiene el flujo másico #constante, véase help(ct.MassFlowController) #Nótese el uso del objeto downstream que es un tipo Reservoir devinido arriba outlet = ct.MassFlowController(r1, downstream) #fija el flujo másico a la salida del reactor outlet.set_mass_flow_rate(mdot_out) net = ct.ReactorNet([r1]) #Creo los "pasos de tiempo" en los que quiero ver el progreso de la solución #El solver de cantera resuelve de forma automática y escoge su paso de tiempo de #forma automática así t sea 2, 20 o 2000 puntos t = np.linspace(0, residence_time, 2) # #¿Por qué el código original de Ray multiplica por 5 el tiempo de residencia? #t = np.linspace(0, 5*residence_time, 2)
m2 = ct.MassFlowController(air_in, przestrzen_obliczeniowa, mdot=air_mdot) # ignition modelled by Gaussian pulse fwhm = 0.2 amplitude = 0.1 t0 = 1.0 igniter_mdot = lambda t: amplitude * math.exp(-(t - t0)**2 * 4 * math. log(2) / fwhm**2) m3 = ct.MassFlowController(igniter, przestrzen_obliczeniowa, mdot=igniter_mdot) # pressure constraint on exhaust, applied by using a valve v = ct.Valve(przestrzen_obliczeniowa, exhaust, K=1.0) # creating simulation of reactor network sim = ct.ReactorNet([przestrzen_obliczeniowa]) # setting time limits of simulation tfinal = 8.0 tnow = 0.0 # Let's assume OH radicals' mass fraction reaches constant value when reactor approaches state of steady burning. If so, # lifespan of radicals become virtually dependant only on time limits of simulation # That would be critical mistake, so in order to avoid it, a counter is added to check if dy/dt (where y is OH mass fraction) # becomes equal to 0, if so, and OH mass fraction (current) is lower than its maximum value, loop becomes broken instantly # and lifespan counter freezes at current value. # delcaring variable for previous OH mass fraction value to check, if reactor has reached it's steady state
def JSR_steadystate(gas, resTime, volume, kinetic_sens=0, physical_sens=0, observables=[], physical_params=[], energycon='off', pressureValveCoefficient=0.01, maxsimulationTime=1000): # Inlet gas conditions are passed into function in the "gas" object, which is a cantera object # Reactor parameters passed into function as resTime and volume. Residence time and volume of JSR #kinetic sens and physical sens are optional parameters which are set to zero by default. Set them to 1 to #calculate sensitivity based on kinetic or physical parameters. If these are set to 1 you must pass #an array of all observables to calculate sensitivities for reactorPressure = gas.P # This is the "conductance" of the pressure valve and will determine its efficiency in # holding the reactor pressure to the desired conditions. It is an optional parameter pressureValveCoefficient = pressureValveCoefficient # This parameter will allow you to decide if the valve's conductance is acceptable. If there # is a pressure rise in the reactor beyond this tolerance, you will get a warning maxPressureRiseAllowed = 0.001 # Simulation termination criterion maxSimulationTime = maxsimulationTime # seconds. An optional parameter fuelAirMixtureTank = ct.Reservoir(gas) exhaust = ct.Reservoir(gas) stirredReactor = ct.IdealGasReactor(gas, energy=energycon, volume=volume) massFlowController = ct.MassFlowController(upstream=fuelAirMixtureTank, downstream=stirredReactor, mdot=stirredReactor.mass / resTime) pressureRegulator = ct.Valve(upstream=stirredReactor, downstream=exhaust, K=pressureValveCoefficient) reactorNetwork = ct.ReactorNet([stirredReactor]) #This block adds kinetic sensitivity parameters for all reactions if desired. if kinetic_sens == 1 and bool(observables): for i in range(gas.n_reactions): stirredReactor.add_sensitivity_reaction(i) if kinetic_sens == 1 and bool(observables) == False: print( 'Please supply a non-empty list of observables for sensitivity analysis or set kinetic_sens=0' ) if physical_sens == 1 and bool(observables): print('Placeholder') if physical_sens == 1 and bool(observables) == False: print( 'Please supply a non-empty list of observables for sensitivity analysis or set physical_sens=0' ) # now compile a list of all variables for which we will store data columnNames = [ stirredReactor.component_name(item) for item in range(stirredReactor.n_vars) ] columnNames = ['pressure'] + columnNames # use the above list to create a DataFrame timeHistory = pd.DataFrame(columns=columnNames) # Start the stopwatch tic = time.time() #Establish a matrix to hold sensitivities for kinetic parameters, along with tolerances if kinetic_sens == 1 and bool(observables): senscolumnNames = ['Reaction'] + observables sensArray = pd.DataFrame(columns=senscolumnNames) senstempArray = np.zeros((len(observables), gas.n_reactions)) reactorNetwork.rtol_sensitivity = 1.0e-6 reactorNetwork.atol_sensitivity = 1.0e-6 if physical_sens == 1 and bool(observables): senscolumnNames = ['Parameter'] + observables psensArray = pd.DataFrame(columns=senscolumnNames) senstempArray = np.zeros((len(observables), len(physical_params))) reactorNetwork.advance_to_steady_state() if kinetic_sens == 1 and bool(observables): for k in np.arange(len(observables)): for j in np.arange(gas.n_reactions): #print( k,j) senstempArray[k, j] = reactorNetwork.sensitivity( observables[k], j) sensArray['Reaction'] = gas.reaction_equations() sensArray[observables] = senstempArray.T #state = np.hstack([stirredReactor.thermo.P, stirredReactor.mass, #stirredReactor.volume, stirredReactor.T, stirredReactor.thermo.X]) # Stop the stopwatch toc = time.time() print('Simulation Took {:3.2f}s to compute'.format(toc - tic)) # We now check to see if the pressure rise during the simulation, a.k.a the pressure valve # was okay pressureDifferential = timeHistory['pressure'].max( ) - timeHistory['pressure'].min() if (abs(pressureDifferential / reactorPressure) > maxPressureRiseAllowed): print( "WARNING: Non-trivial pressure rise in the reactor. Adjust K value in valve" ) if kinetic_sens == 1 and bool(observables) and physical_sens == 0: modelData = jsr_model_data(kinetic_sens=sensArray, reactorSolution=stirredReactor) return modelData if physical_sens == 1 and bool(observables) and kinetic_sens == 0: modelData = jsr_model_data(physical_sens=psensArray, reactorSolution=stirredReactor) if physical_sens == 1 and bool(observables) and kinetic_sens == 1: modelData = jsr_model_data(physical_sens=psensArray, kinetic_sens=sensArray, reactorSolution=stirredReactor) else: modelData = jsr_model_data(reactorSolution=stirredReactor) return modelData