Example #1
0
    def test_force_pass_arguments(self):
        self.assertEqual(
            pmutt._force_pass_arguments(self.sum_fn, **{
                'num1': 1,
                'num2': 2
            }), 3)

        self.assertEqual(
            pmutt._force_pass_arguments(self.kwargs_fn, **{'num1': 1}),
            {'num1': 1})

        self.assertEqual(
            pmutt._force_pass_arguments(self.sum_class, **{
                'num1': 1,
                'num2': 2
            }), self.sum_class(num1=1, num2=2))

        self.assertEqual(
            pmutt._force_pass_arguments(self.sum_class, **{
                'num1': 1,
                'num2': 2,
                'num3': 3
            }), self.sum_class(num1=1, num2=2))

        self.assertEqual(
            pmutt._force_pass_arguments(self.kwargs_class, **{
                'num1': 1,
                'num2': 2,
                'num3': 3
            }), self.kwargs_class(num1=1, num2=2, num3=3))
Example #2
0
def get_state_quantity(species, stoich, method_name, **kwargs):
    if method_name == 'get_q':
        state_quantity = 1.
    else:
        state_quantity = 0.

    for specie, coeff in zip(species, stoich):
        # Process the inputs and methods for each specie
        specie_kwargs = _get_specie_kwargs(specie.name, **kwargs)

        method = getattr(specie, method_name)
        if method_name == 'get_q':
            state_quantity *= \
                    _force_pass_arguments(method, **specie_kwargs)**coeff
        else:
            state_quantity += \
                    _force_pass_arguments(method, **specie_kwargs)*coeff
    return state_quantity
Example #3
0
def write_thermo_yaml(phases=None,
                      species=None,
                      reactions=None,
                      lateral_interactions=None,
                      units=None,
                      filename=None,
                      T=300.,
                      P=1.,
                      newline='\n',
                      ads_act_method='get_H_act',
                      yaml_options={
                          'default_flow_style': None,
                          'indent': 2,
                          'sort_keys': False,
                          'width': 79
                      }):
    """Writes the units, phases, species, lateral interactions, reactions and 
    additional options in the CTI format for OpenMKM
    
    Parameters
    ----------
        phases : list of :class:`~pmutt.omkm.phase.Phase` objects
            Phases to write in YAML file. The species should already be assigned.
        species : list of :class:`~pmutt.empirical.nasa.Nasa`, :class:`~pmutt.empirical.nasa.Nasa9` or :class:`~pmutt.empirical.shomate.Shomate`
            Species to write in YAML file.
        reactions : list of :class:`~pmutt.omkm.reaction.SurfaceReaction`
            Reactions to write in YAML file.
        lateral_interactions : list of :class:`~pmutt.mixture.cov.PiecewiseCovEffect` objects, optional
            Lateral interactions to include in YAML file. Default is None.
        units : dict or :class:`~pmutt.omkm.units.Unit` object, optional
            Units to write 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.yaml file. If not specified, returns file
            as str.
        T : float, optional
            Temperature in K. Default is 300 K.
        P : float, optional
            Pressure in atm. Default is 1 atm.
        newline : str, optional
            Type of newline to use. Default is Linux newline ('\\n')
        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'.
    Returns
    -------
        lines_out : str
            If ``filename`` is None, CTI file is returned.
    """
    lines = [
        _get_file_timestamp(comment_char='# '),
        '# See documentation for OpenMKM YAML file here:',
        '# https://vlachosgroup.github.io/openmkm/input',
    ]
    yaml_dict = {}
    '''Organize units units'''
    if units is None:
        units = Units()
    elif isinstance(units, dict):
        units = Units(**units)
    units_out = units.to_omkm_yaml()
    '''Pre-assign IDs for lateral interactions so phases can be written'''
    if lateral_interactions is not None:
        interactions_out = []
        i = 0
        for lat_interaction in lateral_interactions:
            if lat_interaction.name is None:
                lat_interaction.name = 'i_{:04d}'.format(i)
                i += 1
            interaction_dict = lat_interaction.to_omkm_yaml(units=units)
            interactions_out.append(interaction_dict)
    '''Pre-assign IDs for reactions so phases can be written'''
    beps = []
    if reactions is not None:
        reactions_out = []
        i = 0
        for reaction in reactions:
            # Assign reaction ID if not present
            if reaction.id is None:
                reaction.id = 'r_{:04d}'.format(i)
                i += 1
            # Write reaction
            reaction_dict = reaction.to_omkm_yaml(units=units, T=T)
            reactions_out.append(reaction_dict)

            # Add unique BEP relationship if any
            try:
                bep = reaction.bep
            except AttributeError:
                pass
            else:
                if bep is not None and bep not in beps:
                    beps.append(bep)
    '''Write phases'''
    if phases is not None:
        phases_out = []
        for phase in phases:
            phase_dict = _force_pass_arguments(phase.to_omkm_yaml, units=units)
            phases_out.append(phase_dict)
        # yaml_dict['phases'] = phases_out
    '''Write species'''
    if species is not None:
        species_out = []
        for ind_species in species:
            ind_species_dict = _force_pass_arguments(ind_species.to_omkm_yaml,
                                                     units=units)
            species_out.append(ind_species_dict)
        # yaml_dict['species'] = species_out
    '''Organize BEPs'''
    if len(beps) > 0:
        beps_out = []
        i = 0
        for bep in beps:
            # Assign name if necessary
            if bep.name is None:
                bep.name = 'b_{:04d}'.format(i)
                i += 1
            bep_dict = _force_pass_arguments(bep.to_omkm_yaml, units=units)
            beps_out.append(bep_dict)
        # yaml_dict['beps'] = beps_out
    '''Organize fields'''
    fields = (
        'units',
        'phases',
        'species',
        'reactions',
        'beps',
        'interactions',
    )
    for field in fields:
        try:
            val = locals()['{}_out'.format(field)]
        except:
            pass
        else:
            # Create a YAML string
            yaml_str = yaml.dump(data={field: val},
                                 stream=None,
                                 **yaml_options)
            lines.extend([
                '', '#' + '-' * 79, '# {}'.format(field.upper()),
                '#' + '-' * 79, yaml_str
            ])

            # yaml_dict[field] = val

    # Convert to YAML format
    # yaml_str = yaml.dump(data=yaml_dict, stream=None, **yaml_options)
    # Remove redundant quotes
    # yaml_str = yaml_str.replace('\'', '')
    # lines.append(yaml_str)

    lines_out = '\n'.join(lines)
    # Remove redundant strings
    lines_out = lines_out.replace('\'', '')
    # Add spacing between list elements
    lines_out = lines_out.replace('\n-', '\n\n-')
    if filename is not None:
        filename = Path(filename)
        with open(filename, 'w', newline=newline) as f_ptr:
            f_ptr.write(lines_out)
    else:
        # Or return as string
        return lines_out
Example #4
0
def write_cti(phases=None,
              species=None,
              reactions=None,
              lateral_interactions=None,
              units=None,
              filename=None,
              T=300.,
              P=1.,
              newline='\n',
              use_motz_wise=False,
              ads_act_method='get_H_act',
              write_xml=True):
    """Writes the units, phases, species, lateral interactions, reactions and 
    additional options in the CTI format for OpenMKM
    
    Parameters
    ----------
        phases : list of :class:`~pmutt.omkm.phase.Phase` objects
            Phases to write in CTI file. The species should already be assigned.
        species : list of :class:`~pmutt.empirical.nasa.Nasa`, :class:`~pmutt.empirical.nasa.Nasa9` or :class:`~pmutt.empirical.shomate.Shomate`
            Species to write in CTI file.
        reactions : list of :class:`~pmutt.omkm.reaction.SurfaceReaction`
            Reactions to write in CTI file.
        lateral_interactions : list of :class:`~pmutt.mixture.cov.PiecewiseCovEffect` objects, optional
            Lateral interactions to include in CTI file. Default is None.
        units : dict or :class:`~pmutt.omkm.units.Unit` object, optional
            Units to write 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.
        T : float, optional
            Temperature in K. Default is 300 K.
        P : float, optional
            Pressure in atm. Default is 1 atm.
        newline : str, optional
            Type of newline to use. Default is Linux newline ('\\n')
        use_motz_wise : bool, optional
            Whether to use Motz-wise sticking coefficients or not. Default is
            False
        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'.
        write_xml : bool, optional
            If True and ``filename`` is not ``None``, automatically generates
            an XML file with the CTI file.
    Returns
    -------
        lines_out : str
            If ``filename`` is None, CTI file is returned.
    """
    lines = [
        _get_file_timestamp(comment_char='# '),
        '# See documentation for OpenMKM CTI file here:',
        '# https://vlachosgroup.github.io/openmkm/input'
    ]
    '''Write units'''
    lines.extend(['', '#' + '-' * 79, '# UNITS', '#' + '-' * 79])
    if units is None:
        units = Units()
    elif isinstance(units, dict):
        units = Units(**units)
    lines.append(units.to_cti())
    '''Pre-assign IDs for lateral interactions so phases can be written'''
    if lateral_interactions is not None:
        lat_inter_lines = []
        i = 0
        if lateral_interactions is not None:
            for lat_interaction in lateral_interactions:
                if lat_interaction.name is None:
                    lat_interaction.name = '{:04d}'.format(i)
                    i += 1

                lat_inter_CTI = _force_pass_arguments(lat_interaction.to_cti,
                                                      units=units)
                lat_inter_lines.append(lat_inter_CTI)
    '''Pre-assign IDs for reactions so phases can be written'''
    if reactions is not None:
        beps = []
        reaction_lines = []
        i = 0
        for reaction in reactions:
            # Assign reaction ID if not present
            if reaction.id is None:
                reaction.id = '{:04d}'.format(i)
                i += 1
            # Write reaction
            reaction_CTI = _force_pass_arguments(reaction.to_cti,
                                                 units=units,
                                                 T=T)
            reaction_lines.append(reaction_CTI)

            # Add unique BEP relationship if any
            try:
                bep = reaction.bep
            except AttributeError:
                pass
            else:
                if bep is not None and bep not in beps:
                    beps.append(bep)
    '''Write phases'''
    if phases is not None:
        lines.extend(['', '#' + '-' * 79, '# PHASES', '#' + '-' * 79])
        for phase in phases:
            phase_CTI = _force_pass_arguments(phase.to_cti, units=units)
            lines.append(phase_CTI)
    '''Write species'''
    if species is not None:
        lines.extend(['', '#' + '-' * 79, '# SPECIES', '#' + '-' * 79])
        for ind_species in species:
            ind_species_CTI = _force_pass_arguments(ind_species.to_cti,
                                                    units=units)
            lines.append(ind_species_CTI)
    '''Write lateral interactions'''
    if lateral_interactions is not None:
        lines.extend(
            ['', '#' + '-' * 79, '# LATERAL INTERACTIONS', '#' + '-' * 79])
        lines.extend(lat_inter_lines)

    if reactions is not None:
        '''Write reaction options'''
        lines.extend(
            ['', '#' + '-' * 79, '# REACTION OPTIONS', '#' + '-' * 79])
        if use_motz_wise:
            lines.extend(['enable_motz_wise()\n'])
        else:
            lines.extend(['disable_motz_wise()\n'])
        '''Write reactions'''
        lines.extend(['', '#' + '-' * 79, '# REACTIONS', '#' + '-' * 79])
        lines.extend(reaction_lines)
        '''Write BEP Relationships'''
        if len(beps) > 0:
            lines.extend(
                ['', '#' + '-' * 79, '# BEP Relationships', '#' + '-' * 79])
            # Only write each BEP once
            i = 0
            for bep in beps:
                bep_CTI = _force_pass_arguments(bep.to_cti, units=units)
                # Increment counter if necessary
                if bep.name is None:
                    i += 1
                lines.append(bep_CTI)
    '''Write to file'''
    lines_out = '\n'.join(lines)
    if filename is not None:
        filename = Path(filename)
        with open(filename, 'w', newline=newline) as f_ptr:
            f_ptr.write(lines_out)
        '''Write XML file'''
        if write_xml:
            xml_filename = filename.with_suffix('.xml').as_posix()
            convert(filename=filename, outName=xml_filename)
    else:
        # Or return as string
        return lines_out
Example #5
0
def _write_reaction_lines(reactions, species_delimiter, reaction_delimiter,
                          include_TS, stoich_format, act_method_name, act_unit,
                          float_format, column_delimiter, sden_operation,
                          **kwargs):
    """Write the reaction lines in the Chemkin format

    Parameters
    ----------
        reactions : list of :class:`~pmutt.reaction.ChemkinReaction` objects
            Chemkin reactions to write in surf.inp file
        species_delimiter : str
            Delimiter to separate species when writing reactions
        reaction_delimiter : str
            Delimiter to separate reaction sides
        act_method_name : str
            Name of method to use to calculate activation function
        act_unit : str
            Units to calculate activation energy
        float_format : str
            String format to print floating numbers
        stoich_format : str
            String format to print stoichiometric coefficients
        column_delimiter : str
            Delimiter to separate columns
        kwargs : keyword arguments
            Parameters needed to calculate activation energy and preexponential
            factor
    Returns
    -------
        reaction_lines : str
            Reactions represented in Chemkin format
    """
    max_reaction_len = _get_max_reaction_len(
        reactions=reactions,
        species_delimiter=species_delimiter,
        reaction_delimiter=reaction_delimiter,
        stoich_format=stoich_format,
        include_TS=include_TS)
    float_field = '{:%s}' % float_format

    reaction_lines = []
    for reaction in reactions:
        # Get reaction string
        reaction_str = reaction.to_string(
            species_delimiter=species_delimiter,
            reaction_delimiter=reaction_delimiter,
            stoich_format=stoich_format,
            include_TS=include_TS).ljust(max_reaction_len)
        # Calculate preexponential factor
        if reaction.is_adsorption:
            A = reaction.sticking_coeff
        else:
            # If using delta_G, take out entropic contribution in A
            if act_method_name in ('get_GoRT_act', 'get_G_act',
                                   'get_delta_GoRT', 'get_delta_G'):
                include_entropy = False
            else:
                include_entropy = True
            A = reaction.get_A(include_entropy=include_entropy,
                               sden_operation=sden_operation,
                               **kwargs)
        A_str = float_field.format(A)

        # Format beta value
        beta_str = float_field.format(reaction.beta)

        # Calculate activation energy
        if act_method_name != 'get_EoRT_act' and \
           act_method_name != 'get_E_act':
            kwargs['activation'] = True
        kwargs['units'] = act_unit
        Ea_method = getattr(reaction, act_method_name)
        try:
            Ea = _force_pass_arguments(Ea_method, **kwargs)
        except AttributeError:
            Ea = 0.
        Ea_str = float_field.format(Ea)

        reaction_line = '{0}{4}{1}{4}{2}{4}{3}'.format(reaction_str, A_str,
                                                       beta_str, Ea_str,
                                                       column_delimiter)
        if reaction.is_adsorption:
            reaction_line = '{}\nSTICK'.format(reaction_line)
        reaction_lines.append(reaction_line)
    return reaction_lines
Example #6
0
def write_EA(reactions,
             conditions,
             write_gas_phase=False,
             filename='EAs.inp',
             act_method_name='get_EoRT_act',
             float_format=' .2E',
             species_delimiter='+',
             reaction_delimiter='<=>',
             stoich_format='.0f',
             newline='\n',
             column_delimiter='  '):
    """Writes the EAs.inp or EAg.inp file for Chemkin

    Parameters
    ----------
        reactions : list of :class:`~pmutt.reaction.ChemkinReaction` objects
            Reactions to write
        conditions : list of dicts
            Conditions to evaluate each reaction. The key of the dictionaries
            should be a relevant quantity to evaluate the reaction (e.g. T, P)
        write_gas_phase : bool, optional
            If True, only gas phase reactions are written (including
            adsorption). If False, only surface phase reactions are written.
            Default is False
        filename : str, optional
            Filename for the EAs.inp file. Default is 'EAs.inp'
        method_name : str, optional
            Method to use to calculate values. Typical values are:

            - 'get_EoRT_act' (default)
            - 'get_HoRT_act'
            - 'get_GoRT_act'

        float_format : float, optional
            Format to write numbers. Default is ' .2E' (scientific notation
            rounded to 2 decimal places with a preceding space if the value is
            positive)
        stoich_format : float, optional
            Format to write stoichiometric numbers. Default is '.0f' (integer)
        newline : str, optional
            Newline character to use. Default is the Linux newline character
        column_delimiter : str, optional
            Delimiter for columns. Default is '  '
    """
    valid_reactions = []
    for reaction in reactions:
        # Skip gas-phase reactions if we want surface phase
        if not write_gas_phase and reaction.gas_phase:
            continue
        # Skip surface-phase reactions if we want gas phase
        if write_gas_phase and not reaction.gas_phase:
            continue
        valid_reactions.append(reaction)

    n_reactions = len(valid_reactions)
    # Add initial comment help line and number of reactions
    lines = [
        _get_filestamp(),
        ('!The first line is the number of reactions. Subsequent lines follow '
         'the format'),
        ('!of rxn (from surf.out) followed by the EA/RT value at each run '
         'condition.'),
        ('!There may be one slight deviation from surf.out: any repeated '
         'species should'),
        ('!be included in the reaction string with a stoichiometric '
         'coefficient equal to'),
        ('!the number of times the species appears in the reaction. '
         'If not using'), '!MultiInput, then only the first value is used.',
        '  {}  !Number of reactions'.format(n_reactions)
    ]

    # Find length to pad reactions
    max_rxn_len = _get_max_reaction_len(reactions=valid_reactions,
                                        species_delimiter=species_delimiter,
                                        reaction_delimiter=reaction_delimiter,
                                        stoich_format=stoich_format,
                                        include_TS=False)
    rxn_padding = max_rxn_len + len(column_delimiter)

    # Define string formats to use
    float_field = '{:%s}' % float_format
    str_field = '{:%d}' % rxn_padding

    column_line = _write_column_line(padding=rxn_padding,
                                     column_delimiter=column_delimiter,
                                     float_format=float_format,
                                     n_conditions=len(conditions))
    lines.append(column_line)

    # Add line for each reaction step
    for reaction in valid_reactions:
        line = [
            str_field.format(
                reaction.to_string(species_delimiter=species_delimiter,
                                   reaction_delimiter=reaction_delimiter,
                                   stoich_format=stoich_format,
                                   include_TS=False))
        ]
        for condition in conditions:
            method = getattr(reaction, act_method_name)
            quantity = _force_pass_arguments(method, **condition)
            line.append(float_field.format(quantity))
        lines.append(column_delimiter.join(line))
    lines.append('EOF')
    with open(filename, 'w', newline=newline) as f_ptr:
        f_ptr.write('\n'.join(lines))