Esempio n. 1
0
def write_T_flow(T=None,
                 P=None,
                 Q=None,
                 abyv=None,
                 conditions=None,
                 filename=None,
                 float_format='.3E',
                 newline='\n',
                 column_delimiter='  '):
    """Writes the T_flow.inp Chemkin file

    Parameters
    ----------
        T : list of float
            Temperatures in K
        P : list of float
            Pressures in atm
        Q : list of float
            Volumetric flow rate in cm3/s
        abyv : list of float
            Catalyst area to reactor volume ratio in 1/cm
        filename : str, optional
            Name of the file. If not specified, returns file as str
        float_format : str, optional
            Format to write floating point numbers. Default is '.3E' (i.e.
            scientific notation rounded to 3 decimal places)
        newline : str, optional
            Newline character. Default is the Linux newline character
        column_delimiter : str, optional
            Delimiter for columns. Default is '  '
    Returns
    -------
        lines_out : str
            T_flow lines as a string if ``filename`` is None
    """
    line_field = '{:%s}{}{:%s}{}{:%s}{}{:%s}  !{:<3}' % (
        float_format, float_format, float_format, float_format)
    lines = [
        _get_file_timestamp(comment_char='! '),
        '!Conditions for each reaction run',
        '!Only used when MultiInput in tube.inp is set to "T"',
        '!T[K]    {0}P[atm]   {0}Q[cm3/s] {0}abyv[cm-1]{0}Run #'.format(
            column_delimiter)
    ]
    for i, (T_i, P_i, Q_i, abyv_i) in enumerate(zip(T, P, Q, abyv)):
        lines.append(
            line_field.format(T_i, column_delimiter, P_i, column_delimiter,
                              Q_i, column_delimiter, abyv_i, i + 1))
    lines.append('EOF')

    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:
        return lines_out
Esempio n. 2
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
Esempio n. 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
Esempio n. 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
Esempio n. 5
0
def write_tube_mole(mole_frac_conditions,
                    nasa_species,
                    filename=None,
                    float_format=' .3f',
                    newline='\n',
                    column_delimiter='  '):
    """Write tube_mole.inp Chemkin file

    Parameters
    ----------
    mole_frac_conditions : list of dict
        Each dictionary should have the keys as the name of the species and the
        value as the initial mole fraction
    nasa_species : list of :class:`~pmutt.empirical.nasa.Nasa` objects
        Nasa species to find phase information
    filename : str, optional
        Name of the file. If not specified, returns file as str
    float_format : str, optional
        Format to write floating point numbers. Default is '.3E' (i.e.
        scientific notation rounded to 3 decimal places)
    newline : str, optional
        Newline character. Default is the Linux newline character
    column_delimiter : str, optional
        Delimiter for columns. Default is '  '
    Returns
    -------
        lines_out : str
            tube_mole lines as a string if ``filename`` is None
    """
    # Prepare the float field for printing mole fractions
    float_field = '{:%s}' % float_format

    # Get relevant species
    unique_specie_names = set()
    for mole_fracs in mole_frac_conditions:
        for specie_name in mole_fracs.keys():
            unique_specie_names.add(specie_name)
    # Convert nasa_species to a dict for quicker lookup
    unique_species = [
        specie for specie in nasa_species if specie.name in unique_specie_names
    ]

    # Find length to pad species
    max_species_len = _get_max_species_len(species=unique_species,
                                           ignore_gas_phase=False,
                                           include_phase=True)
    species_padding = max_species_len + len(column_delimiter)

    column_line = _write_column_line(padding=species_padding,
                                     column_delimiter=column_delimiter,
                                     float_format=float_format,
                                     n_conditions=len(mole_frac_conditions))
    species_lines = []
    for specie in unique_species:
        specie_line = _get_specie_str(
            specie=specie, include_phase=True).ljust(species_padding)
        for condition in mole_frac_conditions:
            # If the mole fraction was not specified, assumed to be 0
            try:
                float_str = float_field.format(condition[specie.name])
            except KeyError:
                float_str = float_field.format(0.)
            specie_line = '{}{}{}'.format(specie_line, column_delimiter,
                                          float_str)
        species_lines.append(specie_line)

    lines = [
        _get_file_timestamp(comment_char='! '),
        "!Specify the 'species/phase/' pair /(in quotes!)/ and the associated",
        ("!composition values. If the composition does not sum to 1 for each "
         "phase or"),
        ("!site type, it will be renormalized to 1. At the end of a "
         "calculation, a"),
        ("!file containing the complete composition and mass flux "
         "(the last entry) will"),
        ("!be generated. This file's format is completely compatible with the "
         "current"),
        "!input file and can be used to restart that calculation.",
        ("0       itube_restart -- will be >0 if a restart file is used or 0 "
         "for the first run"),
        '{:<3}    Number of nonzero species'.format(len(unique_species)),
        column_line,
    ]
    lines.extend(species_lines)
    lines.append('EOF')

    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:
        return lines_out
Esempio n. 6
0
def write_surf(reactions,
               sden_operation='min',
               filename=None,
               T=c.T0('K'),
               species_delimiter='+',
               reaction_delimiter='=',
               act_method_name='get_E_act',
               act_unit='kcal/mol',
               float_format=' .3E',
               stoich_format='.0f',
               newline='\n',
               column_delimiter='  ',
               use_mw_correction=True,
               **kwargs):
    """Writes the surf.inp Chemkin file

    Parameters
    ----------
        reactions : list of :class:`~pmutt.reaction.ChemkinReaction` objects
            Chemkin reactions to write in surf.inp file. Purely gas-phase
            reactions will be ignored
        filename : str, optional
            Filename for surf.inp file. If not specified, returns file as str
        T : float, optional
            Temperature to calculate activation energy. Default is 298.15 K
        species_delimiter : str, optional
            Delimiter to separate species when writing reactions.
            Default is '+'
        reaction_delimiter : str, optional
            Delimiter to separate reaction sides. Default is '='
        act_method_name : str, optional
            Name of method to use to calculate activation function
        act_unit : str, optional
            Units to calculate activation energy. Default is 'kcal/mol'
        float_format : str, optional
            String format to print floating numbers. Default is ' .3E' (i.e.
            scientific notation rounded to 3 decimal places with a leading
            space for positive numbers)
        stoich_format : str, optional
            String format to print stoichiometric coefficients.
            Default is '.0f' (i.e. integers)
        newline : str, optional
            Newline character. Default is the Linux newline character
        column_delimiter : str, optional
            Delimiter to separate columns. Default is '  '
        use_mw_correction : bool, optional
            If True, uses the Motz-Wise corrections. Default is True
        kwargs : keyword arguments
            Parameters needed to calculate activation energy and preexponential
            factor
    Returns
    -------
        lines_out : str
            surf.inp lines as a string if ``filename`` is None
    """
    # Organize species by their catalyst sites
    cat_adsorbates = {}
    unique_cat_sites = []
    nasa_species = reactions.get_species(include_TS=False)
    for specie in nasa_species.values():
        # Skip gas phase species
        if specie.phase.upper() == 'G':
            continue
        # Skip the bulk species
        if specie.cat_site.bulk_specie == specie.name:
            continue

        cat_name = specie.cat_site.name
        try:
            cat_adsorbates[cat_name].append(specie)
        except KeyError:
            cat_adsorbates[cat_name] = [specie]
            unique_cat_sites.append(specie.cat_site)

    cat_site_lines = []
    for cat_site in unique_cat_sites:
        # Add catalyst site header
        cat_site_name = '{}/'.format(cat_site.name)
        cat_site_lines.append('SITE/{:<14}SDEN/{:.5E}/'.format(
            cat_site_name, cat_site.site_density))
        cat_site_lines.append('')
        # Add species adsorbed on that site
        for specie in cat_adsorbates[cat_site.name]:
            cat_site_lines.append('{}{}/{}/'.format(column_delimiter,
                                                    specie.name,
                                                    specie.n_sites))
        cat_site_lines.append('')
    # Write bulk species
    for cat_site in unique_cat_sites:
        # Add bulk line
        cat_site_lines.append('BULK {}/{:.1f}/'.format(cat_site.bulk_specie,
                                                       cat_site.density))

    # Get surface reaction lines
    surf_reactions = \
        [reaction for reaction in reactions if not reaction.gas_phase]
    reaction_lines = _write_reaction_lines(
        reactions=surf_reactions,
        species_delimiter=species_delimiter,
        reaction_delimiter=reaction_delimiter,
        include_TS=False,
        stoich_format=stoich_format,
        act_method_name=act_method_name,
        act_unit=act_unit,
        float_format=float_format,
        column_delimiter=column_delimiter,
        T=T,
        sden_operation=sden_operation,
        **kwargs)

    # Check length of lines
    if any([(len(reaction_line) > 80) for reaction_line in reaction_lines]):
        warn_msg = ('Reaction lines exceed 80 character limit when writing '
                    'surf.inp. This may cause errors when Chemkin reads this '
                    'file. Consider passing a different float_format, '
                    'column_delimiter, or expressing the reactions more '
                    'succiently to pmutt.io.chemkin.write_surf.')
        warn(warn_msg, UserWarning)

    # Preparing reaction line header
    mw_field = '{:<5}'
    if use_mw_correction:
        mw_str = mw_field.format('MWON')
    else:
        mw_str = mw_field.format('MWOFF')

    if 'oRT' in act_method_name:
        act_unit_str = ''
    else:
        act_unit_str = act_unit.upper()

    lines = [
        _get_file_timestamp(comment_char='! '),
        '!Surface species',
        '!Each catalyst site has the following format:',
        '!SITE/[Site name]/      SDEN/[Site density in mol/cm2]/',
        '![Adsorbate Name]/[# of Sites occupied]/ (for every adsorbate)',
        '!BULK [Bulk name]/[Bulk density in g/cm3]',
    ]
    lines.extend(cat_site_lines)
    lines.extend([
        'END', '', '!Surface-phase reactions.',
        '!The reaction line has the following format:',
        '!REACTIONS  MW[ON/OFF]   [Ea units]',
        '!where MW stands for Motz-Wise corrections and if the Ea',
        ('!units are left blank, then the activation energy should '
         'be in cal/mol'), '!The rate constant expression is:',
        '!k = kb/h/site_den^(n-1) * (T)^beta * exp(-Ea/RT)',
        ('!where site_den is the site density and is the number '
         'of surface species (including empty sites)'),
        '!Each line has 4 columns:',
        '!- Reaction reactants and products separated by =',
        '!- Preexponential factor, kb/h/site_den^(n-1), or ',
        '!  sticking coefficient if adsorption reaction',
        '!- Beta (power to raise T in rate constant expression)',
        ('!- Ea (Activation Energy or Gibbs energy of activation in '
         'specified units'),
        ('!Adsorption reactions can be represented using the STICK '
         'keyword'), 'REACTIONS{2}{0}{2}{1}'.format(mw_str, act_unit_str,
                                                    column_delimiter)
    ])
    lines.extend(reaction_lines)
    lines.append('END')

    lines_out = '\n'.join(lines)
    if filename is not None:
        # Write the file
        with open(filename, 'w', newline=newline) as f_ptr:
            f_ptr.write(lines_out)
    else:
        return lines_out
Esempio n. 7
0
def write_gas(nasa_species,
              filename=None,
              T=c.T0('K'),
              reactions=[],
              species_delimiter='+',
              reaction_delimiter='=',
              act_method_name='get_E_act',
              act_unit='kcal/mol',
              float_format=' .3E',
              stoich_format='.0f',
              newline='\n',
              column_delimiter='  ',
              **kwargs):
    """Writes the gas.inp Chemkin file.

    Parameters
    ----------
        nasa_species : list of :class:`~pmutt.empirical.nasa.Nasa` objects
            Surface and gas species used in Chemkin mechanism. Used to write
            elements section
        filename : str, optional
            File name for gas.inp file. If not specified, returns gas.inp as
            string
        reactions : :class:`~pmutt.reaction.Reactions` object, optional
            Reactions in mechanism. Reactions with only gas-phase species will
            be written to this file
    Returns
    -------
        lines_out : str
            gas.inp lines as a string if ``filename`` is None
    """
    # Get unique elements and gas-phase species
    unique_elements = set()
    gas_species = []
    for specie in nasa_species:
        for element in specie.elements.keys():
            unique_elements.add(element)
        if specie.phase.upper() == 'G':
            gas_species.append(specie.name)
    unique_elements = list(unique_elements)

    # Get gas-phase reactions
    gas_reactions = [reaction for reaction in reactions if reaction.gas_phase]
    reaction_lines = _write_reaction_lines(
        reactions=gas_reactions,
        species_delimiter=species_delimiter,
        reaction_delimiter=reaction_delimiter,
        include_TS=False,
        stoich_format=stoich_format,
        act_method_name=act_method_name,
        act_unit=act_unit,
        float_format=float_format,
        column_delimiter=column_delimiter,
        T=T,
        sden_operation=None,
        **kwargs)
    # Check length of lines
    if any([(len(reaction_line) > 80) for reaction_line in reaction_lines]):
        warn_msg = ('Reaction lines exceed 80 character limit when writing '
                    'gas.inp. This may cause errors when Chemkin reads this '
                    'file. Consider passing a different float_format, '
                    'column_delimiter, or expressing the reactions more '
                    'succiently to pmutt.io.chemkin.write_gas.')
        warn(warn_msg, UserWarning)

    # Collect all the lines into a list
    lines = [
        _get_file_timestamp(comment_char='! '),
        '!Elements present in gas and surface species', 'ELEMENTS'
    ]
    lines.extend(unique_elements)
    lines.extend(['END', '', '!Gas-phase species', 'SPECIES'])
    lines.extend(gas_species)
    lines.extend([
        'END', '', '!Gas-phase reactions. The rate constant expression is:',
        '!k = kb/h * (T)^beta * exp(-Ea/RT)', '!Each line has 4 columns:',
        '!- Reaction reactants and products separated by <=>',
        '!- Preexponential factor, kb/h',
        '!- Beta (power to raise T in rate constant expression)',
        ('!- Ea (Activation Energy or Gibbs energy of activation in '
         'kcal/mol'), 'REACTIONS'
    ])
    lines.extend(reaction_lines)
    lines.append('END')

    lines_out = '\n'.join(lines)
    if filename is not None:
        with open(filename, 'w', newline=newline) as f_ptr:
            f_ptr.write('\n'.join(lines))
    else:
        return lines_out
Esempio n. 8
0
def write_EA(reactions,
             conditions,
             write_gas_phase=False,
             filename=None,
             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. If not specified, returns EAs file
            as str
        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 '  '
    Returns
    -------
        lines_out : str
            EA.inp lines as a string if ``filename`` is None
    """
    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_file_timestamp(comment_char='! '),
        ('!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')
    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:
        return lines_out