def compute_thermo(self, kinetics_flag: bool = False, e0_only: bool = False, ) -> None: """ Generate thermodynamic data for a species. Populates the species.thermo attribute. Args: kinetics_flag (bool, optional): Whether this call is used for generating species statmech for a rate coefficient calculation. e0_only (bool, optional): Whether to only run statmech (w/o thermo) to compute E0. """ if not kinetics_flag: # initialize the Arkane species_dict so that species for which thermo is calculated won't interfere # with species used for a rate coefficient calculation. arkane.input.species_dict = dict() if self.sp_level.to_arkane_level_of_theory(variant='AEC', raise_error=False, warn=False) is None: raise ValueError(f'Cannot compute thermo without a valid Arkane Level for AEC.') if self.species is None: raise InputError('Cannot not compute thermo without a species object.') arkane_output_path = self.generate_arkane_species_file(species=self.species, bac_type=self.bac_type) if arkane_output_path is not None: try: arkane_species = arkane_input_species(self.species.label, self.species.arkane_file) except ValueError: arkane_species = arkane.input.species_dict[self.species.label] self.species.rmg_species = Species(molecule=[self.species.mol]) self.species.rmg_species.reactive = True if self.species.mol_list: # add resonance structures for thermo determination arkane_species.molecule = self.species.mol_list self.species.rmg_species.molecule = self.species.mol_list statmech_success = self.run_statmech(arkane_species=arkane_species, arkane_file_path=self.species.arkane_file, arkane_output_path=arkane_output_path, bac_type=self.bac_type, sp_level=self.sp_level, plot=False) if statmech_success: self.species.e0 = arkane_species.conformer.E0.value_si * 0.001 # convert to kJ/mol logger.debug(f'Assigned E0 to {self.species.label}: {self.species.e0:.2f} kJ/mol') if not e0_only: thermo_job = ThermoJob(arkane_species, 'NASA') thermo_job.execute(output_directory=arkane_output_path, plot=True) self.species.thermo = arkane_species.get_thermo_data() if not kinetics_flag: plotter.log_thermo(self.species.label, path=arkane_output_path) else: logger.error(f'Could not run statmech job for species {self.species.label}') clean_output_directory(species_path=os.path.join(self.output_directory, 'Species', self.species.label))
def process(self): """ Process ARC outputs and generate thermo and kinetics. """ # Thermo: species_list_for_thermo_parity = list() species_for_thermo_lib = list() species_for_transport_lib = list() unconverged_species = list() for species in self.species_dict.values(): if not species.is_ts and 'ALL converged' in self.output[species.label]['status']: output_path = self._generate_arkane_species_file(species) unique_arkane_species_label = False while not unique_arkane_species_label: try: arkane_spc = arkane_input_species(str(species.label), species.arkane_file) except ValueError: species.label += '_' + str(randint(0, 999)) else: unique_arkane_species_label = True species.rmg_species = Species(molecule=[species.mol]) species.rmg_species.reactive = True if species.mol_list: arkane_spc.molecule = species.mol_list species.rmg_species.molecule = species.mol_list # add resonance structures for thermo determination statmech_success = self._run_statmech(arkane_spc, species.arkane_file, output_path, use_bac=self.use_bac) if not statmech_success: continue if species.generate_thermo: thermo_job = ThermoJob(arkane_spc, 'NASA') thermo_job.execute(output_directory=output_path, plot=False) species.thermo = arkane_spc.getThermoData() plotter.log_thermo(species.label, path=output_path) species_for_thermo_lib.append(species) if self.use_bac and self.sp_level: # If BAC was used, save another Arkane YAML file of this species with no BAC, so it can be used # for further rate calculations if needed (where the conformer.E0 has no BAC) statmech_success = self._run_statmech(arkane_spc, species.arkane_file, output_path, use_bac=False) # if statmech_success: # arkane_spc.label += str('_no_BAC') # arkane_spc.thermo = None # otherwise thermo won't be calculated, although we don't really care # thermo_job = ThermoJob(arkane_spc, 'NASA') # thermo_job.execute(output_directory=output_path, plot=False) try: species.rmg_thermo = self.rmgdb.thermo.getThermoData(species.rmg_species) except (ValueError, AttributeError) as e: logger.info('Could not retrieve RMG thermo for species {0}, possibly due to missing 2D structure ' '(bond orders). Not including this species in the parity plots.' '\nGot: {1}'.format(species.label, e.message)) else: if species.generate_thermo: species_list_for_thermo_parity.append(species) if 'onedmin converged' in self.output[species.label]['status'].lower(): species_for_transport_lib.append(species) elif 'ALL converged' not in self.output[species.label]['status']: unconverged_species.append(species) # Kinetics: rxn_list_for_kinetics_plots = list() arkane_spc_dict = dict() # a dictionary with all species and the TSs for rxn in self.rxn_list: logger.info('\n\n') species = self.species_dict[rxn.ts_label] # The TS if 'ALL converged' in self.output[species.label]['status'] and rxn.check_ts(): self.copy_freq_output_for_ts(species.label) success = True rxn_list_for_kinetics_plots.append(rxn) output_path = self._generate_arkane_species_file(species) arkane_ts = arkane_transition_state(str(species.label), species.arkane_file) arkane_spc_dict[species.label] = arkane_ts self._run_statmech(arkane_ts, species.arkane_file, kinetics=True) for spc in rxn.r_species + rxn.p_species: if spc.label not in arkane_spc_dict.keys(): # add an extra character to the arkane_species label to distinguish between species calculated # for thermo and species calculated for kinetics (where we don't want to use BAC) arkane_spc = arkane_input_species(str(spc.label + '_'), spc.arkane_file) self._run_statmech(arkane_spc, spc.arkane_file, kinetics=True) rxn.dh_rxn298 = sum([product.thermo.getEnthalpy(298) for product in arkane_spc_dict.values() if product.label in rxn.products])\ - sum([reactant.thermo.getEnthalpy(298) for reactant in arkane_spc_dict.values() if reactant.label in rxn.reactants]) arkane_rxn = arkane_reaction(label=str(rxn.label), reactants=[str(label + '_') for label in arkane_spc_dict.keys() if label in rxn.reactants], products=[str(label + '_') for label in arkane_spc_dict.keys() if label in rxn.products], transitionState=rxn.ts_label, tunneling='Eckart') kinetics_job = KineticsJob(reaction=arkane_rxn, Tmin=self.t_min, Tmax=self.t_max, Tcount=self.t_count) logger.info('Calculating rate for reaction {0}'.format(rxn.label)) try: kinetics_job.execute(output_directory=output_path, plot=False) except (ValueError, OverflowError) as e: # ValueError: One or both of the barrier heights of -9.3526 and 62.683 kJ/mol encountered in Eckart # method are invalid. # # File "/home/alongd/Code/RMG-Py/arkane/kinetics.py", line 136, in execute # self.generateKinetics(self.Tlist.value_si) # File "/home/alongd/Code/RMG-Py/arkane/kinetics.py", line 179, in generateKinetics # klist[i] = self.reaction.calculateTSTRateCoefficient(Tlist[i]) # File "rmgpy/reaction.py", line 818, in rmgpy.reaction.Reaction.calculateTSTRateCoefficient # File "rmgpy/reaction.py", line 844, in rmgpy.reaction.Reaction.calculateTSTRateCoefficient # OverflowError: math range error logger.error('Failed to generate kinetics for {0} with message:\n{1}'.format(rxn.label, e)) success = False if success: rxn.kinetics = kinetics_job.reaction.kinetics plotter.log_kinetics(species.label, path=output_path) rxn.rmg_reactions = rmgdb.determine_rmg_kinetics(rmgdb=self.rmgdb, reaction=rxn.rmg_reaction, dh_rxn298=rxn.dh_rxn298) logger.info('\n\n') output_dir = os.path.join(self.project_directory, 'output') libraries_path = os.path.join(output_dir, 'RMG libraries') if species_list_for_thermo_parity: plotter.draw_thermo_parity_plots(species_list_for_thermo_parity, path=output_dir) plotter.save_thermo_lib(species_for_thermo_lib, path=libraries_path, name=self.project, lib_long_desc=self.lib_long_desc) if species_for_transport_lib: plotter.save_transport_lib(species_for_thermo_lib, path=libraries_path, name=self.project) if rxn_list_for_kinetics_plots: plotter.draw_kinetics_plots(rxn_list_for_kinetics_plots, path=output_dir, t_min=self.t_min, t_max=self.t_max, t_count=self.t_count) plotter.save_kinetics_lib(rxn_list=rxn_list_for_kinetics_plots, path=libraries_path, name=self.project, lib_long_desc=self.lib_long_desc) self._clean_output_directory() if unconverged_species: if not os.path.isdir(output_dir): os.makedirs(output_dir) with open(os.path.join(output_dir, 'unconverged_species.log'), 'w') as f: for spc in unconverged_species: f.write(spc.label) if spc.is_ts: f.write(str(' rxn: {0}'.format(spc.rxn_label))) elif spc.mol is not None: f.write(str(' SMILES: {0}'.format(spc.mol.toSMILES()))) f.write(str('\n'))
def process(self): """Process ARC outputs and generate thermo and kinetics""" # Thermo: species_list_for_thermo_parity = list() species_for_thermo_lib = list() for species in self.species_dict.values(): if not species.is_ts and 'ALL converged' in self.output[ species.label]['status']: species_for_thermo_lib.append(species) output_file_path = self._generate_arkane_species_file(species) arkane_spc = arkane_species(str(species.label), species.arkane_file) if species.mol_list: arkane_spc.molecule = species.mol_list stat_mech_job = StatMechJob(arkane_spc, species.arkane_file) stat_mech_job.applyBondEnergyCorrections = self.use_bac stat_mech_job.modelChemistry = self.model_chemistry stat_mech_job.frequencyScaleFactor = assign_frequency_scale_factor( self.model_chemistry) stat_mech_job.execute(outputFile=output_file_path, plot=False) if species.generate_thermo: thermo_job = ThermoJob(arkane_spc, 'NASA') thermo_job.execute(outputFile=output_file_path, plot=False) species.thermo = arkane_spc.getThermoData() plotter.log_thermo(species.label, path=output_file_path) species.rmg_species = Species(molecule=[species.mol]) species.rmg_species.reactive = True if species.mol_list: species.rmg_species.molecule = species.mol_list # add resonance structures for thermo determination try: species.rmg_thermo = self.rmgdb.thermo.getThermoData( species.rmg_species) except ValueError: logging.info( 'Could not retrieve RMG thermo for species {0}, possibly due to missing 2D structure ' '(bond orders). Not including this species in the parity plots.' .format(species.label)) else: if species.generate_thermo: species_list_for_thermo_parity.append(species) # Kinetics: rxn_list_for_kinetics_plots = list() arkane_spc_dict = dict() # a dictionary with all species and the TSs for rxn in self.rxn_list: logging.info('\n\n') species = self.species_dict[rxn.ts_label] # The TS if 'ALL converged' in self.output[ species.label]['status'] and rxn.check_ts(): self.copy_freq_output_for_ts(species.label) success = True rxn_list_for_kinetics_plots.append(rxn) output_file_path = self._generate_arkane_species_file(species) arkane_ts = arkane_transition_state(str(species.label), species.arkane_file) arkane_spc_dict[species.label] = arkane_ts stat_mech_job = StatMechJob(arkane_ts, species.arkane_file) stat_mech_job.applyBondEnergyCorrections = False if not self.model_chemistry: stat_mech_job.modelChemistry = self.model_chemistry else: stat_mech_job.applyAtomEnergyCorrections = False stat_mech_job.frequencyScaleFactor = assign_frequency_scale_factor( self.model_chemistry) stat_mech_job.execute(outputFile=None, plot=False) for spc in rxn.r_species + rxn.p_species: if spc.label not in arkane_spc_dict.keys(): # add an extra character to the arkane_species label to distinguish between species calculated # for thermo and species calculated for kinetics (where we don't want to use BAC) arkane_spc = arkane_species(str(spc.label + '_'), spc.arkane_file) stat_mech_job = StatMechJob(arkane_spc, spc.arkane_file) arkane_spc_dict[spc.label] = arkane_spc stat_mech_job.applyBondEnergyCorrections = False if not self.model_chemistry: stat_mech_job.modelChemistry = self.model_chemistry else: stat_mech_job.applyAtomEnergyCorrections = False stat_mech_job.frequencyScaleFactor = assign_frequency_scale_factor( self.model_chemistry) stat_mech_job.execute(outputFile=None, plot=False) # thermo_job = ThermoJob(arkane_spc, 'NASA') # thermo_job.execute(outputFile=None, plot=False) # arkane_spc.thermo = arkane_spc.getThermoData() rxn.dh_rxn298 = sum([product.thermo.getEnthalpy(298) for product in arkane_spc_dict.values() if product.label in rxn.products])\ - sum([reactant.thermo.getEnthalpy(298) for reactant in arkane_spc_dict.values() if reactant.label in rxn.reactants]) arkane_rxn = arkane_reaction( label=str(rxn.label), reactants=[ str(label + '_') for label in arkane_spc_dict.keys() if label in rxn.reactants ], products=[ str(label + '_') for label in arkane_spc_dict.keys() if label in rxn.products ], transitionState=rxn.ts_label, tunneling='Eckart') kinetics_job = KineticsJob(reaction=arkane_rxn, Tmin=self.t_min, Tmax=self.t_max, Tcount=self.t_count) logging.info('Calculating rate for reaction {0}'.format( rxn.label)) try: kinetics_job.execute(outputFile=output_file_path, plot=False) except ValueError as e: """ ValueError: One or both of the barrier heights of -9.35259 and 62.6834 kJ/mol encountered in Eckart method are invalid. """ logging.error( 'Failed to generate kinetics for {0} with message:\n{1}' .format(rxn.label, e)) success = False if success: rxn.kinetics = kinetics_job.reaction.kinetics plotter.log_kinetics(species.label, path=output_file_path) rxn.rmg_reactions = rmgdb.determine_rmg_kinetics( rmgdb=self.rmgdb, reaction=rxn.rmg_reaction, dh_rxn298=rxn.dh_rxn298) logging.info('\n\n') output_dir = os.path.join(self.project_directory, 'output') if species_list_for_thermo_parity: plotter.draw_thermo_parity_plots(species_list_for_thermo_parity, path=output_dir) libraries_path = os.path.join(output_dir, 'RMG libraries') # species_list = [spc for spc in self.species_dict.values()] plotter.save_thermo_lib(species_for_thermo_lib, path=libraries_path, name=self.project, lib_long_desc=self.lib_long_desc) if rxn_list_for_kinetics_plots: plotter.draw_kinetics_plots(rxn_list_for_kinetics_plots, path=output_dir, t_min=self.t_min, t_max=self.t_max, t_count=self.t_count) libraries_path = os.path.join(output_dir, 'RMG libraries') plotter.save_kinetics_lib(rxn_list=rxn_list_for_kinetics_plots, path=libraries_path, name=self.project, lib_long_desc=self.lib_long_desc) self.clean_output_directory()