def gen_geometry(self, reactant_mol, product_mol, reactant_mol_copy, add_bonds, break_bonds, **kwargs): # Initial optimization Hatom = gen3D.readstring('smi', '[H]') ff = pybel.ob.OBForceField.FindForceField(self.forcefield) reactant_mol.gen3D(self.fixed_atoms, forcefield=self.forcefield, method=self.constraintff_alg, make3D=False) product_mol.gen3D(self.fixed_atoms, forcefield=self.forcefield, method=self.constraintff_alg, make3D=False) # Arrange # If arrange error can use try arrange3D = gen3D.Arrange3D(reactant_mol, product_mol, self.fixed_atoms, self.fixed_atoms, self.cluster_bond) msg = arrange3D.arrangeIn3D() if msg != '': self.logger.info(msg) # After arrange to prevent openbabel use the previous product coordinates if it is isomorphic # to the current one, even if it has different atom indices participating in the bonds. ff.Setup(Hatom.OBMol) reactant_mol.gen3D(self.fixed_atoms, forcefield=self.forcefield, method=self.constraintff_alg, make3D=False) ff.Setup(Hatom.OBMol) product_mol.gen3D(self.fixed_atoms, forcefield=self.forcefield, method=self.constraintff_alg, make3D=False) ff.Setup(Hatom.OBMol) reactant = reactant_mol.toNode() product = product_mol.toNode() self.logger.info( 'Reactant and product geometry is :\n{}\n****\n{}'.format( str(reactant), str(product))) subdir = path.join(path.dirname(self.ard_path), 'reactions') if not path.exists(subdir): os.mkdir(subdir) b_dirname = product_mol.write('inchiKey').strip() dirname = self.dir_check(subdir, b_dirname) output_dir = util.makeOutputSubdirectory(subdir, dirname) kwargs['output_dir'] = output_dir #self.makeInputFile(reactant, product, **kwargs) self.makeCalFile(reactant, 'reactant.xyz', **kwargs) self.makeCalFile(product, 'product.xyz', **kwargs) self.makeisomerFile(add_bonds, break_bonds, **kwargs) reactant_mol.setCoordsFromMol(reactant_mol_copy) return output_dir
def generateReactant3D(self): """ Convert the reactant SMILES to a :class:`gen3D.Molecule` object and generate a 3D geometry. Return the object and store the corresponding :class:`node.Node` object in `self.reactant`. """ reac_mol = gen3D.readstring('smi', self.reac_smi) reac_mol.addh() reac_mol.gen3D(forcefield=self.forcefield) return reac_mol
def gen_geometry(reactant_mol, product_mol, fixed_atoms): # Set up constraint constraint_forcefield = ob.OBFFConstraints() constraint_forcefield.AddAtomConstraint(1) ff = pb.ob.OBForceField.FindForceField('uff') Hatom = gen3D.readstring('smi', '[H]') # reactant_bonds = [tuple(sorted((bond.GetBeginAtomIdx() - 1, bond.GetEndAtomIdx() - 1)) + [bond.GetBondOrder()]) # for bond in pb.ob.OBMolBondIter(reactant_mol.OBMol)] # reactant_bonds = tuple(sorted(reactant_bonds)) # print(reactant_bonds) reactant_mol.gen3D(fixed_atoms, forcefield='uff', method='ConjugateGradients', make3D=False) product_mol.gen3D(fixed_atoms, forcefield='uff', method='ConjugateGradients', make3D=False) #print(product_mol.write('inchikey')) arrange3D = gen3D.Arrange3D(reactant_mol, product_mol, fixed_atoms, cluster_bond=[(12, 14, 1), (12, 15, 1), (12, 16, 1), (12, 17, 1), (12, 13, 1), (17, 23, 1), (16, 23, 1)]) msg = arrange3D.arrangeIn3D() if msg != '': print(msg) ff.Setup(Hatom.OBMol, constraint_forcefield) ff.SetConstraints(constraint_forcefield) reactant_mol.gen3D(fixed_atoms, forcefield='uff', method='ConjugateGradients', make3D=False) ff.Setup(Hatom.OBMol, constraint_forcefield) ff.SetConstraints(constraint_forcefield) product_mol.gen3D(fixed_atoms, forcefield='uff', method='ConjugateGradients', make3D=False) ff.Setup( Hatom.OBMol, constraint_forcefield ) # Ensures that new coordinates are generated for next molecule (see above) ff.SetConstraints(constraint_forcefield) product_geometry = product_mol.toNode() reactant_geometry = reactant_mol.toNode() return reactant_geometry, product_geometry
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)
def genInput(self, reactant_mol, product_mol, threshold=6.0): start_time = time.time() # 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. # Set up constraint constraint_forcefield = ob.OBFFConstraints() constraint_forcefield.AddAtomConstraint(1) ff = pybel.ob.OBForceField.FindForceField(self.forcefield) Hatom = gen3D.readstring('smi', '[H]') reactant_mol.gen3D(self.fixed_atoms, forcefield=self.forcefield, method=self.constraintff_alg, make3D=False) product_mol.gen3D(self.fixed_atoms, forcefield=self.forcefield, method=self.constraintff_alg, make3D=False) # Arrange try: arrange3D = gen3D.Arrange3D(reactant_mol, product_mol, self.fixed_atoms, self.cluster_bond) msg = arrange3D.arrangeIn3D() if msg != '': print(msg) except: self.logger.info('Here is the {} product.'.format(self.num)) self.logger.info('Arrange fail') return False, False ff.Setup(Hatom.OBMol, constraint_forcefield) ff.SetConstraints(constraint_forcefield) reactant_mol.gen3D(self.fixed_atoms, forcefield=self.forcefield, method=self.constraintff_alg, make3D=False) ff.Setup(Hatom.OBMol, constraint_forcefield) ff.SetConstraints(constraint_forcefield) product_mol.gen3D(self.fixed_atoms, forcefield=self.forcefield, method=self.constraintff_alg, make3D=False) ff.Setup(Hatom.OBMol, constraint_forcefield) ff.SetConstraints(constraint_forcefield) # Ensures that new coordinates are generated for next molecule (see above) # Check reactant expected forming bond length must smaller than 4 angstrom after arrange. Default = 4 # After arrange to prevent openbabel use the previous product coordinates if it is isomorphic # to the current one, even if it has different atom indices participating in the bonds. # return the maximum value in array dist = self.check_bond_length(reactant_mol, self.form_bonds) if dist >= threshold: self.logger.info('\nHere is the {} product.'.format(self.num)) self.logger.info('Form bonds: {}\nBreak bonds: {}\nForm bond distance: {}'.format(self.form_bonds, self.break_bonds, dist)) self.logger.info('Form bond distance is greater than threshold.') self.finalize(start_time, 'arrange') self.logger.info('Finished {}/{}'.format(self.num, self.count)) return False, False else: self.logger.info('\nHere is the {} product.'.format(self.num)) self.logger.info('Structure:\n{}'.format(str(reactant_mol.toNode()))) self.logger.info('Structure:\n{}\n'.format(str(product_mol.toNode()))) self.logger.info('Form bonds: {}\nBreak bonds: {}\nForm bond distance: {}'.format(self.form_bonds, self.break_bonds, dist)) product_geometry = product_mol.toNode() reactant_geometry = reactant_mol.toNode() # reactant_mol.setCoordsFromMol(reac_mol_copy) self.finalize(start_time, 'arrange') return reactant_geometry, product_geometry