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))
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
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
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
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
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))