def __init__(self, reaction_model=None): """ Class for `solving' for equilibrium coverages and rates as a function of reaction parameters. This class acts as a base class to be inherited by other solver classes, but is not functional on its own. rxn_parameters: list of necessary parameters to solve the kinetic system. This will usually be populated by the scaler. A functional derived solver class must also contain the methods: get_coverage(): a function which returns coverages for each adsorbate as a list [cvg_ads1,cvg_ads2,...] get_rate(): a function which returns steady-state reaction rates for each elementary step as a list [rate_rxn1,rate_rxn2,...] get_residual(): a function for computing the norm of the residual. This is the condition which will be minimized to reach steady-state. compile(): a function to set-up/compile the solver. """ if reaction_model is None: reaction_model = ReactionModel() self._rxm = reaction_model self._compiled = False
def __init__(self, reaction_model=None): if reaction_model is None: reaction_model = ReactionModel() self._rxm = reaction_model defaults = dict( cross_interaction_mode='geometric_mean', transition_state_cross_interaction_mode='intermediate_state', default_self_interaction_parameter=0, interaction_response_function='linear', interaction_response_parameters={ 'slope': 1, 'cutoff': 0.25, 'smoothing': 0.05 }, interaction_fitting_mode=None, input_overrides_fit=True, #user inputs override fitted values interaction_strength=1, #weight interaction parameters by this numerical_delta_theta=None) self._rxm.update(defaults) self._required = { 'cross_interaction_mode': str, 'transition_state_cross_interaction_mode': str, 'interaction_fitting_mode': None } self._log_strings = { 'interaction_fitting_success': "interaction parameter ${param}@${surface} = ${paramval} (avg_error=${error})" }
def __init__(self,reaction_model=None): if reaction_model is None: reaction_model = ReactionModel() self._rxm = reaction_model self._solver_output = ['coverage','rate', #outputs requiring solver 'turnover_frequency','selectivity','rate_control', 'noninteracting_coverages']
def __init__(self,reaction_model = None): """Class for `scaling' descriptors to free energies of reaction and activation (or other parameters). This class acts as a base class to be inherited by other scaler classes, but is not functional on its own. This class contains the description of the microkinetic model (adsorbate_names, gas_names, etc.) along with the temperature and gas_pressures. In most cases these will automatically be populated by the parent reaction_model class. The scaler-specific attributes are: gas_energies: defines the energies of the gas-phase species. This sets the references for the system. gas_thermo_mode: the mode for obtaining thermal contributions in the gas phase. Default is to use the ideal gas approxmation. adsorbate_thermo_mode: the mode for obtaining thermal contributions from adsorbed species. Default is to use the harmonic adsorbate approximation. frequency_dict: a dictionary of vibrational frequencies (in eV) for each gas/adsorbate. Should be of the form frequency_dict[ads] = [freq1, freq2,...]. Needed for ideal gas or harmonic adsorbate approximations. A functional derived scaler class must also contain the methods: get_electronic_energies(descriptors): a function to `scale' the descriptors to electronic energies. Returns a dictionary of the electronic energies of each species in the model. get_energetics(descriptors): a function to obtain the reaction energetics from the descriptors. Should return a list of length N (number of elementary reactions): [[E_rxn1,E_a1],[E_rxn2,E_a2],...[E_rxnN,E_aN]] get_rxn_parameters(descriptors): a function to obtain all necessary reaction parameters from the descriptors. Should return a list of length N (number of elementary reactions): [[param1_rxn1,param2_rxn1...]...[param1_rxnN,param2_rxnN...]]. For a simple model this could be the same as get_energetics, but models accounting for interactions may require more parameters which can be scaled. """ if reaction_model is None: reaction_model = ReactionModel() self._rxm = reaction_model defaults = dict( parameter_mode = 'formation_energy', ) self._rxm.update(defaults,override=False)
def __init__(self,reaction_model=ReactionModel()): self._rxm = reaction_model defaults = dict( cross_interaction_mode='geometric_mean', transition_state_cross_interaction_mode='intermediate_state', interaction_response_function = 'linear', interaction_response_parameters = {'max_coverage':1,'cutoff':0.25, 'smoothing':0.05}, interaction_fitting_mode=None, interaction_strength = 1, #weight interaction parameters by this ) self._rxm.update(defaults) self._required = {'cross_interaction_mode':str, 'transition_state_cross_interaction_mode':str, 'interaction_fitting_mode':None }
def __init__(self, reaction_model=None): if reaction_model is None: reaction_model = ReactionModel() self._rxm = reaction_model self._log_strings = { 'harmonic_transition_state_warning': 'averaging initial/final state thermal contributions for ${TS}', 'shomate_warning': 'temperature below shomate minumum for ${gas};' + ' Cp(${T}) and S(${T}) are used below ${T}.', 'force_recompilation': 'Enabling model.force_recompilation = True. Necessary for field corrections', } # set defaults defaults = dict( gas_thermo_mode='ideal_gas', adsorbate_thermo_mode='harmonic_adsorbate', electrochemical_thermo_mode='simple_electrochemical', pressure_mode='static', thermodynamic_corrections=['gas', 'adsorbate'], thermodynamic_variables=[ 'temperature', 'gas_pressures', 'voltage', 'beta', 'pH', 'Upzc' ], ideal_gas_params=catmap.data.ideal_gas_params, fixed_entropy_dict=catmap.data.fixed_entropy_dict, shomate_params=catmap.data.shomate_params, hbond_dict=catmap.data.hbond_dict, atoms_dict={}, frequency_dict={}, force_recalculation=False, ) self._required = { 'thermodynamic_corrections': list, 'thermodynamic_variables': list, } self._zpe_dict = {} self._enthalpy_dict = {} self._entropy_dict = {} self._rxm.update(defaults) for corr in self.thermodynamic_corrections: self._required[corr + '_thermo_mode'] = str self.thermodynamic_variables.append(corr + '_thermo_mode')
def __init__(self, reaction_model=ReactionModel()): """Class for `parsing' information from raw data (databases, spreadsheets, text files, trajectories, etc.) into a structure which is useful to the microkinetic model. This class acts as a base class to be inherited by other parser classes, but it is not functional on its own. input_file: defines the file path or object to get data from A functional derived parser class must also contain the methods: parse(input_file): a function to parse the input_file file/object and return properly formatted data. The parse function should save all necessary attributes to the Parser class. After parsing the parent microkinetic model class will update itself from the Parser attributes. """ self._rxm = reaction_model self._required = {} #No user-defined attributes are required.
def __init__(self, reaction_model=ReactionModel()): """Class for `mapping' equilibrium coverages and rates through descriptor space. This class acts as a base class to be inherited by other mapper classes, but is not functional on its own. get_rxn_parameter_map(descriptor_ranges,resolution): Uses a scaler object to determine the reaction parameters as a function of descriptor space. May be useful for debugging or providing intuition about rate determining steps. Should return a list of the form [[descriptor_1,descriptor_2,...],[rxn_parameter1, rxn_parameter2, ...]] save_map(map,map_file): creates a pickle of the "map" list and dumps it to the map_file load_map(map_file): loads a "map" list by loading a pickle from the map_file A functional derived mapper class must also contain the methods: get_coverage_map(descriptor_ranges,resolution): a function which returns a list of the form [[descriptor_1,descriptor_2,...], [cvg_ads1,cvg_ads2,...]] get_rates_map(descriptor_ranges,resolution): a function which returns a list of the form [[descriptor_1,descriptor_2,...], [rate_rxn1,rate_rxn2,...]] """ self._rxm = reaction_model self._solver_output = [ 'coverage', 'rate', #outputs requiring solver 'turnover_frequency', 'selectivity', 'rate_control', 'noninteracting_coverages' ]
def __init__(self, reaction_model=ReactionModel()): self._rxm = reaction_model self._log_strings = { 'harmonic_transition_state_warning': 'averaging initial/final state thermal contributions for ${TS}', 'shomate_warning': 'temperature below shomate minumum for ${gas};' + ' Cp(${T}) and S(${T}) are used below ${T}.' } #set defaults defaults = dict( gas_thermo_mode='ideal_gas', adsorbate_thermo_mode='harmonic_adsorbate', pressure_mode='static', thermodynamic_corrections=['gas', 'adsorbate'], thermodynamic_variables=['temperature', 'gas_pressures'], ideal_gas_params=catmap.data.ideal_gas_params, fixed_entropy_dict=catmap.data.fixed_entropy_dict, shomate_params=catmap.data.shomate_params, atoms_dict={}, frequency_dict={}, force_recalculation=False, ) self._required = { 'thermodynamic_corrections': list, 'thermodynamic_variables': list, } self._zpe_dict = {} self._enthalpy_dict = {} self._entropy_dict = {} self._rxm.update(defaults) for corr in self.thermodynamic_corrections: self._required[corr + '_thermo_mode'] = str self.thermodynamic_variables.append(corr + '_thermo_mode')
def load(setup_file): rxm = ReactionModel(setup_file=setup_file) return rxm
def plot_single(self, mapp, rxn_index, ax=None, overlay_map=None, alpha_range=None, **plot_args): """ :param mapp: :param rxn_index: Index for the reaction :type rxn_index: int :param ax: axes object :param overlay_map: :type overlay_map: :type alpha_range: :type alpha_range: .. todo:: __doc__ """ if not ax: fig = plt.figure() ax = fig.add_subplot(111) xy, rates = zip(*mapp) dim = len(xy[0]) if dim == 1: x = zip(*xy)[0] descriptor_ranges = [[min(x), max(x)]] if not self.plot_function: if self.log_scale == True: self.plot_function = 'semilogy' else: self.plot_function = 'plot' elif dim == 2: x, y = zip(*xy) descriptor_ranges = [[min(x), max(x)], [min(y), max(y)]] if not self.plot_function: self.plot_function = 'contourf' if 'cmap' not in plot_args: plot_args['cmap'] = self.colormap eff_res = self.resolution * self.resolution_enhancement if self.min: minval = self.min else: minval = None maparray = RM.map_to_array(mapp, descriptor_ranges, eff_res, log_interpolate=self.log_scale, minval=minval) if self.max is None: self.max = maparray.T[rxn_index].max() if self.min is None: self.min = maparray.T[rxn_index].min() if dim == 2: if maparray.min() <= self.min: plot_args['extend'] = 'min' if maparray.max() >= self.max: plot_args['extend'] = 'max' if maparray.max() >= self.max and maparray.min() <= self.min: plot_args['extend'] = 'both' if 'extend' not in plot_args: plot_args['extend'] = 'neither' if self.log_scale and dim == 2: maparray = np.log10(maparray) min_val = np.log10(float(self.min)) max_val = np.log10(float(self.max)) if min_val < -200: min_val = max(maparray.min(), -200) elif max_val == np.inf: max_val = min(maparray.max(), 200) else: min_val = self.min max_val = self.max maparray = np.clip(maparray, min_val, max_val) log_scale = self.log_scale if overlay_map: overlay_array = RM.map_to_array(overlay_map, descriptor_ranges, eff_res) if alpha_range: alpha_min, alpha_max = alpha_range else: alpha_min = overlay_array.min() alpha_max = overlay_array.max() overlay_array = (overlay_array - overlay_array.min()) overlay_array = overlay_array / (alpha_max - alpha_min) overlay_array = np.clip(overlay_array, 0, 1) maparray = np.clip(maparray, min_val, max_val) norm_array = (maparray - maparray.min()) norm_array = norm_array / (maparray.max() - maparray.min()) maparray = norm_array * overlay_array maparray = (maparray - maparray.min()) maparray = maparray / (maparray.max() - maparray.min()) maparray = maparray * (max_val - min_val) + min_val maparray = norm_array * overlay_array norm_array = (maparray - maparray.min()) norm_array = norm_array / (maparray.max() - maparray.min()) maparray = norm_array * (max_val - min_val) + min_val if dim == 1: x_range = descriptor_ranges[0] plot_in = [np.linspace(*x_range + eff_res), maparray[:, rxn_index]] plot = getattr(ax, self.plot_function)(*plot_in) elif dim == 2: x_range, y_range = descriptor_ranges z = maparray[:, :, rxn_index] if self.log_scale: levels = range(int(min_val), int(max_val) + 1) if len(levels) < 3 * self.n_ticks: levels = np.linspace( int(min_val), int(max_val), 3 * self.n_ticks) else: levels = np.linspace(min_val, max_val, min(eff_res, 25)) plot_in = [np.linspace(*x_range + [eff_res[0]]), np.linspace(*y_range + [eff_res[1]]), z, levels] plot = getattr(ax, self.plot_function)(*plot_in, **plot_args) pos = ax.get_position() if self.aspect: ax.set_aspect(self.aspect) ax.apply_aspect() if dim == 1: ax.set_xlim(descriptor_ranges[0]) ax.set_xlabel(self.descriptor_labels[0]) ax.set_ylim([float(self.min), float(self.max)]) elif dim == 2: if self.colorbar: if log_scale: # take only integer tick labels cbar_nums = range(int(min_val), int(max_val) + 1) mod = max(int(len(cbar_nums) / self.n_ticks), 1) cbar_nums = [n for i, n in enumerate(cbar_nums) if not i % mod] cbar_nums = np.array(cbar_nums) else: cbar_nums = np.linspace(min_val, max_val, self.n_ticks) formatstring = '%.' + str(self.axis_label_decimals) + 'g' cbar_labels = [formatstring % (s,) for s in cbar_nums] cbar_labels = [lab.replace('e-0', 'e-').replace('e+0', 'e') for lab in cbar_labels] plot.set_clim(min_val, max_val) fig = ax.get_figure() axpos = list(ax.get_position().bounds) xsize = axpos[2] * 0.04 ysize = axpos[3] xp = axpos[0] + axpos[2] + 0.04 * axpos[2] yp = axpos[1] cbar_box = [xp, yp, xsize, ysize] cbar_ax = fig.add_axes(cbar_box) cbar = fig.colorbar(mappable=plot, ticks=cbar_nums, cax=cbar_ax, extend=plot_args['extend']) cbar.ax.set_yticklabels(cbar_labels) if getattr(self, 'colorbar_label', None): cbar_kwargs = getattr(self, 'colorbar_label_kwargs', {'rotation': -90}) cbar_ax.set_ylabel(self.colorbar_label, **cbar_kwargs) if self.descriptor_labels: ax.set_xlabel(self.descriptor_labels[0]) ax.set_ylabel(self.descriptor_labels[1]) ax.set_xlim(descriptor_ranges[0]) ax.set_ylim(descriptor_ranges[1]) if 'title' in plot_args and plot_args['title']: if 'title_size' not in plot_args: n_pts = self.plot_size * 72 font_size = min([n_pts / len(plot_args['title']), 14]) else: font_size = plot_args['title_size'] ax.set_title(plot_args['title'], size=font_size) if getattr(self, 'n_xticks', None): ax.xaxis.set_major_locator(MaxNLocator(self.n_xticks)) if getattr(self, 'n_yticks', None): ax.yaxis.set_major_locator(MaxNLocator(self.n_yticks)) self.plot_descriptor_pts(mapp, rxn_index, ax=ax, plot_in=plot_in) return ax
def setUp(self): shutil.copy(os.path.join(ref_dir, 'CO_oxidation.mkm'), '.') shutil.copy(os.path.join(ref_dir, 'energies.txt'), '.') self.rxn_model = ReactionModel(setup_file='CO_oxidation.mkm') self.mechanism = MechanismAnalysis(self.rxn_model)
def __init__(self, reaction_model=ReactionModel()): FirstOrderInteractions.__init__(self, reaction_model)
from glob import glob import sys from catmap.model import ReactionModel output_variable = sys.argv[1] logfile = glob('*.log') if len(logfile) > 1: raise InputError('Ambiguous logfile. Ensure that only one file ends with .log') model = ReactionModel(setup_file=logfile[0]) if output_variable == 'rate_control': dim = 2 else: dim = 1 labels = model.output_labels[output_variable] def flatten_2d(output): "Helper function for flattening rate_control output" flat = [] for x in output: flat+= x return flat #flatten rate_control labels if output_variable == 'rate_control': flat_labels = [] for i in labels[0]: for j in labels[1]: flat_labels.append('d'+i+'/d'+j) labels = flat_labels
def plot_single(self, mapp, rxn_index, ax=None, overlay_map=None, alpha_range=None, **plot_args): """ :param mapp: :param rxn_index: Index for the reaction :type rxn_index: int :param ax: axes object :param overlay_map: :type overlay_map: :type alpha_range: :type alpha_range: .. todo:: __doc__ """ if not ax: fig = plt.figure() ax = fig.add_subplot(111) xy, rates = zip(*list(mapp)) dim = len(xy[0]) if dim == 1: x = list(zip(*xy))[0] descriptor_ranges = [[min(x), max(x)]] if not self.plot_function: if self.log_scale == True: self.plot_function = 'semilogy' else: self.plot_function = 'plot' elif dim == 2: x, y = zip(*xy) descriptor_ranges = [[min(x), max(x)], [min(y), max(y)]] if not self.plot_function: self.plot_function = 'contourf' if 'cmap' not in plot_args: plot_args['cmap'] = self.colormap eff_res = self.resolution * self.resolution_enhancement if self.min: minval = self.min else: minval = None maparray = RM.map_to_array(mapp, descriptor_ranges, eff_res, log_interpolate=self.log_scale, minval=minval) if self.max is None: self.max = maparray.T[rxn_index].max() if self.min is None: self.min = maparray.T[rxn_index].min() if dim == 2: if maparray.min() <= self.min: plot_args['extend'] = 'min' if maparray.max() >= self.max: plot_args['extend'] = 'max' if maparray.max() >= self.max and maparray.min() <= self.min: plot_args['extend'] = 'both' if 'extend' not in plot_args: plot_args['extend'] = 'neither' if self.log_scale and dim == 2: maparray = np.log10(maparray) min_val = np.log10(float(self.min)) max_val = np.log10(float(self.max)) if min_val < -200: min_val = max(maparray.min(), -200) elif max_val == np.inf: max_val = min(maparray.max(), 200) else: min_val = self.min max_val = self.max maparray = np.clip(maparray, min_val, max_val) log_scale = self.log_scale if overlay_map: overlay_array = RM.map_to_array(overlay_map, descriptor_ranges, eff_res) if alpha_range: alpha_min, alpha_max = alpha_range else: alpha_min = overlay_array.min() alpha_max = overlay_array.max() overlay_array = (overlay_array - overlay_array.min()) overlay_array = overlay_array / (alpha_max - alpha_min) overlay_array = np.clip(overlay_array, 0, 1) maparray = np.clip(maparray, min_val, max_val) norm_array = (maparray - maparray.min()) norm_array = norm_array / (maparray.max() - maparray.min()) maparray = norm_array * overlay_array maparray = (maparray - maparray.min()) maparray = maparray / (maparray.max() - maparray.min()) maparray = maparray * (max_val - min_val) + min_val maparray = norm_array * overlay_array norm_array = (maparray - maparray.min()) norm_array = norm_array / (maparray.max() - maparray.min()) maparray = norm_array * (max_val - min_val) + min_val if dim == 1: x_range = descriptor_ranges[0] plot_in = [np.linspace(*x_range + eff_res), maparray[:, rxn_index]] plot = getattr(ax, self.plot_function)(*plot_in) elif dim == 2: x_range, y_range = descriptor_ranges z = maparray[:, :, rxn_index] if self.log_scale: levels = range(int(min_val), int(max_val) + 1) if len(levels) < 3 * self.n_ticks: levels = np.linspace(int(min_val), int(max_val), 3 * self.n_ticks) else: # python 3 cannot do int < list, thus # we look at the first element if it is # a list. levels = np.linspace( min_val, max_val, min(eff_res if type(eff_res) is int else eff_res[0], 25)) plot_in = [ np.linspace(*x_range + [eff_res[0]]), np.linspace(*y_range + [eff_res[1]]), z, levels ] plot = getattr(ax, self.plot_function)(*plot_in, **plot_args) pos = ax.get_position() if self.aspect: ax.set_aspect(self.aspect) ax.apply_aspect() if dim == 1: ax.set_xlim(descriptor_ranges[0]) ax.set_xlabel(self.descriptor_labels[0]) ax.set_ylim([float(self.min), float(self.max)]) elif dim == 2: if self.colorbar: if log_scale: #take only integer tick labels cbar_nums = range(int(min_val), int(max_val) + 1) mod = max(int(len(cbar_nums) / self.n_ticks), 1) cbar_nums = [ n for i, n in enumerate(cbar_nums) if not i % mod ] cbar_nums = np.array(cbar_nums) else: cbar_nums = np.linspace(min_val, max_val, self.n_ticks) formatstring = '%.' + str(self.axis_label_decimals) + 'g' cbar_labels = [formatstring % (s, ) for s in cbar_nums] cbar_labels = [ lab.replace('e-0', 'e-').replace('e+0', 'e') for lab in cbar_labels ] plot.set_clim(min_val, max_val) fig = ax.get_figure() axpos = list(ax.get_position().bounds) xsize = axpos[2] * 0.04 ysize = axpos[3] xp = axpos[0] + axpos[2] + 0.04 * axpos[2] yp = axpos[1] cbar_box = [xp, yp, xsize, ysize] cbar_ax = fig.add_axes(cbar_box) cbar = fig.colorbar(mappable=plot, ticks=cbar_nums, cax=cbar_ax, extend=plot_args['extend']) cbar.ax.set_yticklabels(cbar_labels) if getattr(self, 'colorbar_label', None): cbar_kwargs = getattr(self, 'colorbar_label_kwargs', {'rotation': -90}) cbar_ax.set_ylabel(self.colorbar_label, **cbar_kwargs) if self.descriptor_labels: ax.set_xlabel(self.descriptor_labels[0]) ax.set_ylabel(self.descriptor_labels[1]) ax.set_xlim(descriptor_ranges[0]) ax.set_ylim(descriptor_ranges[1]) if 'title' in plot_args and plot_args['title']: if 'title_size' not in plot_args: n_pts = self.plot_size * 72 font_size = min([n_pts / len(plot_args['title']), 14]) else: font_size = plot_args['title_size'] ax.set_title(plot_args['title'], size=font_size) if getattr(self, 'n_xticks', None): ax.xaxis.set_major_locator(MaxNLocator(self.n_xticks)) if getattr(self, 'n_yticks', None): ax.yaxis.set_major_locator(MaxNLocator(self.n_yticks)) self.plot_descriptor_pts(mapp, rxn_index, ax=ax, plot_in=plot_in) return ax
input_file = '\n'.join(lines) #Join the lines with a line break input = open(file_name, 'w') #open the file name in write mode input.write(input_file) #write the text input.close() #close the file print 'Successfully created input file' file_name = 'energies.txt' make_input_file(file_name, formation_energies, frequency_dict) #Test that input is parsed correctly from catmap.model import ReactionModel from catmap.parsers import TableParser rxm = ReactionModel() #The following lines are normally assigned by the setup_file #and are thus not usually necessary. rxm.surface_names = ['Rh'] rxm.adsorbate_names = ['CO', 'C', 'O', 'H', 'CH', 'OH', 'CH2', 'CH3'] rxm.transition_state_names = ['C-O', 'H-OH', 'H-C'] rxm.gas_names = ['CO_g', 'H2_g', 'CH4_g', 'H2O_g'] rxm.species_definitions = {'s': {'site_names': ['111']}} #Now we initialize a parser instance (also normally done by setup_file) parser = TableParser(rxm) parser.input_file = file_name parser.parse() #All structured data is stored in species_definitions; thus we can #check that the parsing was successful by ensuring that all the #data in the input file was collected in this dictionary. for key in rxm.species_definitions:
lines = [header] + lines #add header to top input_file = '\n'.join(lines) #Join the lines with a line break input = open(file_name,'w') #open the file name in write mode input.write(input_file) #write the text input.close() #close the file print 'Successfully created input file' file_name = 'energies.txt' make_input_file(file_name,formation_energies,frequency_dict) #Test that input is parsed correctly from catmap.model import ReactionModel from catmap.parsers import TableParser rxm = ReactionModel() #The following lines are normally assigned by the setup_file #and are thus not usually necessary. rxm.surface_names = ['Rh'] rxm.adsorbate_names = ['CO','C','O','H','CH','OH','CH2','CH3'] rxm.transition_state_names = ['C-O','H-OH','H-C'] rxm.gas_names = ['CO_g','H2_g','CH4_g','H2O_g'] rxm.species_definitions = {'s':{'site_names':['111']}} #Now we initialize a parser instance (also normally done by setup_file) parser = TableParser(rxm) parser.input_file = file_name parser.parse() #All structured data is stored in species_definitions; thus we can #check that the parsing was successful by ensuring that all the #data in the input file was collected in this dictionary. for key in rxm.species_definitions: