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
Exemplo n.º 2
0
 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
Exemplo n.º 3
0
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
Exemplo n.º 4
0
    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