def free_flame(chemistry='gri30.cti', fuel={'CH4': 1.}, oxidizer={ 'O2': 1., 'N2': 3.76 }, temperature=300., pressure=1., phi=1., **kwargs): # for unrealistic parameters if pressure < 0.: raise ValueError('Negative pressure') if temperature < 0.: raise ValueError('Negative inlet temperature') if phi < 0.: raise ValueError('Negative equivalence ratio') # parameters params = {} params['T'] = temperature params['p'] = pressure params['phi'] = phi case = params2name(params) # pressure, convert to [Pa] pressure *= ct.one_atm # gas object gas = cg.mixture(chemistry, fuel, oxidizer, temperature, pressure, phi) f = free_flame_(gas, **kwargs) # return for unburnt flame if np.max(f.T) < temperature + 100.: return 1 f.save('{}.xml'.format(case)) return 0
def counterflow_premixed_extinction(chemistry='FFCM-1.cti', fuel={'CH4': 1.}, oxidizer={ 'O2': 1., 'N2': 3.76 }, T=300., p=1., phi=1., **kwargs): # read kwargs # IO flags if 'folder_overwrite' in kwargs.keys(): flag_folder = kwargs['folder_overwrite'] else: flag_folder = True # parameters to approach extinction if 'a_init' in kwargs.keys(): a_init = kwargs['a_init'] else: a_init = 100. if 'a_max' in kwargs.keys(): a_max = kwargs['a_max'] else: a_max = 1.E+5 if 'L_init' in kwargs.keys(): L_init = kwargs['L_init'] else: L_init = 0.05 # factors # a_{n+1} = exp(f0) * a_n if 'f0' in kwargs.keys(): f0 = kwargs['f0'] else: f0 = 0.1 # f0_{n+1} = f0_n / f1 if 'f1' in kwargs.keys(): f1 = kwargs['f1'] else: f1 = 2.0 # a_threshold_n = a_n * f2 if 'f2' in kwargs.keys(): f2 = kwargs['f2'] else: f2 = 1.E-3 # for unrealistic parameters if p < 0.: raise ValueError('Negative pressure') if T < 0.: raise ValueError('Negative inlet temperature') if phi < 0.: raise ValueError('Negative equivalence ratio') params = {} params['T'] = T params['p'] = p params['phi'] = phi folder_name = fn.params2name(params) pwd = os.getcwd() os.makedirs(folder_name, exist_ok=flag_folder) os.chdir(folder_name) # calculate free flame ctd.free_flame(chemistry=chemistry, fuel=fuel, oxidizer=oxidizer, pressure=p, temperature=T, phi=phi, **kwargs) # iterate to get the extinction params['a'] = a_init L = L_init flag = 3 while True: if flag == 1: print('Strain rate = {:g} extinct'.format(params['a'])) f0 /= f1 break if flag == 2: print('Strain rate = {:g} negative flame speed'.format( params['a'])) #break if flag == 3: print('Strain rate = {:g} initialization'.format(params['a'])) solution = None else: print('Strain rate = {:g} success'.format(params['a'])) flame_name = fn.params2name(params) solution = '{}.xml'.format(flame_name) a_old = params['a'] L_old = L f0_a = np.exp(f0) L = L_old / np.power(f0_a, 0.5) params['a'] = a_old * f0_a a_diff = params['a'] - a_old if params['a'] > a_max or a_diff < f2 * a_old: break flag = ctd.counterflow_premixed_flame(chemistry=chemistry, fuel=fuel, oxidizer=oxidizer, temperature=T, pressure=p, phi=phi, a=params['a'], solution=solution, width=L, **kwargs) os.chdir(pwd) return
def counterflow_premixed_flame(chemistry='gri30.xml', fuel={'CH4': 1.}, oxidizer={ 'O2': 1, 'N2': 3.76 }, temperature=300., pressure=1., phi=1., a=1000., solution=None, **kwargs): # for unrealistic parameters if pressure < 0.: raise ValueError('Negative pressure') if temperature < 0.: raise ValueError('Negative inlet temperature') if phi < 0.: raise ValueError('Negative equivalence ratio') # read kwargs if 'transport' in kwargs.keys(): transport = kwargs['transport'] else: transport = 'Mix' if 'width' in kwargs.keys(): width = kwargs['width'] else: width = 0.05 if 'loglevel' in kwargs.keys(): loglevel = kwargs['loglevel'] else: # supress log output loglevel = 0 # kwargs for flame solver if 'ct_ratio' in kwargs.keys(): ct_ratio = kwargs['ct_ratio'] else: ct_ratio = 2. if 'ct_slope' in kwargs.keys(): ct_slope = kwargs['ct_slope'] else: ct_slope = 0.02 if 'ct_curve' in kwargs.keys(): ct_curve = kwargs['ct_curve'] else: ct_curve = 0.02 if 'ct_prune' in kwargs.keys(): ct_prune = kwargs['ct_prune'] else: ct_prune = 0.01 if 'ct_max_grids' in kwargs.keys(): ct_max_grids = kwargs['ct_max_grids'] else: ct_max_grids = 5000 # case name params = {} params['T'] = temperature params['p'] = pressure params['phi'] = phi params['a'] = a case = params2name(params) # pressure pressure *= ct.one_atm # gas object #gas = ct.Solution(chemistry) # construct mixutre #mixture = cg.mixture_two_streams(gas, fuel, oxidizer, phi) # unburnt stream #gas.TPX = temperature, pressure, mixture gas = cg.mixture(chemistry, fuel, oxidizer, temperature, pressure, phi) rho_u = gas.density # burnt stream gas.equilibrate('HP') rho_b = gas.density gas = cg.mixture(chemistry, fuel, oxidizer, temperature, pressure, phi) # get inlet velocity based on the strain rate # $a_1=\dfrac{2U_1}{L}\left(1+\dfrac{U_2\sqrt{\rho_2}}{U_1\sqrt{\rho_1}}\right)$ # $a_2=\dfrac{2U_2}{L}\left(1+\dfrac{U_1\sqrt{\rho_1}}{U_2\sqrt{\rho_2}}\right)$ # with $\rho_1 U_1^2 = \rho_2 U_2^2$ # $a_1=\dfrac{4U_1}{L}$ $a_2=\dfrac{4U_2}{L}$ # set stream 1 and 2 for unburnt and equilibrium status respectively v_u = a * width / 4.0 v_b = np.sqrt(rho_u * np.square(v_u) / rho_b) # mass rate m_u = rho_u * v_u m_b = rho_b * v_b # Create flame object f = ct.CounterflowPremixedFlame(gas=gas, width=width) f.transport_model = transport f.P = pressure f.reactants.mdot = m_u f.products.mdot = m_b f.set_refine_criteria(ratio=ct_ratio, slope=ct_slope, curve=ct_curve, prune=ct_prune) f.set_max_grid_points(f.flame, ct_max_grids) # load saved case if presented if solution is not None: f.restore(solution, loglevel=loglevel) # scaling of saved solution solution_width = f.grid[-1] - f.grid[0] width_factor = width / solution_width solution_a = 4. * f.u[0] / solution_width a_factor = a / solution_a normalized_grid = f.grid / solution_width u_factor = a_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 * a_factor) f.set_profile('lambda', normalized_grid, f.L * np.square(a_factor)) f.reactants.mdot = m_u f.products.mdot = m_b else: f.set_initial_guess() f.solve(loglevel=loglevel, auto=True) HRR = f.heat_release_rate idx = HRR.argmax() if HRR[idx] > 1000: f.save('{}.xml'.format(case)) if f.u[idx] > 0: return 0 else: return 2 else: return 1
def Ze(chemistry='FFCM-1.cti', fuel={'CH4': 1.}, oxidizer={ 'O2': 1., 'N2': 3.76 }, T=300., p=1., phi=1., perturb=0.01, **kwargs): # working directory pwd = os.getcwd() # solution name flame_params = {} flame_params['T'] = T flame_params['p'] = p flame_params['phi'] = phi flame_name = params2name(flame_params) solution = '{}.xml'.format(flame_name) # perturbation factor = np.array([1. - perturb, 1., 1. + perturb]) # quantaties flux = np.zeros(3) Tb = np.zeros(3) params = {} for i, f in enumerate(factor): # subfolder params['N2'] = f folder_name = params2name(params) os.makedirs(folder_name, exist_ok=True) os.chdir(folder_name) if not os.path.isfile(solution): # oxidizer stream with perturbation stream = {} for k, v in oxidizer.items(): stream[k] = v stream['N2'] = oxidizer['N2'] * f pc.driver.free_flame(chemistry, fuel, stream, T, p, phi, **kwargs) fs = pc.flame.FreeFlameState(solution, chemistry, fuel, oxidizer) flux[i] = fs.mass_flux() Tb[i] = fs.flame.T[-1] os.chdir(pwd) grad = np.gradient(flux, Tb) Ze = 2. * grad[1] * (Tb[1] - T) / flux[1] return Ze