def genNetwork(self, mol_object, **kwargs): """ Execute the automatic reaction discovery procedure. """ # Database qm_collection = db['qm_calculate_center'] config_collection = db['config'] statistics_collection = db['statistics'] targets = list(config_collection.find({'generations': 1})) config_collection.update_one( targets[0], {"$set": { 'config_path': kwargs['config_path'] }}, True) # Reactant information reactant_inchi_key = mol_object.write('inchiKey').strip() # inchikey # Generate all possible products gen = Generate(mol_object, **kwargs) self.logger.info('Generating all possible products...') gen.generateProducts() prod_mols = gen.get_prods() add_bonds = gen.get_add_bonds() break_bonds = gen.get_break_bonds() prod_mols_filtered = [] self.logger.info(f'{len(prod_mols)} possible products are generated\n') # Filter reactions based on standard heat of reaction delta H if self.method.lower() == 'mopac': self.logger.info( f'Now use {self.method} to filter the delta H of reactions....\n' ) if self.generations == 1: os.mkdir(path.join(path.dirname(self.ard_path), 'reactions')) H298_reac = self.get_mopac_H298(mol_object) config_collection.update_one( targets[0], { "$set": { 'reactant_energy': H298_reac, 'use_irc': self.use_irc, 'use_qmmm': self.use_qmmm } }, True) mol_object_copy = mol_object.copy() for prod_mol in prod_mols: if self.filter_dh_mopac( mol_object, self.cluster_bond, prod_mol, add_bonds[prod_mols.index(prod_mol)], break_bonds[prod_mols.index(prod_mol)], len(prod_mols), qm_collection, refH=None): prod_mols_filtered.append(prod_mol) # Recovery mol_object_copy = mol_object.copy() else: H298_reac = targets[0]['reactant_energy'] mol_object_copy = mol_object.copy() for prod_mol in prod_mols: if self.filter_dh_mopac( mol_object, self.cluster_bond, prod_mol, add_bonds[prod_mols.index(prod_mol)], break_bonds[prod_mols.index(prod_mol)], len(prod_mols), qm_collection, refH=H298_reac): prod_mols_filtered.append(prod_mol) # Recovery mol_object_copy = mol_object.copy() elif self.method.lower() == 'xtb': self.logger.info( 'Now use {} to filter the delta H of reactions....\n'.format( self.method)) if self.generations == 1: os.mkdir(path.join(path.dirname(self.ard_path), 'reactions')) H298_reac = self.get_xtb_H298( config_path=kwargs['config_path']) config_collection.update_one( targets[0], { "$set": { 'reactant_energy': H298_reac, 'use_irc': self.use_irc, 'use_qmmm': self.use_qmmm } }, True) mol_object_copy = mol_object.copy() for prod_mol in prod_mols: if self.filter_dh_xtb( mol_object, prod_mol, self.cluster_bond, add_bonds[prod_mols.index(prod_mol)], break_bonds[prod_mols.index(prod_mol)], len(prod_mols), qm_collection, config_path=kwargs['config_path'], refH=None): prod_mols_filtered.append(prod_mol) mol_object.setCoordsFromMol(mol_object_copy) else: H298_reac = targets[0]['reactant_energy'] mol_object_copy = mol_object.copy() for prod_mol in prod_mols: if self.filter_dh_xtb( mol_object, prod_mol, self.cluster_bond, add_bonds[prod_mols.index(prod_mol)], break_bonds[prod_mols.index(prod_mol)], len(prod_mols), qm_collection, config_path=kwargs['config_path'], refH=H298_reac): prod_mols_filtered.append(prod_mol) mol_object.setCoordsFromMol(mol_object_copy) else: self.logger.info( 'Now use {} to filter the delta H of reactions....\n'.format( self.method)) # Load thermo database and choose which libraries to search thermo_db = ThermoDatabase() thermo_db.load(path.join(settings['database.directory'], 'thermo')) thermo_db.libraryOrder = [ 'primaryThermoLibrary', 'NISTThermoLibrary', 'thermo_DFT_CCSDTF12_BAC', 'CBS_QB3_1dHR', 'DFT_QCI_thermo', 'BurkeH2O2', 'GRI-Mech3.0-N', ] if self.generations == 1: H298_reac = mol_object.getH298(thermo_db) update_field = {'reactant_energy': H298_reac} config_collection.update_one(targets[0], {"$set": update_field}, True) else: H298_reac = targets[0]['reactant_energy'] prod_mols_filtered = [ mol for mol in prod_mols if self.filter_dh_rmg(H298_reac, mol, thermo_db) ] self.logger.info('Generate geometry........\n') for mol in prod_mols_filtered: index = prod_mols.index(mol) # Generate geometry and return path mol_object.gen3D(self.fixed_atoms, forcefield=self.forcefield, method=self.constraintff_alg, make3D=False) reac_mol_copy = mol_object.copy() dir_path = self.gen_geometry(mol_object, mol, reac_mol_copy, add_bonds[index], break_bonds[index]) product_inchi_key = mol.write('inchiKey').strip() self.logger.info( f"\nReactant inchi key: {reactant_inchi_key}\nProduct inchi key: {product_inchi_key}\nReactant smiles: {mol_object.write('can').split()}\nProduct smiles: {mol.write('can').split()}\nDirectory path: {dir_path}\n" ) qm_collection.insert_one({ 'reaction': [reactant_inchi_key, product_inchi_key], 'reactant_smiles': mol_object.write('can').split()[0], 'reactant_inchi_key': reactant_inchi_key, 'product_inchi_key': product_inchi_key, 'product_smiles': mol.write('can').split()[0], 'path': dir_path, 'ssm_status': 'job_unrun', 'generations': self.generations, }) self.logger.info('After delta H filter {} product remain.\n'.format( len(prod_mols_filtered))) # Generate geometry and insert to database statistics_collection.insert_one({ 'reactant_smiles': mol_object.write('can').split()[0], 'reactant_inchi_key': reactant_inchi_key, 'add how many products': len(prod_mols_filtered), 'generations': self.generations })
def execute(self, **kwargs): """ Execute the automatic reaction discovery procedure. """ start_time = time.time() reac_mol = self.initialize() # self.optimizeReactant(reac_mol, **kwargs) gen = Generate(reac_mol) self.logger.info('Generating all possible products...') gen.generateProducts(nbreak=self.nbreak, nform=self.nform) prod_mols = gen.prod_mols self.logger.info('{} possible products generated\n'.format( len(prod_mols))) # Load thermo database and choose which libraries to search thermo_db = ThermoDatabase() thermo_db.load(os.path.join(settings['database.directory'], 'thermo')) thermo_db.libraryOrder = [ 'primaryThermoLibrary', 'NISTThermoLibrary', 'thermo_DFT_CCSDTF12_BAC', 'CBS_QB3_1dHR', 'DFT_QCI_thermo', 'KlippensteinH2O2', 'GRI-Mech3.0-N', ] # Filter reactions based on standard heat of reaction H298_reac = reac_mol.getH298(thermo_db) self.logger.info('Filtering reactions...') prod_mols_filtered = [ mol for mol in prod_mols if self.filterThreshold(H298_reac, mol, thermo_db, **kwargs) ] self.logger.info('{} products remaining\n'.format( len(prod_mols_filtered))) # Generate 3D geometries if prod_mols_filtered: self.logger.info('Feasible products:\n') rxn_dir = util.makeOutputSubdirectory(self.output_dir, 'reactions') # These two lines are required so that new coordinates are # generated for each new product. Otherwise, Open Babel tries to # use the coordinates of the previous molecule if it is isomorphic # to the current one, even if it has different atom indices # participating in the bonds. a hydrogen atom is chosen # arbitrarily, since it will never be the same as any of the # product structures. Hatom = gen3D.readstring('smi', '[H]') ff = pybel.ob.OBForceField.FindForceField(self.forcefield) reac_mol_copy = reac_mol.copy() for rxn, mol in enumerate(prod_mols_filtered): mol.gen3D(forcefield=self.forcefield, make3D=False) arrange3D = gen3D.Arrange3D(reac_mol, mol) msg = arrange3D.arrangeIn3D() if msg != '': self.logger.info(msg) ff.Setup( Hatom.OBMol ) # Ensures that new coordinates are generated for next molecule (see above) reac_mol.gen3D(make3D=False) ff.Setup(Hatom.OBMol) mol.gen3D(make3D=False) ff.Setup(Hatom.OBMol) reactant = reac_mol.toNode() product = mol.toNode() rxn_num = '{:04d}'.format(rxn) output_dir = util.makeOutputSubdirectory(rxn_dir, rxn_num) kwargs['output_dir'] = output_dir kwargs['name'] = rxn_num self.logger.info('Product {}: {}\n{}\n****\n{}\n'.format( rxn, product.toSMILES(), reactant, product)) self.makeInputFile(reactant, product, **kwargs) reac_mol.setCoordsFromMol(reac_mol_copy) else: self.logger.info('No feasible products found') # Finalize self.finalize(start_time)