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