def make_clustered_grid(nx, ny, x_cp, y_cp, x_cc, y_cc): x_half1 = Flamelet._clustered_grid(nx // 2, x_cp * 2., x_cc)[0] * 0.5 y_half1 = Flamelet._clustered_grid(nx // 2, y_cp * 2., y_cc)[0] * 0.5 x_half2 = (0.5 - x_half1)[::-1] y_half2 = (0.5 - y_half1)[::-1] x_range = np.hstack((x_half1, 0.5 + x_half2)) y_range = np.hstack((y_half1, 0.5 + y_half2)) dx_mid = x_range[nx // 2 - 1] - x_range[nx // 2 - 2] x_range[nx // 2 - 1] -= dx_mid / 3. x_range[nx // 2 + 0] += dx_mid / 3. dy_mid = y_range[ny // 2 - 1] - y_range[ny // 2 - 2] y_range[ny // 2 - 1] -= dy_mid / 3. y_range[ny // 2 + 0] += dy_mid / 3. return x_range, y_range
def build_unreacted_library(flamelet_specs, verbose=True): """Build a flamelet library for a nonreacting flow, with linear enthalpy and mass fraction profiles. Parameters ---------- flamelet_specs : FlameletSpec or dictionary of arguments for a FlameletSpec flamelet specifications Returns ------- library : spitfire.chemistry.library.Library instance a chemistry library with only the "mixture_fraction" dimension """ fs = FlameletSpec(**flamelet_specs) if isinstance( flamelet_specs, dict) else copy.copy(flamelet_specs) fs.initial_condition = 'unreacted' flamelet = Flamelet(fs) return flamelet.make_library_from_interior_state( flamelet.initial_interior_state)
def build_adiabatic_bs_library(flamelet_specs, verbose=True): """Build a flamelet library with the Burke-Schumann (idealized, one-step combustion) assumptions Parameters ---------- flamelet_specs : FlameletSpec or dictionary of arguments for a FlameletSpec flamelet specifications Returns ------- library : spitfire.chemistry.library.Library instance a chemistry library with only the "mixture_fraction" dimension """ fs = FlameletSpec(**flamelet_specs) if isinstance( flamelet_specs, dict) else copy.copy(flamelet_specs) fs.initial_condition = 'Burke-Schumann' flamelet = Flamelet(fs) return flamelet.make_library_from_interior_state( flamelet.initial_interior_state)
def build_adiabatic_eq_library(flamelet_specs, verbose=True): """Build a flamelet library with the equilibrium (infinitely fast) chemistry assumption, equivalently with Gibbs free energy minimization. Parameters ---------- flamelet_specs : FlameletSpec or dictionary of arguments for a FlameletSpec flamelet specifications Returns ------- library : spitfire.chemistry.library.Library instance a chemistry library with only the "mixture_fraction" dimension """ fs = FlameletSpec(**flamelet_specs) if isinstance( flamelet_specs, dict) else copy.copy(flamelet_specs) fs.initial_condition = 'equilibrium' flamelet = Flamelet(fs) return flamelet.make_library_from_interior_state( flamelet.initial_interior_state)
def _expand_enthalpy_defect_dimension_steady(chi_st, managed_dict, flamelet_specs, table_dict, h_stoich_spacing, verbose, input_integration_args, solver_verbose): flamelet_specs.initial_condition = table_dict[chi_st]['adiabatic_state'] flamelet_specs.stoich_dissipation_rate = chi_st flamelet_specs.heat_transfer = 'nonadiabatic' flamelet_specs.scale_heat_loss_by_temp_range = False flamelet_specs.scale_convection_by_dissipation = False flamelet_specs.use_linear_ref_temp_profile = True flamelet_specs.radiative_emissivity = 0. flamelet_specs.convection_coefficient = 0. flamelet = Flamelet(flamelet_specs) first = True refine_before_extinction = False extinguished = False extinguished_first = False maxT = -1 state_old = np.copy(flamelet.current_interior_state) hval = 0. dh = 1.e-1 diff_target = 1e-1 diff_norm = 1e-1 hval_max = 1.e10 solutions = [] hvalues = [] hvalues.append(hval) solutions.append(dict()) for p in table_dict[chi_st]: if p != 'adiabatic_state': solutions[-1][p] = table_dict[chi_st][p] current_state = table_dict[chi_st]['adiabatic_state'] cput0000 = perf_counter() while first or (not extinguished and hval < hval_max): hval += dh if first: first = False flamelet_specs.convection_coefficient = hval flamelet_specs.initial_condition = current_state flamelet = Flamelet(flamelet_specs) g_library = flamelet.compute_steady_state(verbose=solver_verbose) current_state = flamelet.current_interior_state maxT = np.max(current_state) diff_norm = np.max( np.abs(current_state - state_old) / (np.abs(current_state) + 1.e-4)) extinguished = maxT < ( np.max([flamelet.oxy_stream.T, flamelet.fuel_stream.T]) + 10.) if (extinguished and (not extinguished_first)) and refine_before_extinction: extinguished_first = True extinguished = False hval -= dh dh *= 0.1 diff_target *= 0.1 current_state = state_old.copy() continue state_old = np.copy(current_state) dh *= np.min([np.max([np.sqrt(diff_target / diff_norm), 0.1]), 2.]) hvalues.append(hval) solutions.append(dict()) for p in g_library.props: solutions[-1][p] = g_library[p].ravel() z_dim = Dimension(_mixture_fraction_name, flamelet.mixfrac_grid) h_dim = Dimension(_enthalpy_defect_name + _stoich_suffix, np.array(hvalues)) steady_lib = Library(z_dim, h_dim) steady_lib.extra_attributes['mech_spec'] = flamelet_specs.mech_spec for p in table_dict[chi_st]: if p != 'adiabatic_state': steady_lib[p] = steady_lib.get_empty_dataset() for ig, sol in enumerate(solutions): for p in sol: steady_lib[p][:, ig] = sol[p].ravel() indices = [0] z = flamelet.mixfrac_grid z_st = flamelet.mechanism.stoich_mixture_fraction(flamelet.fuel_stream, flamelet.oxy_stream) h_tz = sca.compute_specific_enthalpy(flamelet_specs.mech_spec, steady_lib)['enthalpy'] h_ad = h_tz[:, 0] nz, nt = h_tz.shape last_hst = interp1d(z, h_ad)(z_st) for i in range(nt - 1): this_hst = interp1d(z, h_tz[:, i])(z_st) if last_hst - this_hst > h_stoich_spacing: indices.append(i) last_hst = this_hst for i in indices: defect = h_tz[:, i] - h_ad gst = float(interp1d(z, defect)(z_st)) this_data = dict() this_data['enthalpy_defect'] = np.copy(defect) this_data['enthalpy_cons'] = np.copy(h_ad) this_data['enthalpy'] = np.copy(h_tz[:, i]) this_data[_mixture_fraction_name] = flamelet.mixfrac_grid for q in steady_lib.props: this_data[q] = steady_lib[q][:, i] managed_dict[(chi_st, gst)] = this_data dcput = perf_counter() - cput0000 if verbose: print('chi_st = {:8.1e} 1/s converged in {:6.2f} s'.format( chi_st, dcput), flush=True)
def _expand_enthalpy_defect_dimension_transient(chi_st, managed_dict, flamelet_specs, table_dict, h_stoich_spacing, verbose, input_integration_args, solver_verbose): flamelet_specs.initial_condition = table_dict[chi_st]['adiabatic_state'] flamelet_specs.stoich_dissipation_rate = chi_st flamelet_specs.heat_transfer = 'nonadiabatic' flamelet_specs.scale_heat_loss_by_temp_range = True flamelet_specs.scale_convection_by_dissipation = True flamelet_specs.use_linear_ref_temp_profile = True flamelet_specs.convection_coefficient = 1.e7 flamelet_specs.radiative_emissivity = 0. integration_args = dict({ 'first_time_step': 1.e-9, 'max_time_step': 1.e-1, 'write_log': solver_verbose, 'log_rate': 100 }) if input_integration_args is not None: integration_args.update(input_integration_args) if 'transient_tolerance' not in integration_args: integration_args['transient_tolerance'] = 1.e-8 cput0000 = perf_counter() running = True while running and integration_args['transient_tolerance'] > 1.e-15: try: fnonad = Flamelet(flamelet_specs) transient_lib = fnonad.integrate_for_heat_loss(**integration_args) running = False except Exception as e: if solver_verbose: print( f'Transient heat loss calculation failed with tolerance of {integration_args["transient_tolerance"]:.1e}, retrying with 100x lower...' ) integration_args.update( dict({ 'transient_tolerance': integration_args['transient_tolerance'] * 1.e-2 })) indices = [0] z = fnonad.mixfrac_grid z_st = fnonad.mechanism.stoich_mixture_fraction(fnonad.fuel_stream, fnonad.oxy_stream) h_tz = sca.compute_specific_enthalpy(flamelet_specs.mech_spec, transient_lib)['enthalpy'] h_ad = h_tz[0, :] nt, nz = h_tz.shape last_hst = interp1d(z, h_ad)(z_st) for i in range(nt): this_hst = interp1d(z, h_tz[i, :])(z_st) if last_hst - this_hst > h_stoich_spacing: indices.append(i) last_hst = this_hst if nt - 1 not in indices: indices.append(-1) for i in indices: defect = h_tz[i, :] - h_ad gst = float(interp1d(z, defect)(z_st)) this_data = dict() this_data['enthalpy_defect'] = np.copy(defect) this_data['enthalpy_cons'] = np.copy(h_ad) this_data['enthalpy'] = np.copy(h_tz[i, :]) this_data[_mixture_fraction_name] = fnonad.mixfrac_grid for q in transient_lib.props: this_data[q] = transient_lib[q][i, :] managed_dict[(chi_st, gst)] = this_data dcput = perf_counter() - cput0000 if verbose: print('chi_st = {:8.1e} 1/s converged in {:6.2f} s'.format( chi_st, dcput), flush=True)
def build_adiabatic_slfm_library(flamelet_specs, diss_rate_values=np.logspace(-3, 2, 16), diss_rate_ref='stoichiometric', verbose=True, solver_verbose=False, _return_intermediates=False, include_extinguished=False): """Build a flamelet library with an adiabatic strained laminar flamelet model Parameters ---------- flamelet_specs : dictionary or FlameletSpec instance data for the mechanism, streams, mixture fraction grid, etc. diss_rate_values : np.array reference dissipation rate values in the table (note that if the flamelet extinguishes at any point, the extinguished flamelet and larger dissipation rates are not included in the library unless the include_extinguished argument is set to True) diss_rate_ref : str the reference point of the specified dissipation rate values, either 'stoichiometric' or 'maximum' verbose : bool whether or not to show progress of the library construction include_extinguished : bool whether or not to include extinguished states in the output table, if encountered in the provided range of dissipation rates, off by default Returns ------- library : spitfire.chemistry.library.Library instance the structured chemistry library """ if isinstance(flamelet_specs, dict): flamelet_specs = FlameletSpec(**flamelet_specs) m = flamelet_specs.mech_spec fuel = flamelet_specs.fuel_stream oxy = flamelet_specs.oxy_stream flamelet_specs.initial_condition = 'equilibrium' if diss_rate_ref == 'maximum': flamelet_specs.max_dissipation_rate = 0. else: flamelet_specs.stoich_dissipation_rate = 0. cput00 = _write_library_header('adiabatic SLFM', m, fuel, oxy, verbose) f = Flamelet(flamelet_specs) table_dict = dict() nchi = diss_rate_values.size suffix = _stoich_suffix if diss_rate_ref == 'stoichiometric' else '_max' x_values = list() for idx, chival in enumerate(diss_rate_values): if diss_rate_ref == 'maximum': flamelet_specs.max_dissipation_rate = chival else: flamelet_specs.stoich_dissipation_rate = chival flamelet = Flamelet(flamelet_specs) if verbose: print(f'{idx + 1:4}/{nchi:4} (chi{suffix} = {chival:8.1e} 1/s) ', end='', flush=True) cput0 = perf_counter() x_library = flamelet.compute_steady_state(tolerance=1.e-6, verbose=solver_verbose, use_psitc=True) dcput = perf_counter() - cput0 if np.max(flamelet.current_temperature - flamelet.linear_temperature ) < 10. and not include_extinguished: if verbose: print( ' extinction detected, stopping. The extinguished state will not be included in the table.' ) break else: if verbose: print( f' converged in {dcput:6.2f} s, T_max = {np.max(flamelet.current_temperature):6.1f}' ) z_st = flamelet.mechanism.stoich_mixture_fraction( flamelet.fuel_stream, flamelet.oxy_stream) chi_st = flamelet._compute_dissipation_rate( np.array([z_st]), flamelet._max_dissipation_rate, flamelet._dissipation_rate_form)[0] x_values.append(chi_st) table_dict[chi_st] = dict() for k in x_library.props: table_dict[chi_st][k] = x_library[k].ravel() flamelet_specs.initial_condition = flamelet.current_interior_state if _return_intermediates: table_dict[chi_st]['adiabatic_state'] = np.copy( flamelet.current_interior_state) if _return_intermediates: _write_library_footer(cput00, verbose) return table_dict, f.mixfrac_grid, np.array(x_values) else: z_dim = Dimension(_mixture_fraction_name, f.mixfrac_grid) x_dim = Dimension(_dissipation_rate_name + _stoich_suffix, np.array(x_values)) output_library = Library(z_dim, x_dim) output_library.extra_attributes['mech_spec'] = m for quantity in table_dict[chi_st]: output_library[quantity] = output_library.get_empty_dataset() for ix, x in enumerate(x_values): output_library[quantity][:, ix] = table_dict[x][quantity] _write_library_footer(cput00, verbose) return output_library
def _build_nonadiabatic_defect_unstrained_library(initialization, flamelet_specs, n_defect_st=16, verbose=True): flamelet_specs = FlameletSpec(**flamelet_specs) if isinstance( flamelet_specs, dict) else copy.copy(flamelet_specs) m = flamelet_specs.mech_spec fuel = flamelet_specs.fuel_stream oxy = flamelet_specs.oxy_stream z_st = m.stoich_mixture_fraction(fuel, oxy) flamelet_specs.initial_condition = initialization flamelet = Flamelet(flamelet_specs) # compute the extreme enthalpy defect state_ad = flamelet.initial_interior_state adiabatic_lib = flamelet.make_library_from_interior_state(state_ad) enthalpy_ad = sca.compute_specific_enthalpy(m, adiabatic_lib)['enthalpy'] z_interior = flamelet.mixfrac_grid[1:-1] state_cooled_eq = state_ad.copy() state_cooled_eq[::m. n_species] = z_interior * fuel.T + (1 - z_interior) * oxy.T cooled_lib = flamelet.make_library_from_interior_state(state_cooled_eq) enthalpy_cooled_eq = sca.compute_specific_enthalpy(m, cooled_lib)['enthalpy'] z = flamelet.mixfrac_grid h_ad_st = interp1d(z, enthalpy_ad)(z_st) h_ce_st = interp1d(z, enthalpy_cooled_eq)(z_st) defect_ext = h_ad_st - h_ce_st # build the library with equilibrium solutions at with enthalpies offset by the triangular defect form defect_range = np.linspace(-defect_ext, 0, n_defect_st)[::-1] z_dim = Dimension(_mixture_fraction_name, flamelet.mixfrac_grid) g_dim = Dimension(_enthalpy_defect_name + _stoich_suffix, defect_range) output_library = Library(z_dim, g_dim) output_library.extra_attributes['mech_spec'] = m for p in adiabatic_lib.props: output_library[p] = output_library.get_empty_dataset() output_library['enthalpy_defect'] = output_library.get_empty_dataset() output_library['enthalpy_cons'] = output_library.get_empty_dataset() output_library['enthalpy'] = output_library.get_empty_dataset() output_library[_mixture_fraction_name] = output_library.get_empty_dataset() fz = z.copy() fz[z <= z_st] = z[z <= z_st] / z_st fz[z > z_st] = (1 - z[z > z_st]) / (1 - z_st) ns = m.n_species g_library = flamelet.make_library_from_interior_state( flamelet.initial_interior_state) for ig in range(n_defect_st): defected_enthalpy = enthalpy_ad + defect_range[ig] * fz for iz in range(1, z.size - 1): y = np.zeros(ns) for ispec in range(ns): y[ispec] = g_library['mass fraction ' + m.species_names[ispec]][iz] m.gas.HPY = defected_enthalpy[iz], flamelet.pressure, y if initialization == 'equilibrium': m.gas.equilibrate('HP') g_library['temperature'][iz] = m.gas.T for ispec in range(ns): g_library['mass fraction ' + m.species_names[ispec]][iz] = m.gas.Y[ispec] for p in g_library.props: if p != 'defected_enthapy': output_library[p][:, ig] = g_library[p].ravel() output_library['enthalpy_defect'][:, ig] = defected_enthalpy - enthalpy_ad output_library['enthalpy_cons'][:, ig] = enthalpy_ad output_library['enthalpy'][:, ig] = defected_enthalpy output_library[ _mixture_fraction_name][:, ig] = flamelet.mixfrac_grid.ravel() return output_library
def update_figure(cpoint, ccoeff, npts): z, inv_dz = get_data(cpoint, ccoeff, npts) traces_dz = [ dict( x=z, y=inv_dz, mode='lines+markers', opacity=0.7, line=dict(width=4, color='Black'), marker={ 'symbol': 'square', 'size': 8, 'color': 'RoyalBlue', 'line': dict(width=1, color='Black') }, ) ] fs = dict(flamelet_specs) fs.update( dict({ 'grid_points': npts, 'grid_cluster_intensity': ccoeff, 'grid_cluster_point': cpoint, 'initial_condition': 'equilibrium' })) f = Flamelet(**fs) T_eq = f.initial_temperature fs.update(dict({'initial_condition': 'Burke-Schumann'})) f = Flamelet(**fs) T_bs = f.initial_temperature traces_T = [ dict( name='Equilibrium', x=z, y=T_eq, mode='lines+markers', opacity=0.7, line=dict(width=4, color='Black'), marker={ 'symbol': 'square', 'size': 8, 'color': 'RoyalBlue', 'line': dict(width=1, color='Black') }, ), dict( name='Burke-Schumann', x=z, y=T_bs, mode='lines+markers', opacity=0.7, line=dict(width=4, color='Black'), marker={ 'symbol': 'square', 'size': 8, 'color': 'Salmon', 'line': dict(width=1, color='Black') }, ) ] return { 'data': traces_dz, 'layout': dict( xaxis={'type': 'linear', 'title': 'mixture fraction', 'range': [0, 1]}, yaxis={'type': 'log', 'title': 'equivalent uniform grid points', 'range': [0, 4]}, margin={'l': 40, 'b': 40, 't': 10, 'r': 10}, legend={'x': 0, 'y': 1}, hovermode='closest', transition={'duration': 1e-3}, ) }, \ { 'data': traces_T, 'layout': dict( xaxis={'type': 'linear', 'title': 'mixture fraction', 'range': [0, 1]}, yaxis={'type': 'linear', 'title': 'T (K)', 'range': [298, 3000]}, margin={'l': 40, 'b': 40, 't': 10, 'r': 10}, legend={'x': 0, 'y': 1}, hovermode='closest', transition={'duration': 1e-3}, ) }
def get_data(cpoint, ccoeff, npts): z, dz = Flamelet._clustered_grid(npts, cpoint, ccoeff) dz = np.hstack([dz, 1. - z[-2]]) return z, 1. / dz