Exemple #1
0
    def _extend_presumed_pdf_first_dim(lam_lib,
                                       pdf_spec,
                                       added_suffix,
                                       num_procs,
                                       verbose=False):
        turb_dims = [
            Dimension(d.name + added_suffix, d.values) for d in lam_lib.dims
        ]
        if pdf_spec.pdf != 'delta':
            turb_dims.append(
                Dimension(
                    pdf_spec.variance_name, pdf_spec.scaled_variance_values
                    if pdf_spec.scaled_variance_values is not None else
                    pdf_spec.variance_values))
            turb_lib = Library(*turb_dims)
            for p in lam_lib.props:
                turb_lib[p] = turb_lib.get_empty_dataset()

        if pdf_spec.pdf == 'delta':
            turb_lib = Library(*turb_dims)
            for p in lam_lib.props:
                turb_lib[p] = lam_lib[p].copy()
            return turb_lib

        if verbose:
            cput0 = perf_counter()
            num_integrals = len(turb_lib.props)
            for d in turb_lib.dims:
                num_integrals *= d.npts
            print(
                f'{pdf_spec.variance_name}: computing {num_integrals} integrals... ',
                end='',
                flush=True)

        if num_procs == 1:
            for p in turb_lib.props:
                turb_lib[p] = _convolve_full_property(p, None, turb_lib,
                                                      lam_lib, pdf_spec)
        else:
            pool = Pool(processes=num_procs)
            manager = Manager()
            managed_dict = manager.dict()
            pool.map(
                partial(_convolve_full_property,
                        managed_dict=managed_dict,
                        turb_lib=turb_lib,
                        lam_lib=lam_lib,
                        pdf_spec=pdf_spec), turb_lib.props)
            for p in managed_dict:
                turb_lib[p] = managed_dict[p]

        if verbose:
            cputf = perf_counter()
            dcput = cputf - cput0
            avg = float(num_integrals) / dcput
            print(
                f'completed in {dcput:.1f} seconds, average = {int(avg)} integrals/s.'
            )
        return turb_lib
Exemple #2
0
def _apply_presumed_pdf_1var(library,
                             variable_name,
                             pdf_spec,
                             added_suffix=_mean_suffix,
                             num_procs=1,
                             verbose=False):
    require_pytabprops('apply_presumed_PDF_model')

    index = 0
    for d in library.dims:
        if d.name == variable_name:
            break
        else:
            index += 1
    if index == len(library.dims):
        raise KeyError(
            f'Invalid variable name \"{variable_name}\" for library with names {library.dim_names}'
        )

    _pdf_spec = copy.copy(pdf_spec)
    if _pdf_spec.variance_name is None:
        if variable_name == _mixture_fraction_name:
            _pdf_spec.variance_name = _scaled_scalar_variance_name
        else:
            _pdf_spec.variance_name = variable_name + '_variance'

    if index == 0:
        library_t = _extend_presumed_pdf_first_dim(library, _pdf_spec,
                                                   added_suffix, num_procs,
                                                   verbose)
    else:
        swapped_dims = library.dims
        swapped_dims[index], swapped_dims[0] = swapped_dims[0], swapped_dims[
            index]
        swapped_lib = Library(*swapped_dims)
        for p in library.props:
            swapped_lib[p] = np.swapaxes(library[p], 0, index)
        swapped_lib_t = _extend_presumed_pdf_first_dim(swapped_lib, _pdf_spec,
                                                       added_suffix, num_procs,
                                                       verbose)
        swapped_lib_t_dims = copy.copy(swapped_lib_t.dims)
        swapped_lib_t_dims[0], swapped_lib_t_dims[index] = swapped_lib_t_dims[
            index], swapped_lib_t_dims[0]
        library_t = Library(*swapped_lib_t_dims)
        for p in swapped_lib_t.props:
            library_t[p] = np.swapaxes(swapped_lib_t[p], index, 0)

    for key in library.extra_attributes:
        library_t.extra_attributes[key] = copy.copy(
            library.extra_attributes[key])

    return library_t
Exemple #3
0
def _build_nonadiabatic_defect_slfm_library(flamelet_specs,
                                            heat_loss_expansion='transient',
                                            diss_rate_values=np.logspace(
                                                -3, 2, 16),
                                            diss_rate_ref='stoichiometric',
                                            verbose=True,
                                            solver_verbose=False,
                                            h_stoich_spacing=10.e3,
                                            num_procs=1,
                                            integration_args=None,
                                            n_defect_st=32,
                                            extend_defect_dim=False):
    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

    cput00 = _write_library_header('nonadiabatic (defect) SLFM', m, fuel, oxy,
                                   verbose)

    ugt = _build_unstructured_nonadiabatic_defect_slfm_library(
        flamelet_specs, heat_loss_expansion, diss_rate_values, diss_rate_ref,
        verbose, solver_verbose, h_stoich_spacing, num_procs, integration_args)

    structured_defect_table, x_values, g_values = _interpolate_to_structured_defect_dimension(
        ugt, n_defect_st, verbose=verbose, extend=extend_defect_dim)

    key0 = list(structured_defect_table.keys())[0]
    z_values = structured_defect_table[key0][_mixture_fraction_name]

    z_dim = Dimension(_mixture_fraction_name, z_values)
    x_dim = Dimension(_dissipation_rate_name + _stoich_suffix, x_values)
    g_dim = Dimension(_enthalpy_defect_name + _stoich_suffix, g_values)

    output_library = Library(z_dim, x_dim, g_dim)
    output_library.extra_attributes['mech_spec'] = m

    for quantity in structured_defect_table[key0]:
        values = output_library.get_empty_dataset()

        for ix, x in enumerate(x_values):
            for ig, g in enumerate(g_values):
                values[:, ix, ig] = structured_defect_table[(x, g)][quantity]

                output_library[quantity] = values

    _write_library_footer(cput00, verbose)
    return output_library
Exemple #4
0
        def test(self):
            output_library = run()

            gold_file = abspath(join('tests',
                                     'tabulation',
                                     'adiabatic_slfm',
                                     'gold.pkl'))
            gold_library = Library.load_from_file(gold_file)

            for prop in gold_library.props:
                self.assertIsNone(assert_allclose(gold_library[prop], output_library[prop], rtol=1.e-6, atol=1.e-6))
Exemple #5
0
        def test_parallel(self):
            output_library = run(num_procs=2)

            gold_file = abspath(join('tests',
                                     'tabulation',
                                     'nonadiabatic_defect_transient_slfm',
                                     'gold.pkl'))
            gold_library = Library.load_from_file(gold_file)

            for prop in gold_library.props:
                self.assertIsNone(assert_allclose(gold_library[prop], output_library[prop], rtol=2.e-4, atol=1.e-4))
Exemple #6
0
        def test(self):
            output_library = run()

            gold_file = abspath(
                join('tests', 'tabulation',
                     'nonadiabatic_defect_burke_schumann', 'gold.pkl'))
            gold_library = Library.load_from_file(gold_file)

            for prop in gold_library.props:
                self.assertIsNone(
                    assert_allclose(gold_library[prop],
                                    output_library[prop],
                                    atol=1.e-14))
Exemple #7
0
            def test(self):
                output_library = run()

                gold_file = abspath(join('tests',
                                         'tabulation',
                                         'turb_adiabatic_equilibrium',
                                         'gold.pkl'))
                gold_library = Library.load_from_file(gold_file)

                for prop in gold_library.props:
                    self.assertIsNone(assert_allclose(gold_library[prop], output_library[prop], atol=1.e-14))

                self.assertEqual(output_library.dims[0].name, 'mixture_fraction_mean')
                self.assertEqual(output_library.dims[1].name, 'scaled_scalar_variance_mean')
Exemple #8
0
            def test(self):
                gas = ct.Solution('h2o2.yaml', transport_model='Multi')
                mech = ChemicalMechanismSpec.from_solution(gas)

                fs = FlameletSpec(mech_spec=mech,
                                  initial_condition='equilibrium',
                                  oxy_stream=mech.stream('TPX', (300, 1.e5, 'O2:1, N2:3.76')),
                                  fuel_stream=mech.stream('TPY', (300, 1.e5, 'H2:1')),
                                  grid_points=16)
                eq_lib1 = build_adiabatic_eq_library(fs, verbose=False)

                z_dim = Dimension(eq_lib1.mixture_fraction_name, eq_lib1.mixture_fraction_values)
                fuel_T_dim = Dimension('fuel_temperature', np.linspace(0.0, 1.0, 4))
                air_T_dim = Dimension('air_temperature', np.linspace(0.0, 1.0, 3))

                eq_lib2 = Library(z_dim, fuel_T_dim)
                eq_lib2T = Library(fuel_T_dim, z_dim)
                eq_lib3 = Library(z_dim, fuel_T_dim, air_T_dim)
                eq_lib3T1 = Library(fuel_T_dim, z_dim, air_T_dim)
                eq_lib3T2 = Library(fuel_T_dim, air_T_dim, z_dim)

                for p in eq_lib1.props:
                    eq_lib2[p] = eq_lib2.get_empty_dataset()
                    eq_lib2T[p] = eq_lib2T.get_empty_dataset()
                    eq_lib3[p] = eq_lib3.get_empty_dataset()
                    eq_lib3T1[p] = eq_lib3T1.get_empty_dataset()
                    eq_lib3T2[p] = eq_lib3T2.get_empty_dataset()

                for i, fuel_T_offset in enumerate(fuel_T_dim.values):
                    fuel_T = 300 + fuel_T_offset * 500.
                    fs2 = copy(fs)
                    fs2.fuel_stream.TP = fuel_T, 1.e5

                    eq_tmp = build_adiabatic_eq_library(fs2, verbose=False)
                    for p in eq_lib1.props:
                        eq_lib2[p][:, i] = eq_tmp[p]
                        eq_lib2T[p][i, :] = eq_tmp[p]

                    for j, air_T_offset in enumerate(air_T_dim.values):
                        air_T = 300 + air_T_offset * 500.
                        fs3 = copy(fs2)
                        fs3.oxy_stream.TP = air_T, 1.e5

                        eq_tmp = build_adiabatic_eq_library(fs3, verbose=False)
                        for p in eq_lib1.props:
                            eq_lib3[p][:, i, j] = eq_tmp[p]
                            eq_lib3T1[p][i, :, j] = eq_tmp[p]
                            eq_lib3T2[p][i, j, :] = eq_tmp[p]

                nonT_props = list(eq_lib1.props)
                nonT_props.remove('temperature')
                eq_lib1.remove(*nonT_props)
                eq_lib2.remove(*nonT_props)
                eq_lib2T.remove(*nonT_props)
                eq_lib3.remove(*nonT_props)
                eq_lib3T1.remove(*nonT_props)
                eq_lib3T2.remove(*nonT_props)

                z_svv = np.linspace(0., 1., 6)
                Tf_svv = np.linspace(0., 1., 5)

                eq_lib1_t = apply_presumed_PDF_model(eq_lib1, 'ClipGauss', z_svv, verbose=False)
                eq_lib2_t = apply_presumed_PDF_model(eq_lib2, 'ClipGauss', z_svv, verbose=False)
                eq_lib3_t = apply_presumed_PDF_model(eq_lib3, 'ClipGauss', z_svv, num_procs=1, verbose=False)
                eq_lib2T_t = apply_presumed_PDF_model(eq_lib2T, 'ClipGauss', z_svv, verbose=False)
                eq_lib3T1_t = apply_presumed_PDF_model(eq_lib3T1, 'ClipGauss', z_svv, num_procs=1, verbose=False)
                eq_lib3T2_t = apply_presumed_PDF_model(eq_lib3T2, 'ClipGauss', z_svv, num_procs=1, verbose=False)
                eq_lib2_tt = apply_presumed_PDF_model(eq_lib2_t, 'Beta', Tf_svv,
                                                      'fuel_temperature_mean', '', 'Tfvar', num_procs=1, verbose=False)
                eq_lib3_tt = apply_presumed_PDF_model(eq_lib3_t, 'Beta', Tf_svv,
                                                      'fuel_temperature_mean', '', 'Tfvar', num_procs=1, verbose=False)

                def get_dim_names(lib):
                    return [d.name for d in lib.dims]

                self.assertEqual(['mixture_fraction'], get_dim_names(eq_lib1))
                self.assertEqual(['mixture_fraction_mean', 'scaled_scalar_variance_mean'], get_dim_names(eq_lib1_t))
                self.assertEqual(['mixture_fraction', 'fuel_temperature'], get_dim_names(eq_lib2))
                self.assertEqual(['mixture_fraction_mean', 'fuel_temperature_mean', 'scaled_scalar_variance_mean'],
                                 get_dim_names(eq_lib2_t))
                self.assertEqual(
                    ['mixture_fraction_mean', 'fuel_temperature_mean', 'scaled_scalar_variance_mean', 'Tfvar'],
                    get_dim_names(eq_lib2_tt))
                self.assertEqual(['mixture_fraction', 'fuel_temperature', 'air_temperature'],
                                 get_dim_names(eq_lib3))
                self.assertEqual(['mixture_fraction_mean', 'fuel_temperature_mean', 'air_temperature_mean',
                                  'scaled_scalar_variance_mean'],
                                 get_dim_names(eq_lib3_t))
                self.assertEqual(['mixture_fraction_mean', 'fuel_temperature_mean', 'air_temperature_mean',
                                  'scaled_scalar_variance_mean', 'Tfvar'],
                                 get_dim_names(eq_lib3_tt))

                self.assertEqual(['fuel_temperature', 'mixture_fraction'], get_dim_names(eq_lib2T))
                self.assertEqual(['fuel_temperature_mean', 'mixture_fraction_mean', 'scaled_scalar_variance_mean'],
                                 get_dim_names(eq_lib2T_t), eq_lib2T_t)

                self.assertEqual(['fuel_temperature', 'mixture_fraction', 'air_temperature'],
                                 get_dim_names(eq_lib3T1))
                self.assertEqual(['fuel_temperature', 'air_temperature', 'mixture_fraction'],
                                 get_dim_names(eq_lib3T2))
                self.assertEqual(['fuel_temperature_mean', 'mixture_fraction_mean', 'air_temperature_mean',
                                  'scaled_scalar_variance_mean'],
                                 get_dim_names(eq_lib3T1_t))
                self.assertEqual(['fuel_temperature_mean', 'air_temperature_mean', 'mixture_fraction_mean',
                                  'scaled_scalar_variance_mean'],
                                 get_dim_names(eq_lib3T2_t))

                self.assertFalse(np.any(np.isnan(eq_lib1['temperature'])))
                self.assertFalse(np.any(np.isnan(eq_lib1_t['temperature'])))
                self.assertFalse(np.any(np.isnan(eq_lib2['temperature'])))
                self.assertFalse(np.any(np.isnan(eq_lib2T['temperature'])))
                self.assertFalse(np.any(np.isnan(eq_lib2_t['temperature'])))
                self.assertFalse(np.any(np.isnan(eq_lib2T_t['temperature'])))
                self.assertFalse(np.any(np.isnan(eq_lib3['temperature'])))
                self.assertFalse(np.any(np.isnan(eq_lib3T1['temperature'])))
                self.assertFalse(np.any(np.isnan(eq_lib3T2['temperature'])))
                self.assertFalse(np.any(np.isnan(eq_lib3_t['temperature'])))
                self.assertFalse(np.any(np.isnan(eq_lib3_tt['temperature'])))
                self.assertFalse(np.any(np.isnan(eq_lib3T1_t['temperature'])))
                self.assertFalse(np.any(np.isnan(eq_lib3T2_t['temperature'])))

                self.assertIsNone(assert_allclose(eq_lib2T['temperature'].T, eq_lib2['temperature']))
                self.assertIsNone(assert_allclose(np.swapaxes(eq_lib3T1['temperature'], 0, 1),
                                                  eq_lib3['temperature']))
                self.assertIsNone(assert_allclose(np.swapaxes(np.swapaxes(eq_lib3T2['temperature'], 1, 2), 0, 1),
                                                  eq_lib3['temperature']))

                self.assertIsNone(assert_allclose(np.squeeze(eq_lib1_t['temperature'][:, 0]),
                                                  eq_lib1['temperature']))
                self.assertIsNone(assert_allclose(np.squeeze(eq_lib2_t['temperature'][:, :, 0]),
                                                  eq_lib2['temperature']))
                self.assertIsNone(assert_allclose(np.squeeze(eq_lib3_t['temperature'][:, :, :, 0]),
                                                  eq_lib3['temperature']))
                self.assertIsNone(assert_allclose(np.squeeze(eq_lib3_tt['temperature'][:, :, :, 0, 0]),
                                                  eq_lib3['temperature']))
Exemple #9
0
    def integrate(self,
                  stop_at_time=None,
                  stop_at_steady=None,
                  stop_criteria=None,
                  first_time_step=1.e-6,
                  max_time_step=1.e6,
                  minimum_time_step_count=40,
                  transient_tolerance=1.e-10,
                  write_log=False,
                  log_rate=100,
                  maximum_steps_per_jacobian=1,
                  nonlinear_solve_tolerance=1.e-12,
                  linear_solver='lapack',
                  plot=None,
                  stepper_type=KennedyCarpenterS6P4Q3,
                  nlsolver_type=SimpleNewtonSolver,
                  stepcontrol_type=PIController,
                  extra_integrator_args=dict(),
                  extra_stepper_args=dict(),
                  extra_nlsolver_args=dict(),
                  extra_stepcontrol_args=dict(),
                  save_first_and_last_only=False):
        """Base method for reactor integration

            Parameters
            ----------
            stop_at_time : float
                The final time to stop the simulation at
            stop_at_steady : float
                The tolerance at which a steady state is decided upon and stopped at
            stop_criteria : callable (t, state, residual, n_steps)
                Any callable that returns True when the simulation should stop
            first_time_step : float
                The time step size initially used by the time integrator
            max_time_step : float
                The largest time step the time stepper is allowed to take
            minimum_time_step_count : int
                The minimum number of time steps to run (helpful for slowly evolving simulations, for instance those with low starting temperatures)
            transient_tolerance : float
                the target temporal error for transient integration
            write_log : bool
                whether or not to print integration statistics and status during the simulation
            log_rate : int
                how often to print log information
            maximum_steps_per_jacobian : int
                maximum number of steps Spitfire allows before the Jacobian must be re-evaluated - keep low for robustness, try to increase for performance on large mechanisms
            nonlinear_solve_tolerance : float
                tolerance for the nonlinear solver used in implicit time stepping (optional, default: 1e-12)
            linear_solver : str
                which linear solver to use, at the moment either 'lapack' (dense, direct) or 'superlu' (sparse, direct) are available
            plot : list
                List of variables (temperature and/or specific species names) to be plotted after the time integration completes.
                No plot is shown if a list is not provided.
                Temperature is plotted in the first subplot if any list of variables is provided for plotting (even if temperature is not specified in the list of variables).
                Species mass fractions will be plotted in a second subplot if any species names are provided in the list of variables.
            stepper_type : spitfire.time.TimeStepper
                which (single step) stepper method to use (optional, default: ESDIRK64)
            nlsolver_type : spitfire.time.NonlinearSolver
                which nonlinear solver method to use (optional, default: SimpleNewtonSolver)
            stepcontrol_type : spitfire.time.StepControl
                which time step adaptation method to use (optional, default: PIController)
            extra_integrator_args : dict
                any extra arguments to specify to the time integrator - arguments passed to the odesolve method
            extra_stepper_args : dict
                extra arguments to specify on the spitfire.time.TimeStepper object
            extra_nlsolver_args : dict
                extra arguments to specify on the spitfire.time.NonlinearSolver object
            extra_stepcontrol_args : dict
                extra arguments to specify on the spitfire.time.StepControl object
            save_first_and_last_only : bool
                whether or not to retain all data (False, default) or only the first and last solutions

            Returns
            -------
                a library containing temperature, mass fractions, and density (isochoric) or pressure (isobaric) over time
        """

        def post_step_callback(t, state, *args):
            state[state < 0.] = 0.
            return state

        integrator_args = {'stop_criteria': stop_criteria}
        if stop_at_time is not None:
            integrator_args.update({'stop_at_time': stop_at_time})
        if stop_at_steady is not None:
            integrator_args.update({'stop_at_steady': stop_at_steady})

        integrator_args.update(extra_integrator_args)

        # build the step controller and set attributes
        step_control_args = {'first_step': first_time_step,
                             'max_step': max_time_step,
                             'target_error': transient_tolerance}
        step_control_args.update(extra_stepcontrol_args)
        step_controller = stepcontrol_type(**step_control_args)

        # build the nonlinear solver and set attributes
        nonlinear_solver_args = {'evaluate_jacobian_every_iter': False,
                                 'norm_weighting': 1. / self._variable_scales,
                                 'tolerance': nonlinear_solve_tolerance}
        nonlinear_solver_args.update(extra_nlsolver_args)
        newton_solver = nlsolver_type(**nonlinear_solver_args)

        # build the stepper method and set attributes
        stepper_args = {'nonlinear_solver': newton_solver, 'norm_weighting': 1. / self._variable_scales}
        stepper_args.update(extra_stepper_args)
        stepper = stepper_type(**stepper_args)

        # build the rhs and projector methods and do the integration
        if self._configuration == 'isobaric':
            def rhs_method(time, state):
                k = np.zeros(self._n_equations)
                self._update_mass_transfer_parameters(time)
                self._update_heat_transfer_parameters(time)
                self._griffon.reactor_rhs_isobaric(state, self._initial_pressure,
                                                   self._tf_value, self._yf_value, self._tau_value,
                                                   self._tc_value, self._tr_value,
                                                   self._cc_value, self._re_value,
                                                   self._surface_area_to_volume,
                                                   self._heat_transfer_option, self._is_open, k)
                return k

            def jac_method(state):
                k = np.zeros(self._n_equations)
                j = np.zeros(self._n_equations * self._n_equations)
                self._griffon.reactor_jac_isobaric(state, self._initial_pressure,
                                                   self._tf_value, self._yf_value, self._tau_value,
                                                   self._tc_value, self._tr_value,
                                                   self._cc_value, self._re_value,
                                                   self._surface_area_to_volume,
                                                   self._heat_transfer_option, self._is_open,
                                                   self._rates_sensitivity_option, self._sensitivity_transform_option,
                                                   k, j)
                return j.reshape((self._n_equations, self._n_equations), order='F')

        elif self._configuration == 'isochoric':
            def rhs_method(time, state):
                k = np.zeros(self._n_equations)
                self._update_mass_transfer_parameters(time)
                self._update_heat_transfer_parameters(time)
                self._griffon.reactor_rhs_isochoric(state,
                                                    self._rf_value, self._tf_value, self._yf_value, self._tau_value,
                                                    self._tc_value, self._tr_value,
                                                    self._cc_value, self._re_value,
                                                    self._surface_area_to_volume,
                                                    self._heat_transfer_option, self._is_open, k)
                return k

            def jac_method(state):
                k = np.zeros(self._n_equations)
                j = np.zeros(self._n_equations * self._n_equations)
                self._griffon.reactor_jac_isochoric(state,
                                                    self._rf_value, self._tf_value, self._yf_value, self._tau_value,
                                                    self._tc_value, self._tr_value,
                                                    self._cc_value, self._re_value,
                                                    self._surface_area_to_volume,
                                                    self._heat_transfer_option, self._is_open,
                                                    self._rates_sensitivity_option, k, j)
                return j.reshape((self._n_equations, self._n_equations), order='F')

        setup_wrapper = getattr(self, '_' + linear_solver + '_setup_wrapper')
        setup_method = lambda t, state, prefactor: setup_wrapper(jac_method, state, prefactor)

        solve_method = self._lapack_solve if linear_solver == 'lapack' else self._superlu_solve

        output = odesolve(right_hand_side=rhs_method,
                          initial_state=self._current_state,
                          initial_time=self._current_time,
                          step_size=step_controller,
                          method=stepper,
                          linear_setup=setup_method,
                          linear_solve=solve_method,
                          minimum_time_step_count=minimum_time_step_count,
                          linear_setup_rate=maximum_steps_per_jacobian,
                          verbose=write_log,
                          log_rate=log_rate,
                          extra_logger_log=self._extra_logger_log,
                          extra_logger_title_line1=self._extra_logger_title_line1,
                          extra_logger_title_line2=self._extra_logger_title_line2,
                          norm_weighting=1. / self._variable_scales,
                          post_step_callback=post_step_callback,
                          save_each_step=not save_first_and_last_only,
                          **integrator_args)

        if save_first_and_last_only:
            current_state, current_time, time_step_size = output
            self._current_state = np.copy(current_state)
            self._current_time = np.copy(current_time)
            states = np.zeros((1, current_state.size))
            states[0, :] = current_state
            t = np.zeros(1)
            t[0] = current_time
        else:
            t, states = output
            self._current_state = np.copy(states[-1, :])
            self._current_time = np.copy(t[-1])

        time_dimension = Dimension('time', t)
        output_library = Library(time_dimension)

        if self._configuration == 'isobaric':
            self._current_pressure = self._initial_pressure
            self._current_temperature = self._current_state[0]
            ynm1 = self._current_state[1:]
            self._current_mass_fractions = hstack((ynm1, 1. - sum(ynm1)))
            output_library['temperature'] = states[:, 0]
            output_library['pressure'] = self.current_pressure + np.zeros_like(output_library['temperature'])

        elif self._configuration == 'isochoric':
            ynm1 = self._current_state[2:]
            self._current_mass_fractions = hstack((ynm1, 1. - sum(ynm1)))
            self._current_pressure = self._current_state[0] * self._current_state[1] / sum(
                [yi / Mi for (yi, Mi) in zip(self._current_mass_fractions.tolist(),
                                             self._mechanism.molecular_weights.tolist())])
            self._current_temperature = self._current_state[1]
            output_library['density'] = states[:, 0]
            output_library['temperature'] = states[:, 1]

        species_names = self._mechanism.species_names
        output_library['mass fraction ' + species_names[-1]] = np.ones_like(output_library['temperature'])
        for i, s in enumerate(species_names[:-1]):
            output_library['mass fraction ' + s] = states[:, self._temperature_index + 1 + i]
            output_library['mass fraction ' + species_names[-1]] -= output_library['mass fraction ' + s]

        if plot is not None:
            t = output_library.time_values * 1.e3
            T = output_library['temperature']
            if plot == ['temperature']:  # only plot temperature if it is the only requested variable
                plt.semilogx(t, T)
                plt.xlabel('time (ms)')
                plt.ylabel('Temperature (K)')
                plt.grid()
                plt.show()
            else:  # if variables other than temperature are included in the list, plot those in a separate subplot
                f, (axT, axY) = plt.subplots(2, sharex=True, sharey=False)
                axT.semilogx(t, T)  # always plot T
                axT.set_ylabel('Temperature (K)')
                axT.grid()
                for species_vars in plot:
                    if species_vars is not 'temperature':  # separate subplot for species mass fractions
                        Y = output_library['mass fraction ' + species_vars]
                        axY.loglog(t, Y, label=species_vars)
                axY.set_xlabel('time (ms)')
                axY.set_ylabel('Mass Fractions')
                axY.set_ylim([1.e-12, 1.e0])
                axY.grid()
                axY.legend()
                plt.show()

        return output_library
Exemple #10
0
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)
Exemple #11
0
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
Exemple #12
0
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
Exemple #13
0
def apply_mixing_model(library,
                       mixing_spec,
                       added_suffix=_mean_suffix,
                       num_procs=1,
                       verbose=False):
    """Take an existing tabulated chemistry library and incorporate subgrid variation in each reaction variable with a
        presumed PDF model. This requires statistical independence of the reaction variables. If a reaction variable
        is not included in the mixing_spec dictionary, a delta PDF is presumed for it.

    Parameters
    ----------
    library : spitfire.Library
        an existing library from a reaction model
    mixing_spec : str
        a dictionary mapping reaction variable names to PDFSpec objects that describe the variable's presumed PDF
    added_suffix : str
        string to add to each name, for instance '_mean' if this is the first PDF convolution, or '' if a successive convolution
    num_procs : Int
        how many processors over which to distribute the parallel extinction solves
    verbose : bool
        whether or not (default False) to print information about the PDF convolution

    Returns
    -------
    library : spitfire.Library instance
        the library with any nontrivial variance dimensions added

    """
    require_pytabprops('apply_mixing_model')

    for dim in mixing_spec:
        if dim not in library.dim_names:
            raise KeyError(
                f'Invalid variable name \"{dim}\" for library with names {library.dim_names}'
            )

    mixing_spec = copy.copy(mixing_spec)
    for dim in library.dims:
        if dim.name not in mixing_spec:
            mixing_spec[dim.name] = 'delta'

    for dim in mixing_spec:
        if mixing_spec[dim] == 'delta':
            mixing_spec[dim] = PDFSpec('delta', variance_values=np.array([0.]))

    for dim in mixing_spec:
        if (mixing_spec[dim].variance_values is None
                and mixing_spec[dim].scaled_variance_values is None
                and mixing_spec[dim].pdf == 'delta'):
            mixing_spec[dim] = PDFSpec('delta', variance_values=np.array([0.]))

    extensions = []
    for i, key in enumerate(mixing_spec):
        extensions.append({
            'variable_name': key,
            'pdf_spec': mixing_spec[key],
            'num_procs': num_procs,
            'verbose': verbose
        })

    def get_size(element):
        pdfspec = element['pdf_spec']
        if pdfspec.pdf == 'delta':
            return 0
        else:
            if pdfspec.variance_values is not None:
                return pdfspec.variance_values.size
            else:
                return pdfspec.scaled_variance_values.size

    extensions = sorted(extensions, key=lambda element: get_size(element))
    turb_lib = Library.copy(library)
    for i, ext in enumerate(extensions):
        ext['library'] = turb_lib
        ext['added_suffix'] = '' if i < len(extensions) - 1 else added_suffix
        turb_lib = _apply_presumed_pdf_1var(**ext)
        if get_size(ext) == 1 and ext['pdf_spec'].pdf != 'delta':
            turb_lib = Library.squeeze(
                turb_lib[tuple([slice(None)] * (len(turb_lib.dims) - 1) +
                               [slice(0, 1)])])

    if 'mixing_spec' in turb_lib.extra_attributes:
        turb_lib.extra_attributes['mixing_spec'].update(mixing_spec)
    else:
        turb_lib.extra_attributes['mixing_spec'] = mixing_spec
    return turb_lib