def create_sim(self, p, fuel='H2:1.0, AR:1.0', T_fuel=300, mdot_fuel=0.24, oxidizer='O2:0.2, AR:0.8', T_ox=300, mdot_ox=0.72, width=0.02): # Solution object used to compute mixture properties self.gas = ct.Solution('h2o2.xml', 'ohmech') self.gas.TP = T_fuel, p # Flame object self.sim = ct.CounterflowDiffusionFlame(self.gas, width=width) # Set properties of the fuel and oxidizer mixtures self.sim.fuel_inlet.mdot = mdot_fuel self.sim.fuel_inlet.X = fuel self.sim.fuel_inlet.T = T_fuel self.sim.oxidizer_inlet.mdot = mdot_ox self.sim.oxidizer_inlet.X = oxidizer self.sim.oxidizer_inlet.T = T_ox self.sim.set_initial_guess()
def create_sim(self, p, fuel='H2:1.0, AR:1.0', T_fuel=300, mdot_fuel=0.24, oxidizer='O2:0.2, AR:0.8', T_ox=300, mdot_ox=0.72): initial_grid = np.linspace(0, 0.02, 6) # m tol_ss = [2.0e-5, 1.0e-11] # [rtol, atol] for steady-state problem tol_ts = [5.0e-4, 1.0e-11] # [rtol, atol] for time stepping # IdealGasMix object used to compute mixture properties self.gas = ct.Solution('h2o2.xml', 'ohmech') self.gas.TP = T_fuel, p # Flame object self.sim = ct.CounterflowDiffusionFlame(self.gas, initial_grid) self.sim.flame.set_steady_tolerances(default=tol_ss) self.sim.flame.set_transient_tolerances(default=tol_ts) # Set properties of the fuel and oxidizer mixtures self.sim.fuel_inlet.mdot = mdot_fuel self.sim.fuel_inlet.X = fuel self.sim.fuel_inlet.T = T_fuel self.sim.oxidizer_inlet.mdot = mdot_ox self.sim.oxidizer_inlet.X = oxidizer self.sim.oxidizer_inlet.T = T_ox self.sim.set_initial_guess(fuel='H2')
import os # Create directory for output data files data_directory = 'diffusion_flame_extinction_data/' if not os.path.exists(data_directory): os.makedirs(data_directory) # PART 1: INITIALIZATION # Set up an initial hydrogen-oxygen counterflow flame at 1 bar and low strain # rate (maximum axial velocity gradient = 2414 1/s) reaction_mechanism = 'h2o2.xml' gas = ct.Solution(reaction_mechanism) width = 18.e-3 # 18mm wide f = ct.CounterflowDiffusionFlame(gas, width=width) # Define the operating pressure and boundary conditions f.P = 1.e5 # 1 bar f.fuel_inlet.mdot = 0.5 # kg/m^2/s f.fuel_inlet.X = 'H2:1' f.fuel_inlet.T = 300 # K f.oxidizer_inlet.mdot = 3.0 # kg/m^2/s f.oxidizer_inlet.X = 'O2:1' f.oxidizer_inlet.T = 500 # K # Set refinement parameters f.set_refine_criteria(ratio=3.0, slope=0.1, curve=0.2, prune=0.03) # Define a limit for the maximum temperature below which the flame is # considered as extinguished and the computation is aborted
tol_ss = [1.0e-5, 1.0e-12] # [rtol, atol] for steady-state problem tol_ts = [5.0e-4, 1.0e-11] # [rtol, atol] for time stepping loglevel = 1 # amount of diagnostic output (0 to 5) refine_grid = 1 # 1 to enable refinement, 0 to disable # Create the gas object used to evaluate all thermodynamic, kinetic, and # transport properties. gas = ct.Solution('gri30.xml', 'gri30_mix') gas.TP = gas.T, p # Create an object representing the counterflow flame configuration, # which consists of a fuel inlet on the left, the flow in the middle, # and the oxidizer inlet on the right. f = ct.CounterflowDiffusionFlame(gas, initial_grid) # Set the state of the two inlets f.fuel_inlet.mdot = mdot_f f.fuel_inlet.X = comp_f f.fuel_inlet.T = tin_f f.oxidizer_inlet.mdot = mdot_o f.oxidizer_inlet.X = comp_o f.oxidizer_inlet.T = tin_o # Set error tolerances f.flame.set_steady_tolerances(default=tol_ss) f.flame.set_transient_tolerances(default=tol_ts) # Set the boundary emissivities
def CounterflowPartiallyPremixedFlame(mech='gri30.xml', transport='UnityLewis', flag_soret=False, flag_radiation=False, fuel_name='CH4', strain_rate=285., width=0.01, p=1., phi_f='inf', phi_o=0., tin_f=300., tin_o=300., solution=None): ################################################################################ # Create the gas object used to evaluate all thermodynamic, kinetic, and # transport properties. gas = ct.Solution(mech) phi_f = float(phi_f) if phi_f <= 1.: sys.exit('Equivalence ratio of fuel side {:g}'.format(phi_f)) if phi_o >= 1.: sys.exit('Equivalence ratio of oxidizer side {:g}'.format(phi_o)) # construct case name flame_params = {} flame_params['F'] = fuel_name flame_params['p'] = p flame_params['a'] = strain_rate flame_params['phif'] = phi_f flame_params['phio'] = phi_o flame_params['tf'] = tin_f flame_params['to'] = tin_o case_name = params2name(flame_params) ################################################################################ p *= ct.one_atm # pressure # Create an object representing the counterflow flame configuration, # which consists of a fuel inlet on the left, the flow in the middle, # and the oxidizer inlet on the right. f = ct.CounterflowDiffusionFlame(gas, width=width) f.transport_model = transport f.P = p if solution is not None: f.restore(solution, loglevel=0) solution_width = f.grid[-1] - f.grid[0] width_factor = width / solution_width solution_strain = (f.u[0] - f.u[-1]) / solution_width strain_factor = strain_rate / solution_strain normalized_grid = f.grid / solution_width u_factor = strain_factor * width_factor # update solution initialization following Fiala & Sattelmayer f.flame.grid = normalized_grid * width f.set_profile('u', normalized_grid, f.u * u_factor) f.set_profile('V', normalized_grid, f.V * strain_factor) f.set_profile('lambda', normalized_grid, f.L * np.square(strain_factor)) oxy = {'O2': 1., 'N2': 3.76} # air composition fuel_index = gas.species_index(fuel_name) stoich_nu = gas.n_atoms(fuel_index, 'C') + gas.n_atoms(fuel_index, 'H') / 4. comp_f = {} comp_o = {} comp_f[fuel_name] = 1 for k, v in oxy.items(): comp_o[k] = v comp_f[k] = v * stoich_nu / phi_f comp_o[fuel_name] = phi_o / stoich_nu gas.TPX = tin_f, p, comp_o dens_o = gas.density gas.TPX = tin_o, p, comp_f dens_f = gas.density # fuel and oxidizer streams have the same velocity u = strain_rate * width / 2. # get mass flow rate mdot_o = u * dens_o mdot_f = u * dens_f # kg/m^2/s # Set the state of the two inlets f.fuel_inlet.mdot = mdot_f f.fuel_inlet.X = comp_f f.fuel_inlet.T = tin_f f.oxidizer_inlet.mdot = mdot_o f.oxidizer_inlet.X = comp_o f.oxidizer_inlet.T = tin_o # Set the boundary emissivities f.set_boundary_emissivities(0.0, 0.0) # Turn radiation off f.radiation_enabled = False f.set_refine_criteria(ratio=2, slope=0.1, curve=0.1, prune=0.01) # Solve the problem try: f.solve(loglevel=0, auto=True) except Exception as e: print('Error: not converge for case:', e) return -1 if flag_radiation: f.radiation_enabled = True try: f.solve(loglevel=0, auto=True) except Exception as e: print('Error: not converge for case:', e) return -1 if flag_soret: f.soret_enabled = True try: f.solve(loglevel=0, auto=True) except Exception as e: print('Error: not converge for case:', e) return -1 f.save('{}.xml'.format(case_name)) if np.max(f.T) < np.max((tin_f, tin_o)) + 100: return 1 else: return 0
def counterflow_flame(mech='gri30.xml', transport='Multi', flag_soret=True, flag_radiation=False, fuel={'CH4': 1}, oxy={ 'O2': 1, 'N2': 3.76 }, strain_rate=100., width=0.01, p=1., phi_f='inf', phi_o=0., tin_f=300., tin_o=300., solution=None): ################################################################################ # Create the gas object used to evaluate all thermodynamic, kinetic, and # transport properties. gas = ct.Solution(mech) phi_f = float(phi_f) if phi_f <= 1.: sys.exit('Equivalence ratio of fuel side {:g}'.format(phi_f)) if phi_o >= 1.: sys.exit('Equivalence ratio of oxidizer side {:g}'.format(phi_o)) # construct case name flame_params = {} flame_params['p'] = p flame_params['a'] = strain_rate flame_params['phif'] = phi_f flame_params['phio'] = phi_o flame_params['tf'] = tin_f flame_params['to'] = tin_o case_name = params2name(flame_params) p *= ct.one_atm # pressure ################################################################################ #fuel = { 'H2':1., 'N2':1. } # fuel composition #oxy = {'O2':1., 'N2':3.76} # air composition Z_element = ['C', 'H', 'O'] for e in Z_element: if e not in gas.element_names: Z_element.remove(e) # get stoichiometric coefficients element_nu = {'C': 2, 'O': -1, 'H': 0.5} o_fuel = 0. for k, v in fuel.items(): for e in gas.element_names: if e in element_nu.keys(): o_fuel += v * gas.n_atoms(k, e) * element_nu[e] o_oxy = 0. for k, v in oxy.items(): for e in gas.element_names: if e in element_nu.keys(): o_oxy += v * gas.n_atoms(k, e) * element_nu[e] stoich_nu = -o_fuel / o_oxy # composition for two streams comp_f = {} comp_o = {} for k in fuel.keys(): comp_f[k] = 0. comp_o[k] = 0. for k in oxy.keys(): comp_f[k] = 0. comp_o[k] = 0. for k, v in fuel.items(): comp_f[k] += v comp_o[k] += v * phi_o / stoich_nu for k, v in oxy.items(): comp_o[k] += v comp_f[k] += v * stoich_nu / phi_f # fuel and oxidizer streams have the same velocity u = strain_rate * width / 2. # get mass flow rate gas.TPX = tin_f, p, comp_o dens_o = gas.density mdot_o = u * dens_o gas.TPX = tin_o, p, comp_f dens_f = gas.density mdot_f = u * dens_f # kg/m^2/s # Create an object representing the counterflow flame configuration, # which consists of a fuel inlet on the left, the flow in the middle, # and the oxidizer inlet on the right. f = ct.CounterflowDiffusionFlame(gas, width=width) if solution is not None: try: f.restore(solution, loglevel=0) except Exception as e: print(e, 'Start to solve from initialization') f.transport_model = transport # Set the state of the two inlets f.fuel_inlet.mdot = mdot_f f.fuel_inlet.X = comp_f f.fuel_inlet.T = tin_f f.oxidizer_inlet.mdot = mdot_o f.oxidizer_inlet.X = comp_o f.oxidizer_inlet.T = tin_o # Set the boundary emissivities f.set_boundary_emissivities(0.0, 0.0) # Turn radiation off f.radiation_enabled = False f.set_refine_criteria(ratio=2, slope=0.1, curve=0.1, prune=0.01) # Solve the problem try: f.solve(loglevel=0, auto=True) except Exception as e: print('Error: not converge for case:', e) return -1 if flag_radiation: f.radiation_enabled = True try: f.solve(loglevel=0, auto=True) except Exception as e: print('Error: not converge for case:', e) return -1 if flag_soret: f.soret_enabled = True try: f.solve(loglevel=0, auto=True) except Exception as e: print('Error: not converge for case:', e) return -1 f.save('{}.xml'.format(case_name)) ################################################################################ # post-processing # Calculate Bilger's mixture fraction # fuel gas.TPX = tin_f, p, comp_f z_f = 0 for e in Z_element: z_f += (element_nu[e] * gas.elemental_mass_fraction(e) / gas.atomic_weight(e)) comp_fuel = np.hstack((gas.T, gas.Y)) fuel_str = ' '.join([format(x, '12.6e') for x in comp_fuel]) # oxidizer gas.TPX = tin_o, p, comp_o z_o = 0 for e in Z_element: z_o += (element_nu[e] * gas.elemental_mass_fraction(e) / gas.atomic_weight(e)) comp_oxy = np.hstack((gas.T, gas.Y)) oxy_str = ' '.join([format(x, '12.6e') for x in comp_oxy]) # stoichiometric mixture comp_st = {} for k in fuel.keys(): comp_st[k] = 0. for k in oxy.keys(): comp_st[k] = 0. for k, v in fuel.items(): comp_st[k] += v for k, v in oxy.items(): comp_st[k] += v * stoich_nu gas.TPX = tin_o, p, comp_st z_st = 0 for e in Z_element: z_st += (element_nu[e] * gas.elemental_mass_fraction(e) / gas.atomic_weight(e)) Zst = (z_st - z_o) / (z_f - z_o) z = np.zeros(f.T.shape) for e in Z_element: z += (element_nu[e] * f.elemental_mass_fraction(e) / gas.atomic_weight(e)) Z = (z - z_o) / (z_f - z_o) # thermal diffusivity alpha = f.thermal_conductivity / (f.cp_mass * f.density) # kinetic viscosity nu = f.viscosity / f.density # a mixture fraction based on fuel stream is pure fuel gas.TPX = tin_f, p, fuel z_f = 0 for e in Z_element: z_f += (element_nu[e] * gas.elemental_mass_fraction(e) / gas.atomic_weight(e)) gas.TPX = tin_o, p, oxy z_o = 0 for e in Z_element: z_o += (element_nu[e] * gas.elemental_mass_fraction(e) / gas.atomic_weight(e)) Z1st = (z_st - z_o) / (z_f - z_o) z = np.zeros(f.T.shape) for e in Z_element: z += (element_nu[e] * f.elemental_mass_fraction(e) / gas.atomic_weight(e)) Z1 = (z - z_o) / (z_f - z_o) # heat release rate Q = f.heat_release_rate ################################################################################ # output data = np.column_stack((f.grid, f.T, f.Y.transpose(), Z, Z1, Q, alpha, nu)) data_names = (['grid', 'T'] + gas.species_names + ['Z', 'Z1', 'Q', 'alpha', 'nu']) np.savetxt('{}.dat'.format(case_name), data, header=('FUEL: {0}\nOXIDIZER: {1}\n'.format(fuel_str, oxy_str) + 'Zst = {:g}; Z1st = {:g}\n'.format(Zst, Z1st) + ' '.join(data_names)), comments='') if np.max(f.T) < np.max((tin_f, tin_o)) + 100: return 1 else: return 0
def counterflow_flame(mech='gri30.xml', transport='Multi', flag_soret=True, flag_radiation=False, fuel_name='CH4', strain_rate=100., width=0.01, p=1., phi_f='inf', phi_o=0., tin_f=300., tin_o=300., solution=None): ################################################################################ # Create the gas object used to evaluate all thermodynamic, kinetic, and # transport properties. gas = ct.Solution(mech) phi_f = float(phi_f) if phi_f <= 1.: sys.exit('Equivalence ratio of fuel side {:g}'.format(phi_f)) if phi_o >= 1.: sys.exit('Equivalence ratio of oxidizer side {:g}'.format(phi_o)) # construct case name flame_params = {} flame_params['F'] = fuel_name flame_params['p'] = p flame_params['a'] = strain_rate flame_params['phif'] = phi_f flame_params['phio'] = phi_o flame_params['tf'] = tin_f flame_params['to'] = tin_o # flame_params_str = [] # for k, v in flame_params.items(): # try: # param_str = '{0}-{1:g}'.format(k,v) # except ValueError: # param_str = '{0}-{1}'.format(k,v) # flame_params_str.append(param_str) # # case_name = '_'.join(flame_params_str) case_name = params2name(flame_params) p *= ct.one_atm # pressure # if phi_f < 10.0: # case_name = '{0}_p{1:g}_a{2:g}_phif{3:g}_phio{4:g}_tf{5:g}_to{6:g}'\ # .format(fuel_name,p/ct.one_atm,strain_rate, # phi_f,phi_o,tin_f,tin_o) # else: # case_name = '{0}_p{1:g}_a{2:g}_phifinf_phio{3:g}_tf{4:g}_to{5:g}'\ # .format(fuel_name,p/ct.one_atm,strain_rate, # phi_f,phi_o,tin_f,tin_o) # ## parameters of the counterflow flame ## a = (U_f+U+o)/width #strain_rate = 100 # 1/s # #width = 0.01 # Distance between inlets is 2 cm # ## single fuel #fuel_name = 'CH4' # #phi_f = 1.7 # equivalence ratio of the fuel side stream #phi_o = 0. # equivalence ratio of the oxidizer side stream # #tin_f = 300.0 # fuel inlet temperature #tin_o = 300.0 # oxidizer inlet temperature # ################################################################################ oxy = {'O2': 1., 'N2': 3.76} # air composition fuel_index = gas.species_index(fuel_name) stoich_nu = gas.n_atoms(fuel_index, 'C') + gas.n_atoms(fuel_index, 'H') / 4. comp_f = {} comp_o = {} comp_f[fuel_name] = 1 for k, v in oxy.items(): comp_o[k] = v comp_f[k] = v * stoich_nu / phi_f comp_o[fuel_name] = phi_o # fuel and oxidizer streams have the same velocity u = strain_rate * width / 2. # get mass flow rate gas.TPX = tin_f, p, comp_o dens_o = gas.density mdot_o = u * dens_o gas.TPX = tin_o, p, comp_f dens_f = gas.density mdot_f = u * dens_f # kg/m^2/s # Create an object representing the counterflow flame configuration, # which consists of a fuel inlet on the left, the flow in the middle, # and the oxidizer inlet on the right. f = ct.CounterflowDiffusionFlame(gas, width=width) if solution is not None: try: f.restore(solution, loglevel=0) except Exception as e: print(e, 'Start to solve from initialization') f.transport_model = transport # Set the state of the two inlets f.fuel_inlet.mdot = mdot_f f.fuel_inlet.X = comp_f f.fuel_inlet.T = tin_f f.oxidizer_inlet.mdot = mdot_o f.oxidizer_inlet.X = comp_o f.oxidizer_inlet.T = tin_o # Set the boundary emissivities f.set_boundary_emissivities(0.0, 0.0) # Turn radiation off f.radiation_enabled = False f.set_refine_criteria(ratio=2, slope=0.1, curve=0.1, prune=0.01) # Solve the problem try: f.solve(loglevel=0, auto=True) except Exception as e: print('Error: not converge for case:', e) return -1 if flag_radiation: f.radiation_enabled = True try: f.solve(loglevel=0, auto=True) except Exception as e: print('Error: not converge for case:', e) return -1 if flag_soret: f.soret_enabled = True try: f.solve(loglevel=0, auto=True) except Exception as e: print('Error: not converge for case:', e) return -1 f.save('{}.xml'.format(case_name)) ################################################################################ # post-processing # Calculate Bilger's mixture fraction # fuel gas.TPX = tin_f, p, comp_f YC_f = gas.elemental_mass_fraction('C') YH_f = gas.elemental_mass_fraction('H') YO_f = gas.elemental_mass_fraction('O') comp_fuel = np.hstack((gas.T, gas.Y)) fuel_str = ' '.join([format(x, '12.6e') for x in comp_fuel]) # oxidizer gas.TPX = tin_o, p, comp_o YC_o = gas.elemental_mass_fraction('C') YH_o = gas.elemental_mass_fraction('H') YO_o = gas.elemental_mass_fraction('O') comp_oxy = np.hstack((gas.T, gas.Y)) oxy_str = ' '.join([format(x, '12.6e') for x in comp_oxy]) # stoichiometric mixture comp_st = {} comp_st[fuel_name] = 1 for k, v in oxy.items(): comp_st[k] = v * stoich_nu gas.TPX = tin_o, p, comp_st YC_st = gas.elemental_mass_fraction('C') YH_st = gas.elemental_mass_fraction('H') YO_st = gas.elemental_mass_fraction('O') Zst = (2.*(YC_st-YC_o)/gas.atomic_weight('C') +(YH_st-YH_o)/2./gas.atomic_weight('H') -(YO_st-YO_o)/gas.atomic_weight('O')) / \ (2.*(YC_f-YC_o)/gas.atomic_weight('C') +(YH_f-YH_o)/2./gas.atomic_weight('H') -(YO_f-YO_o)/gas.atomic_weight('O')) YC = f.elemental_mass_fraction('C') YH = f.elemental_mass_fraction('H') YO = f.elemental_mass_fraction('O') Z = (2.*(YC-YC_o)/gas.atomic_weight('C') +(YH-YH_o)/2./gas.atomic_weight('H') -(YO-YO_o)/gas.atomic_weight('O')) / \ (2.*(YC_f-YC_o)/gas.atomic_weight('C') +(YH_f-YH_o)/2./gas.atomic_weight('H') -(YO_f-YO_o)/gas.atomic_weight('O')) # thermal diffusivity alpha = f.thermal_conductivity / (f.cp_mass * f.density) # kinetic viscosity nu = f.viscosity / f.density # a mixture fraction based on fuel stream is pure CH4 gas.TPX = tin_f, p, {fuel_name: 1} YC_f = gas.elemental_mass_fraction('C') YH_f = gas.elemental_mass_fraction('H') YO_f = gas.elemental_mass_fraction('O') gas.TPX = tin_o, p, oxy YC_o = gas.elemental_mass_fraction('C') YH_o = gas.elemental_mass_fraction('H') YO_o = gas.elemental_mass_fraction('O') Z1st = (2.*(YC_st-YC_o)/gas.atomic_weight('C') +(YH_st-YH_o)/2./gas.atomic_weight('H') -(YO_st-YO_o)/gas.atomic_weight('O')) / \ (2.*(YC_f-YC_o)/gas.atomic_weight('C') +(YH_f-YH_o)/2./gas.atomic_weight('H') -(YO_f-YO_o)/gas.atomic_weight('O')) Z1 = (2.*(YC-YC_o)/gas.atomic_weight('C') +(YH-YH_o)/2./gas.atomic_weight('H') -(YO-YO_o)/gas.atomic_weight('O')) / \ (2.*(YC_f-YC_o)/gas.atomic_weight('C') +(YH_f-YH_o)/2./gas.atomic_weight('H') -(YO_f-YO_o)/gas.atomic_weight('O')) # progress variable # C_o: mass fractio of O in products CO2, CO, H2O # C_4spe: mass fraction of CO2, CO, H2O, H2 # C_2spe: mass fraction of CO2 and CO index_H2O = gas.species_index('H2O') index_CO2 = gas.species_index('CO2') index_CO = gas.species_index('CO') index_H2 = gas.species_index('H2') MW_H2O = gas.molecular_weights[index_H2O] MW_CO2 = gas.molecular_weights[index_CO2] MW_CO = gas.molecular_weights[index_CO] MW_H2 = gas.molecular_weights[index_H2] C_o = gas.atomic_weight('O') * (f.Y[index_H2O] / MW_H2O + 2. * f.Y[index_CO2] / MW_CO2 + f.Y[index_CO] / MW_CO) C_4spe = f.Y[index_CO2] + f.Y[index_CO] + f.Y[index_H2O] + f.Y[index_H2] C_2spe = f.Y[index_CO2] + f.Y[index_CO] # heat release rate Q = f.heat_release_rate ################################################################################ # output data = np.column_stack((f.grid, f.T, f.Y.transpose(), Z, Z1, C_o, C_4spe, C_2spe, Q, alpha, nu)) data_names = (['grid', 'T'] + gas.species_names + ['Z', 'Z1', 'C_o', 'C_4spe', 'C_2spe', 'Q', 'alpha', 'nu']) np.savetxt('{}.dat'.format(case_name), data, header=('FUEL: {0}\nOXIDIZER: {1}\n'.format(fuel_str, oxy_str) + 'Zst = {:g}; Z1st = {:g}\n'.format(Zst, Z1st) + ' '.join(data_names)), comments='') if np.max(f.T) < np.max((tin_f, tin_o)) + 100: return 1 else: return 0