Beispiel #1
0
    def to_omkm_yaml(self, act_energy_unit=None, units=None):
        """Writes the object in Cantera's YAML format.

        Parameters
        ----------
            act_energy_unit : str, optional
                Unit to use for activation energy. Default is 'cal/mol'
            units : :class:`~pmutt.omkm.units.Units` object
                If specified, `act_energy_unit` is overwritten.
                Default is None.
        Returns
        -------
            yaml_dict : dict
                Dictionary compatible with Cantera's YAML format
        """
        if units is not None:
            act_energy_unit = units.act_energy

        yaml_dict = {}
        yaml_dict['id'] = self.name
        yaml_dict['slope'] = self.slope
        # Assign intercept
        intercept = c.convert_unit(self.intercept, 'kcal/mol', act_energy_unit)
        intercept_param = _Param('intercept', intercept, '_act_energy')
        _assign_yaml_val(intercept_param, yaml_dict, units)

        yaml_dict['direction'] = self.direction
        if self.synthesis_reactions is not None \
           and len(self.synthesis_reactions) > 0:
            synthesis_reactions = _get_omkm_range(objs=self.synthesis_reactions,
                                                  parent_obj=self,
                                                  format='list')
            yaml_dict['synthesis-reactions'] = synthesis_reactions

        if self.cleavage_reactions is not None \
           and len(self.cleavage_reactions) > 0:
            cleavage_reactions = _get_omkm_range(objs=self.cleavage_reactions,
                                                 parent_obj=self,
                                                 format='list')
            yaml_dict['cleavage-reactions'] = cleavage_reactions

        return yaml_dict
Beispiel #2
0
    def to_omkm_yaml(self, energy_unit=None, units=None):
        """Writes the object in Cantera's YAML format.

        Parameters
        ----------
            energy_unit : str, optional
                Unit to use for energy. Default is 'cal/mol'
            units : :class:`~pmutt.omkm.units.Units` object
                If specified, `energy_unit` is overwritten. Default is None.
        Returns
        -------
            yaml_dict : dict
                Dictionary compatible with Cantera's YAML format
        """
        yaml_dict = {}
        yaml_dict['species'] = [self.name_i, self.name_j]
        yaml_dict['coverage-threshold'] = self.intervals
        '''Assign slope'''
        strength_param = _Param('strength', self.slopes, '_energy')
        _assign_yaml_val(strength_param, yaml_dict, units)
        yaml_dict['id'] = self.name
        return yaml_dict
Beispiel #3
0
def write_yaml(reactor_type=None,
               mode=None,
               nodes=None,
               V=None,
               T=None,
               P=None,
               A=None,
               L=None,
               cat_abyv=None,
               flow_rate=None,
               residence_time=None,
               mass_flow_rate=None,
               end_time=None,
               transient=None,
               stepping=None,
               init_step=None,
               step_size=None,
               atol=None,
               rtol=None,
               full_SA=None,
               reactions_SA=None,
               species_SA=None,
               phases=None,
               reactor=None,
               inlet_gas=None,
               multi_T=None,
               multi_P=None,
               multi_flow_rate=None,
               output_format=None,
               solver=None,
               simulation=None,
               multi_input=None,
               misc=None,
               units=None,
               filename=None,
               yaml_options={
                   'default_flow_style': False,
                   'indent': 4
               },
               newline='\n'):
    """Writes the reactor options in a YAML file for OpenMKM. 
    
    Parameters
    ----------
        reactor_type : str
            Type of reactor. Supported options include:
            
            - pfr
            - pfr_0d
            - cstr
            - batch

            Value written to ``reactor.type``.
        mode : str
            Operation of reactor. Supported options include:

            - Isothermal
            - Adiabatic

            Value written to ``reactor.mode``.
        nodes : int
            Number of nodes to use if ``reactor_type`` is 'pfr_0d'. Value
            written to ``reactor.nodes``
        V : float or str
            Volume of reactor. Value written to ``reactor.volume``. Units of
            length^3. See Notes section regarding unit specification.
        T : float
            Temperature (in K) of reactor. Value written to 
            ``reactor.temperature``.
        P : float or str
            Pressure of reactor. Value written to ``reactor.pressure``. Units
            of pressure. See Notes section regarding unit specification.
        A : float or str
            Surface area of reactor. Value written to ``reactor.area``.
            Units of length^2. See Notes section regarding unit specification.
        L : float or str
            Length of reactor. Value written to ``reactor.length``.
            Units of length. See Notes section regarding unit specification.
        cat_abyv : float or str
            Catalyst surface area to volume ratio. Value written to
            ``reactor.cat_abyv``. Units of 1/length. See Notes section
            regarding unit specification.
        flow_rate : float or str
            Volumetric flow rate of inlet. Value written to
            ``inlet_gas.flow_rate``. Units of length^3/time.
            See Notes section regarding unit specification.
        residence_time : float or str
            Residence time of reactor. Value written to
            ``inlet_gas.residence_time``. Not required if ``flow_rate``
            or ``mass_flow_rate`` already specified. Units of time.
            See Notes section regarding unit specification.
        mass_flow_rate : float or str
            Mass flow rate of inlet. Value written to
            ``inlet_gas.mass_flow_rate``. Units of mass^3/time.
            See Notes section regarding unit specification.
        end_time : float or str
            Reactor simulation time. For continuous reactors, the system is
            assumed to reach steady state by this time. Value written to
            ``simulation.end_time``. Units of time. See Notes section regarding
            unit specification.
        transient : bool
            If True, transient operation results are saved. Otherwise,
            transient files are left blank. Value written to
            ``simulation.transient``.
        stepping : str
            Time steps taken to simulate reactor. Supported options include:

            - logarithmic
            - regular

            Value written to ``simulation.stepping``.
        init_step : float or str
            Initial step to take. Value written to ``simulation.init_step``.
        step_size : float or str
            If ``stepping`` is logarithmic, represents the ratio between the
            next step and the current step. If ``stepping`` is regular,
            represents the time between the next step and the current step in
            units of time. Value written to simulation.step_size. See Notes
            section regarding unit specification.
        atol : float
            Absolute tolerance for solver. Value written to
            ``simulation.solver.atol``.
        rtol : float
            Relative tolerance for solver. Value written to
            ``simulation.solver.rtol``.
        full_SA : bool
            If True, OpenMKM will do a full sensitivity analysis using the
            Fisher Information Matrix (FIM). Value written to
            ``simulation.sensitivity.full``.
        reactions_SA : list of str or list of :class:`~pmutt.omkm.reaction.SurfaceReaction` obj
            List of reactions to perturb using local sensitivity analysis (LSA).
            If a list of :class:`~pmutt.omkm.reaction.SurfaceReaction` is given,
            the ``id`` attribute will be used.
        species_SA : list of str or list of :class:`~pmutt.empirical.EmpiricalBase` obj
            List of species to perturb using local sensitivity analysis (LSA).
            If a list of :class:`~pmutt.empirical.EmpiricalBase` is given,
            the ``name`` attribute will be used.
        phases : list of ``Phase`` objects
            Phases present in reactor. Each phase should have the ``name``
            and ``initial_state`` attribute.
        reactor : dict
            Generic dictionary for reactor to specify values not supported by
            ``write_yaml``.
        inlet_gas : dict
            Generic dictionary for inlet_gas to specify values not supported by
            ``write_yaml``.
        multi_T : list of float
            Multiple temperatures (in K) of reactor. Value written to
            ``simulation.multi_input.temperature``.
        multi_P : list of float/str
            Multiple pressures of reactor. Value written to
            ``simulation.multi_input.pressure``. Units of pressure. See Notes
            section regarding unit specification.
        multi_flow_rate : list of float/str
            Multiple flow rates to run the model. Value written to
            ``simulation.multi_input.flow_rate``. Units of length3/time.
            See Notes section regarding unit specification.
        output_format : str
            Format for output files. Supported options include:
            
            - CSV
            - DAT

            Value written to ``simulation.output_format``.

        misc : dict
            Generic dictionary for any parameter specified at the top level.
        solver : dict
            Generic dictionary for solver to specify values not supported by
            ``write_yaml``.
        simulation : dict
            Generic dictionary for simultaion to specify values not supported by
            ``write_yaml``.            
        multi_input : dict
            Generic dictionary for multi_input to specify values not supported
            by ``write_yaml``.
        units : dict or :class:`~pmutt.omkm.units.Unit` object, optional
            Units used for file. If a dict is inputted, the key is the quantity
            and the value is the unit. If not specified, uses the default units
            of :class:`~pmutt.omkm.units.Unit`.
        filename: str, optional
            Filename for the input.cti file. If not specified, returns file
            as str.
        yaml_options : dict
            Options to pass when converting the parameters to YAML format. See
            `PyYAML documentation`_ for ``dump`` for available options.
    Returns
    -------
        lines_out : str
            If ``filename`` is None, CTI file is returned
    Notes
    -----
        **Units**
        If ``units`` is not specified, all values in file are assumed to be SI
        units. If ``units`` is specified, all values inputted are assumed to
        be in the units specified by ``units``. If a particular unit set is
        desired for a value, a str can be inputted with the form
        quantity="<<value>> <<desired units>>" where ``value`` is float-like
        and ``desired units`` is a string. For example, flow_rate="1 cm3/s".
        See https://vlachosgroup.github.io/openmkm/input for the most up-to-date
        supported values.

        **Generic Dictionaries**
        Also, values in generic dictionaries (i.e. ``reactor``, ``inlet_gas``,
        ``simulation``) will ben written preferentially over arguments passed.
        i.e. The value in ``reactor['temperature']`` will be written instead
        of ``T``.

    .. _`PyYAML Documentation`: https://pyyaml.org/wiki/PyYAMLDocumentation
    """
    lines = [
        _get_file_timestamp(comment_char='# '),
        '# See documentation for OpenMKM YAML file here:',
        '# https://vlachosgroup.github.io/openmkm/input'
    ]
    '''Initialize units'''
    if isinstance(units, dict):
        units = Units(**units)
    '''Organize reactor parameters'''
    if reactor is None:
        reactor = {}
    reactor_params = [
        _Param('type', reactor_type, None),
        _Param('mode', mode, None),
        _Param('nodes', nodes, None),
        _Param('volume', V, '_length3'),
        _Param('area', A, '_length2'),
        _Param('length', L, '_length'),
        _Param('cat_abyv', cat_abyv, '/_length')
    ]
    # Process temperature
    if T is not None:
        reactor_params.append(_Param('temperature', T, None))
    elif multi_T is not None:
        reactor_params.append(_Param('temperature', multi_T[0], None))
    # Process pressure
    if P is not None:
        reactor_params.append(_Param('pressure', P, '_pressure'))
    elif multi_P is not None:
        reactor_params.append(_Param('pressure', multi_P[0], '_pressure'))

    for parameter in reactor_params:
        _assign_yaml_val(parameter, reactor, units)

    if inlet_gas is None:
        inlet_gas = {}
    inlet_gas_params = [
        _Param('residence_time', residence_time, '_time'),
        _Param('mass_flow_rate', mass_flow_rate, '_mass/_time')
    ]
    # Process flow rate
    if flow_rate is not None:
        inlet_gas_params.append(
            _Param('flow_rate', flow_rate, '_length3/_time'))
    elif multi_flow_rate is not None:
        inlet_gas_params.append(
            _Param('flow_rate', multi_flow_rate[0], '_length3/_time'))
    '''Process inlet gas parameters'''
    for parameter in inlet_gas_params:
        _assign_yaml_val(parameter, inlet_gas, units)
    '''Organize solver parameters'''
    if solver is None:
        solver = {}
    solver_params = [_Param('atol', atol, None), _Param('rtol', rtol, None)]
    for parameter in solver_params:
        _assign_yaml_val(parameter, solver, units)
    '''Organize multi parameters'''
    if multi_input is None:
        multi_input = {}
    # Ensure multi quantities are in correct type
    if _is_iterable(multi_T):
        multi_T = list(multi_T)
    if _is_iterable(multi_P):
        multi_P = list(multi_P)
    if _is_iterable(multi_flow_rate):
        multi_flow_rate = list(multi_flow_rate)
    multi_input_params = [
        _Param('temperature', multi_T, None),
        _Param('pressure', multi_P, '_pressure'),
        _Param('flow_rate', multi_flow_rate, '_length3/_time')
    ]
    for parameter in multi_input_params:
        _assign_yaml_val(parameter, multi_input, units)
    '''Organize sensitivity parameters'''
    sensitivity = {}
    if reactions_SA is None:
        reactions_SA_names = None
    else:
        reactions_SA_names = []
        for reaction in reactions_SA:
            if isinstance(reaction, str):
                name = reaction
            else:
                try:
                    name = reaction.id
                except AttributeError:
                    err_msg = ('Unable to write reaction id to reactor YAML '
                               'file because object is invalid type ({}). '
                               'Expected str or '
                               'pmutt.omkm.reaction.SurfaceReaction type with '
                               'id attribute assigned.'
                               ''.format(type(reaction)))
                    raise TypeError(err_msg)
            reactions_SA_names.append(name)
    if species_SA is None:
        species_SA_names = None
    else:
        species_SA_names = []
        for ind_species in species_SA:
            if isinstance(ind_species, str):
                name = ind_species
            else:
                try:
                    name = ind_species.name
                except AttributeError:
                    err_msg = ('Unable to write species name to reactor YAML '
                               'file because object is invalid type ({}). '
                               'Expected str or '
                               'pmutt.empirical.EmpiricalBase type with '
                               'name attribute assigned.'
                               ''.format(type(ind_species)))
                    raise TypeError(err_msg)
            species_SA_names.append(name)
    sensitivity_params = (_Param('full', full_SA, None),
                          _Param('reactions', reactions_SA_names, None),
                          _Param('species', species_SA_names, None))
    for parameter in sensitivity_params:
        _assign_yaml_val(parameter, sensitivity, units)
    '''Organize simulation parameters'''
    if simulation is None:
        simulation = {}
    simulation_params = (_Param('end_time', end_time,
                                '_time'), _Param('transient', transient, None),
                         _Param('stepping', stepping,
                                None), _Param('step_size', step_size, None),
                         _Param('init_step', init_step, None),
                         _Param('output_format', output_format, None))
    for parameter in simulation_params:
        _assign_yaml_val(parameter, simulation, units)
    if len(solver) > 0:
        _assign_yaml_val(_Param('solver', solver, None), simulation, units)
    if len(multi_input) > 0:
        _assign_yaml_val(_Param('multi_input', multi_input, None), simulation,
                         units)
    if len(sensitivity) > 0:
        _assign_yaml_val(_Param('sensitivity', sensitivity, None), simulation,
                         units)
    '''Organize phase parameters'''
    if phases is not None:
        if isinstance(phases, dict):
            phases_dict = phases.copy()
        elif isinstance(phases, list):
            phases_dict = {}
            for phase in phases:
                phase_info = {'name': phase.name}

                # Assign intial state if available
                if phase.initial_state is not None:
                    initial_state_str = '"'
                    for species, mole_frac in phase.initial_state.items():
                        initial_state_str += '{}:{}, '.format(
                            species, mole_frac)
                    initial_state_str = '{}"'.format(initial_state_str[:-2])
                    phase_info['initial_state'] = initial_state_str

                # Assign phase type
                if isinstance(phase, IdealGas):
                    phase_type = 'gas'
                elif isinstance(phase, StoichSolid):
                    phase_type = 'bulk'
                elif isinstance(phase, InteractingInterface):
                    phase_type = 'surfaces'

                try:
                    phases_dict[phase_type].append(phase_info)
                except KeyError:
                    phases_dict[phase_type] = [phase_info]
        # If only one entry for phase type, reassign it.
        for phase_type, phases in phases_dict.items():
            if len(phases) == 1 and phase_type != 'surfaces':
                phases_dict[phase_type] = phases[0]
    '''Assign misc values'''
    if misc is None:
        misc = {}
    yaml_dict = misc.copy()
    '''Assign values to overall YAML dict'''
    headers = (('reactor', reactor), ('inlet_gas', inlet_gas),
               ('simulation', simulation), ('phases', phases_dict))
    for header in headers:
        if len(header[1]) > 0:
            yaml_dict[header[0]] = header[1]
    '''Convert dictionary to YAML str'''
    yaml_str = yaml.dump(yaml_dict, **yaml_options)
    # Remove redundant quotes
    yaml_str = yaml_str.replace('\'', '')
    lines.append(yaml_str)
    '''Write to file'''
    lines_out = '\n'.join(lines)
    if filename is not None:
        with open(filename, 'w', newline=newline) as f_ptr:
            f_ptr.write(lines_out)
    else:
        # Or return as string
        return lines_out
Beispiel #4
0
    def to_omkm_yaml(self,
                     T=300.,
                     P=1.,
                     quantity_unit='molec',
                     length_unit='cm',
                     pressure_unit='atm',
                     units=None):
        """Writes the object in Cantera's YAML format.

        Parameters
        ----------
            T : float, optional
                Temperature in K. Default is 300 K
            P : float, optional
                Pressure in atm. Default is 1 atm
            quantity_unit : str, optional
                Quantity unit to use to calculate A. Default is 'molec'
            length_unit : str, optional
                Length unit to use to calculate A. Default is 'cm'
            units : :class:`~pmutt.omkm.units.Units` object
                If specified, `quantity_unit` and `length_unit` are overwritten.
                Default is None.
        Returns
        -------
            yaml_dict
                Dictionary compatible with Cantera's YAML format
        """
        if units is not None:
            quantity_unit = units.quantity
            length_unit = units.length
            pressure_unit = units.pressure

        species_names = [species.name for species in self.species]
        yaml_dict = {
            'name': self.name,
            'elements': list(self.elements),
            'species': species_names,
            'kinetics': 'surface',
        }
        '''Assign site density'''
        # Convert to appropriate unit
        area_unit = '{}2'.format(length_unit)
        site_den = self.site_density\
                   *c.convert_unit(initial='mol', final=quantity_unit)\
                   /c.convert_unit(initial='cm2', final=area_unit)
        site_den_param = _Param('site-density', site_den,
                                '_quantity/_length^2')
        _assign_yaml_val(site_den_param, yaml_dict, units)
        '''Assign thermo depending on presence of lateral interactions'''
        if self.interactions is None or len(self.interactions) == 0:
            yaml_dict['thermo'] = 'ideal-surface'
            yaml_dict['interactions'] = 'none'
        else:
            yaml_dict['thermo'] = 'surface-lateral-interaction'
            yaml_dict['interactions'] = 'declared-species'
        '''Assign reactions'''
        if self.reactions is None or len(self.reactions) == 0:
            yaml_dict['reactions'] = 'none'
        else:
            yaml_dict['reactions'] = 'declared-species'
        '''Assign BEPs'''
        if self.beps is None or len(self.beps) == 0:
            yaml_dict['beps'] = 'none'
        else:
            yaml_dict['beps'] = 'all'

        return yaml_dict
Beispiel #5
0
    def to_omkm_yaml(self,
                     T=c.T0('K'),
                     P=c.P0('bar'),
                     quantity_unit='molec',
                     length_unit='cm',
                     act_energy_unit='cal/mol',
                     ads_act_method='get_H_act',
                     units=None):
        """Writes the object in Cantera's YAML format.

        Parameters
        ----------
            T : float, optional
                Temperature in K. Default is 298.15 K
            P : float, optional
                Pressure in bar. Default is 1 bar
            quantity_unit : str, optional
                Quantity unit to calculate A. Default is 'molec'
            length_unit : str, optional
                Length unit to calculate A. Default is 'cm'
            act_energy_unit : str, optional
                Unit to use for activation energy. Default is 'cal/mol'
            ads_act_method : str, optional
                Activation method to use for adsorption reactions. Accepted 
                options include 'get_H_act' and 'get_G_act'. Default is
                'get_H_act'.
            units : :class:`~pmutt.omkm.units.Units` object
                If specified, `quantity_unit`, `length_unit`, and
                `act_energy_unit` are overwritten. Default is None.
        Returns
        -------
            yaml_dict : dict
                Dictionary compatible with Cantera's YAML format
        """
        if units is not None:
            quantity_unit = units.quantity
            length_unit = units.length
            act_energy_unit = units.act_energy

        yaml_dict = {}
        # Assign reaction name
        yaml_dict['equation'] = self.to_string(stoich_space=True,
                                               species_delimiter=' + ',
                                               reaction_delimiter=' <=> ',\
                                               include_TS=False)
        
        if self.is_adsorption:
            rate_constant_name = 'sticking-coefficient'
            # Pre-exponential
            A_param = _Param('A', self.sticking_coeff, None)

            # Activation energy
            act_method = getattr(self, ads_act_method)
            act_val = act_method(units=act_energy_unit, T=T, P=P)

            # Sticking-species
            for species in self.reactants:
                # Look for gas phase species
                if isinstance(species.phase, IdealGas) \
                   or species.phase.lower() == 'g' \
                   or species.phase.lower() == 'gas':
                    yaml_dict['sticking-species'] = species.name
                    break
            else:
                err_msg = ('Could not find gas reactant in reaction, {}. '
                           'One species must have its phase attribute set to '
                           '"g" or "gas".'
                           ''.format(str(self)))
                raise ValueError(err_msg)
            # Motz-Wise correction
            yaml_dict['Motz-Wise'] = self.use_motz_wise
        else:
            rate_constant_name = 'rate-constant'
            # Pre-exponential
            A_units = '{}/{}2'.format(quantity_unit, length_unit)
            A = float(self.get_A(T=T, P=P, include_entropy=False, units=A_units))
            # TODO Check units for A. Should have time dependence.
            A_param = _Param('A', A, None)
            # Activation energy
            act_val = self.get_G_act(units=act_energy_unit, T=T, P=P)

        # Assign activation energy, beta and pre-exponential factor
        rate_constant_dict = {}
        _assign_yaml_val(A_param, rate_constant_dict, units)
        rate_constant_dict['b'] = self.beta
        act_param = _Param('Ea', act_val, '_act_energy')
        _assign_yaml_val(act_param, rate_constant_dict, units)
        # Add kinetic parameters to overall dictionary
        yaml_dict[rate_constant_name] = rate_constant_dict

        # Assign reaction ID
        try:
            yaml_dict['id'] = self.id
        except AttributeError:
            pass

        return yaml_dict