Ejemplo n.º 1
0
    def generate(self):
        """ 
        Creates the input for each reaction, runs them, and tests for success.
        If successful, it creates the barrier and product objects.
        It also then does the conformational search, and finally, the hindered rotor scans.
        To make the code the most efficient, all of these happen in parallel, in a sense that
        the jobs are not waiting for each other. E.g., one reaction can still be in the stage
        of TS search, while the other can be already at the hindered rotor scan. This way, 
        all cores are occupied efficiently.

        The switching between the various stages are done via the reac_ts_done variable.
        0: initiate the TS search
        1: check barrier height and errors in TS, and initiates normal mode displacement test, start the irc calculations 
        2: submit product optimization
        3: submit the frequency calculation 
        4: do the optimization of the ts and the products
        5: follow up on the optimizations
        6: finalize calculations, check for wrong number of negative frequencies
        
        If at any times the calculation fails, reac_ts_done is set to -999.
        If all steps are successful, reac_ts_done is set to -1.
        """
        deleted = []
        if len(self.species.reac_inst) > 0:
            alldone = 1
        else:
            alldone = 0

        # status to see of kinbot needs to wait for the product optimizations
        # from another kinbot run, to avoid duplication of calculations
        products_waiting_status = [[] for i in self.species.reac_inst]
        count = 0
        for i in self.species.reac_inst:
            count = count + 1
        all_unique_prod = []
        frag_unique = []
        nameUnique = []
        stpt_inchis = []

        while alldone:
            for index, instance in enumerate(self.species.reac_inst):
                obj = self.species.reac_obj[index]
                instance_name = obj.instance_name
                # START REACTION SEARCH
                if self.species.reac_ts_done[
                        index] == 0 and self.species.reac_step[index] == 0:
                    #verify after restart if search has failed in previous kinbot run
                    status = self.qc.check_qc(instance_name)
                    if status == 'error' or status == 'killed':
                        logging.info(
                            '\tRxn search failed (error or killed) for {}'.
                            format(instance_name))
                        self.species.reac_ts_done[index] = -999

                if self.species.reac_ts_done[
                        index] == 0:  # ts search is ongoing
                    if obj.scan == 0:  #don't do a scan of a bond
                        if self.species.reac_step[index] == obj.max_step + 1:
                            status = self.qc.get_qc_freq(
                                instance_name, self.species.natom)[0]
                            if status == 0:
                                self.species.reac_ts_done[index] = 1
                            elif status == -1:
                                logging.info(
                                    '\tRxn search failed for {}'.format(
                                        instance_name))
                                self.species.reac_ts_done[index] = -999
                        else:
                            self.species.reac_step[
                                index] = reac_family.carry_out_reaction(
                                    obj, self.species.reac_step[index],
                                    self.par.par['qc_command'])

                    else:  # do a bond scan
                        if self.species.reac_step[
                                index] == self.par.par['scan_step'] + 1:
                            status = self.qc.get_qc_freq(
                                instance_name, self.species.natom)[0]
                            if status == 0:
                                self.species.reac_ts_done[index] = 1
                            elif status == -1:
                                logging.info(
                                    '\tRxn search failed for {}'.format(
                                        instance_name))
                                self.species.reac_ts_done[index] = -999
                        else:
                            if self.species.reac_step[index] == 0:
                                self.species.reac_step[
                                    index] = reac_family.carry_out_reaction(
                                        obj, self.species.reac_step[index],
                                        self.par.par['qc_command'])
                            elif self.species.reac_step[index] > 0:
                                status = self.qc.check_qc(instance_name)
                                if status == 'error' or status == 'killed':
                                    logging.info(
                                        '\tRxn search failed for {}'.format(
                                            instance_name))
                                    self.species.reac_ts_done[index] = -999
                                else:
                                    err, energy = self.qc.get_qc_energy(
                                        instance_name)
                                    if err == 0:
                                        self.species.reac_scan_energy[
                                            index].append(energy)
                                        if len(self.species.
                                               reac_scan_energy[index]) > 1:
                                            if self.species.reac_scan_energy[
                                                    index][
                                                        -1] < self.species.reac_scan_energy[
                                                            index][-2]:
                                                self.species.reac_step[
                                                    index] = self.par.par[
                                                        'scan_step']
                                        #ts search restarted w/ next line?
                                        self.species.reac_step[
                                            index] = reac_family.carry_out_reaction(
                                                obj,
                                                self.species.reac_step[index],
                                                self.par.par['qc_command'])

                elif self.species.reac_ts_done[index] == 1:
                    status = self.qc.check_qc(instance_name)
                    if status == 'running': continue
                    elif status == 'error':
                        logging.info(
                            '\tRxn search failed (gaussian error) for {}'.
                            format(instance_name))
                        self.species.reac_ts_done[index] = -999
                    else:
                        #check the barrier height:
                        if self.species.reac_type[
                                index] == 'R_Addition_MultipleBond':
                            sp_energy = self.qc.get_qc_energy(
                                str(self.species.chemid) + '_well_mp2')[1]
                            barrier = (self.qc.get_qc_energy(instance_name)[1]
                                       - sp_energy) * constants.AUtoKCAL
                        else:
                            sp_energy = self.qc.get_qc_energy(
                                str(self.species.chemid) + '_well')[1]
                            barrier = (self.qc.get_qc_energy(instance_name)[1]
                                       - sp_energy) * constants.AUtoKCAL
                        if barrier > self.par.par['barrier_threshold']:
                            logging.info(
                                '\tRxn barrier too high ({0:.2f} kcal/mol) for {1}'
                                .format(barrier, instance_name))
                            self.species.reac_ts_done[index] = -999
                        else:
                            obj.irc = IRC(
                                obj, self.par
                            )  #TODO: this doesn't seem like a good design
                            irc_status = obj.irc.check_irc()
                            if 0 in irc_status:
                                # No IRC started yet, start the IRC now
                                logging.info(
                                    '\tStarting IRC calculations for {}'.
                                    format(instance_name))
                                obj.irc.do_irc_calculations()
                            elif irc_status[0] == 'running' or irc_status[
                                    1] == 'running':
                                continue
                            else:
                                #IRC's have successfully finished, have an error or were killed, in any case
                                #read the geometries and try to make products out of them
                                #verify which of the ircs leads back to the reactant, if any
                                prod = obj.irc.irc2stationary_pt()
                                if prod == 0:
                                    logging.info(
                                        '\t\tNo product found for {}'.format(
                                            instance_name))
                                    self.species.reac_ts_done[index] = -999
                                else:
                                    obj.products = prod
                                    obj.product_bonds = prod.bond
                                    self.species.reac_ts_done[index] = 2

                elif self.species.reac_ts_done[index] == 2:
                    if len(products_waiting_status[index]) == 0:
                        #identify bimolecular products and wells
                        fragments, maps = obj.products.start_multi_molecular()
                        obj.products = []

                        a = []
                        for frag in fragments:
                            a.append(frag)
                            if len(frag_unique) == 0:
                                frag_unique.append(frag)
                            elif len(frag_unique) > 0:
                                new = 1
                                for fragb in frag_unique:
                                    if frag.chemid == fragb.chemid:
                                        e, geom2 = self.qc.get_qc_geom(
                                            str(fragb.chemid) + '_well',
                                            fragb.natom)
                                        if e == 0:
                                            a.pop()
                                            frag = fragb
                                            a.append(frag)
                                            new = 0
                                            break
                                if new:
                                    frag_unique.append(frag)
                        obj.products_final = []
                        for frag in a:
                            self.qc.qc_opt(frag, frag.geom)
                            e, geom2 = self.qc.get_qc_geom(
                                str(frag.chemid) + '_well', frag.natom)
                            obj.products_final.append(frag)

                        #check products make sure they are the same
                        for i, st_pt_i in enumerate(obj.products_final):
                            for j, st_pt_j in enumerate(obj.products_final):
                                if st_pt_i.chemid == st_pt_j.chemid and i < j:
                                    obj.products_final[j] = obj.products_final[
                                        i]

                    #print products generated by IRC
                    products = []
                    for i, st_pt in enumerate(obj.products_final):
                        products.append(st_pt.chemid)

                    products.extend([' ', ' ', ' '])
                    barrier = (self.qc.get_qc_energy(instance_name)[1] -
                               sp_energy) * constants.AUtoKCAL
                    logging.info(
                        '\tReaction {0} has a barrier of {1:.2f} kcal/mol and leads to products {2} {3} {4}'
                        .format(instance_name, barrier, products[0],
                                products[1], products[2]))

                    for i, st_pt in enumerate(obj.products_final):
                        chemid = st_pt.chemid
                        e, st_pt.geom = self.qc.get_qc_geom(
                            str(st_pt.chemid) + '_well', st_pt.natom)
                        if e < 0:
                            logging.info(
                                '\tProduct optimization failed for {}, product {}'
                                .format(instance_name, st_pt.chemid))
                            self.species.reac_ts_done[index] = -999
                            err = -1
                        elif e != 0:
                            err = -1
                        else:
                            e2, st_pt.energy = self.qc.get_qc_energy(
                                str(st_pt.chemid) + '_well')
                            e2, st_pt.zpe = self.qc.get_qc_zpe(
                                str(st_pt.chemid) + '_well')
                            st_pt.characterize(
                                dimer=0
                            )  # not allowed to use the dimer option here
                            if chemid != st_pt.chemid:
                                obj.products_final.pop(i)
                                newfrags, newmaps = st_pt.start_multi_molecular(
                                )  # newfrags is list of stpt obj
                                products_waiting_status[index] = [
                                    0 for frag in newfrags
                                ]
                                fragChemid = []
                                for i, newfr in enumerate(newfrags):
                                    for prod in frag_unique:
                                        if newfr.chemid == prod.chemid:
                                            newfrags.pop(i)
                                            newfr = prod
                                            j = i - 1
                                            newfrags.insert(j, newfr)
                                    #add new frag to frag_unique somehow?
                                    j = i - 1
                                    obj.products_final.insert(j, newfr)
                                    self.qc.qc_opt(newfr, newfr.geom, 0)
                                    fragChemid.append(newfr.chemid)
                                if len(fragChemid) == 1:
                                    fragChemid.append(" ")
                                for i, frag in enumerate(newfrags):
                                    products_waiting_status[index][i] = 1
                                logging.info(
                                    '\ta) Product optimized to other structure for {}, product {} to {} {}'
                                    .format(instance_name, chemid,
                                            fragChemid[0], fragChemid[1]))

                    obj.products = []
                    for prod in obj.products_final:
                        obj.products.append(prod)
                    obj.products_final = []

                    if all([pi == 1 for pi in products_waiting_status[index]]):
                        self.species.reac_ts_done[index] = 3

                elif self.species.reac_ts_done[index] == 3:
                    # wait for the optimization to finish
                    # if two st_pt are the same in the products, we make them exactly identical otherwise
                    # the different ordering of the atoms causes the chemid of the second to be seemingly wrong
                    for i, st_pt_i in enumerate(obj.products):
                        for j, st_pt_j in enumerate(obj.products):
                            if st_pt_i.chemid == st_pt_j.chemid and i < j:
                                obj.products[j] = obj.products[i]
                    '''
                    # generate and compare inchis
                    if len(stpt_inchis) == 0:
                        well0_inchi = cheminfo.create_inchi_from_geom(self.species.atom,self.species.geom)                        
                        well0_chemicalFormula = well0_inchi.split('S/')[1].split('/')[0]
                        well0_stereochem = ''
                        if "/t" in str(well0_inchi):
                            well0_stereochem = well0_inchi.split('/t')[1].split('/')[0]
                        well0_info = [self.species.chemid, well0_chemicalFormula, well0_inchi, well0_stereochem]
                        stpt_inchis.append(well0_info)

                    for st_pt in obj.products:
                        prod_chemid = st_pt.chemid
                        prod_inchi = cheminfo.create_inchi_from_geom(st_pt.atom,st_pt.geom)                        
                        prod_chemicalFormula = prod_inchi.split('S/')[1].split('/')[0]
                        prod_stereochem = ''
                        if "/t" in str(prod_inchi):
                            prod_stereochem = prod_inchi.split('/t')[1].split('/')[0]
                        prod_info = [prod_chemid, prod_chemicalFormula, prod_inchi, prod_stereochem]
                        stpt_inchis.append(prod_info)

                    inchiFile = open('inchis.log','w')
                    well0_chemid = stpt_inchis[0][0]
                    well0_chemicalFormula = stpt_inchis[0][1]
                    well0_stereochem = stpt_inchis[0][3]
                    for inchi in stpt_inchis:
                        inchiFile.write("{}\t|{}\t|{}\t|{}\n".format(inchi[0],inchi[1],inchi[2],inchi[3]))
                        prod_chemid = inchi[0]
                        prod_stereochem = inchi[3]
                        prod_chemicalFormula = inchi[1]
                        if well0_chemicalFormula == prod_chemicalFormula:
                            if str(well0_stereochem) != str(prod_stereochem):
                                logging.warning("\t\t!WARNING! Stereochemistry for product {} differs from the initial well ({}) for reaction {}".format(prod_chemid, well0_chemid, instance_name))
                    inchiFile.close()
                    '''
                    err = 0
                    for st_pt in obj.products:
                        chemid = st_pt.chemid
                        e, st_pt.geom = self.qc.get_qc_geom(
                            str(st_pt.chemid) + '_well', st_pt.natom)
                        if e < 0:
                            logging.info(
                                '\tProduct optimization failed for {}, product {}'
                                .format(instance_name, st_pt.chemid))
                            self.species.reac_ts_done[index] = -999
                            err = -1
                        elif e != 0:
                            err = -1
                        else:
                            e2, st_pt.energy = self.qc.get_qc_energy(
                                str(st_pt.chemid) + '_well')
                            e2, st_pt.zpe = self.qc.get_qc_zpe(
                                str(st_pt.chemid) + '_well')
                            st_pt.characterize(
                                dimer=0
                            )  # not allowed to use the dimer option here
                            if chemid != st_pt.chemid:
                                # product was optimized to another structure, give warning but don't remove reaction
                                logging.info(
                                    '\tb) Product optimized to other structure for {}, product {} to {}'
                                    .format(instance_name, chemid,
                                            st_pt.chemid))
                                e, st_pt.geom = self.qc.get_qc_geom(
                                    str(st_pt.chemid) + '_well', st_pt.natom)
                                if e < 0:
                                    err = -1
                    if err == 0:
                        self.species.reac_ts_done[index] = 4
                elif self.species.reac_ts_done[index] == 4:
                    # Do the TS and product optimization
                    # make a stationary point object of the ts
                    bond_mx = np.zeros(
                        (self.species.natom, self.species.natom))
                    for i in range(self.species.natom):
                        for j in range(self.species.natom):
                            bond_mx[i][j] = max(self.species.bond[i][j],
                                                obj.product_bonds[i][j])

                    err, geom = self.qc.get_qc_geom(instance_name,
                                                    self.species.natom)
                    ts = StationaryPoint(instance_name,
                                         self.species.charge,
                                         self.species.mult,
                                         atom=self.species.atom,
                                         geom=geom,
                                         wellorts=1)
                    err, ts.energy = self.qc.get_qc_energy(instance_name)
                    err, ts.zpe = self.qc.get_qc_zpe(
                        instance_name)  #  NEW STOPS HERE
                    ts.bond = bond_mx
                    ts.find_cycle()
                    ts.find_conf_dihedral()
                    obj.ts = ts
                    #do the ts optimization
                    obj.ts_opt = Optimize(obj.ts, self.par, self.qc)
                    obj.ts_opt.do_optimization()

                    #do the products optimizations
                    for st_pt in obj.products:
                        #do the products optimizations
                        #check for products of other reactions that are the same as this product
                        #in the case such products are found, use the same Optimize object for both
                        for i, inst_i in enumerate(self.species.reac_inst):
                            new = 1
                            if not i == index:
                                obj_i = self.species.reac_obj[i]
                                if self.species.reac_ts_done[i] > 3:
                                    for j, st_pt_i in enumerate(
                                            obj_i.products):
                                        if st_pt_i.chemid == st_pt.chemid:
                                            if len(obj_i.prod_opt) > j:
                                                prod_opt = obj_i.prod_opt[j]
                                                new = 0
                                                break
                        if new:
                            prod_opt = Optimize(st_pt, self.par, self.qc)
                            prod_opt.do_optimization()
                        obj.prod_opt.append(prod_opt)

                    for st_pt in obj.products:
                        #section where comparing products in same reaction occurs
                        if len(obj.prod_opt) > 0:
                            for j, st_pt_opt in enumerate(obj.prod_opt):
                                if st_pt.chemid == st_pt_opt.species.chemid:
                                    if len(obj.prod_opt) > j:
                                        prod_opt = obj.prod_opt[j]
                                        break

                    elog = open("energy.log", 'a')
                    for prod_opt in obj.prod_opt:
                        elog.write("prod_opt: {} |\tenergy: {}\n".format(
                            prod_opt.species.chemid, prod_opt.species.energy))
                    elog.close()

                    self.species.reac_ts_done[index] = 5
                elif self.species.reac_ts_done[index] == 5:
                    #check up on the TS and product optimizations
                    opts_done = 1
                    fails = 0
                    #check if ts is done
                    if not obj.ts_opt.shir == 1:
                        opts_done = 0
                        obj.ts_opt.do_optimization()
                    if obj.ts_opt.shigh == -999:
                        logging.info("Reaction {} ts_opt_shigh failure".format(
                            instance_name))
                        fails = 1
                    for pr_opt in obj.prod_opt:
                        if not pr_opt.shir == 1:
                            opts_done = 0
                            pr_opt.do_optimization()
                        if pr_opt.shigh == -999:
                            logging.info(
                                "Reaction {} pr_opt_shigh failure".format(
                                    instance_name))
                            fails = 1
                    if fails:
                        self.species.reac_ts_done[index] = -999
                    elif opts_done:
                        self.species.reac_ts_done[index] = 6
                elif self.species.reac_ts_done[index] == 6:
                    #Finilize the calculations

                    #continue to PES search in case a new well was found
                    if self.par.par['pes']:
                        #verify if product is monomolecular, and if it is new
                        if len(obj.products) == 1:
                            st_pt = obj.prod_opt[0].species
                            chemid = st_pt.chemid
                            energy = st_pt.energy
                            well_energy = self.species.energy
                            new_barrier_threshold = self.par.par[
                                'barrier_threshold'] - (
                                    energy - well_energy) * constants.AUtoKCAL
                            dir = os.path.dirname(os.getcwd())
                            jobs = open(dir + '/chemids',
                                        'r').read().split('\n')
                            jobs = [ji for ji in jobs]
                            if not str(chemid) in jobs:
                                #this well is new, add it to the jobs
                                while 1:
                                    try:
                                        #try to open the file and write to it
                                        pes.write_input(
                                            self.par, obj.products[0],
                                            new_barrier_threshold, dir)
                                        f = open(dir + '/chemids', 'a')
                                        f.write('{}\n'.format(chemid))
                                        f.close()
                                        break
                                    except IOError:
                                        #wait a second and try again
                                        time.sleep(1)
                                        pass

                        # copy the files of the species to an upper directory
                        frags = obj.products
                        for frag in frags:
                            filecopying.copy_to_database_folder(
                                self.species.chemid, frag.chemid, self.qc)

                    #check for wrong number of negative frequencies
                    neg_freq = 0
                    for st_pt in obj.products:
                        if any([fi < 0. for fi in st_pt.reduced_freqs]):
                            neg_freq = 1
                    if any([fi < 0. for fi in obj.ts.reduced_freqs[1:]]):
                        neg_freq = 1

                    if neg_freq:
                        logging.info('\tFound negative frequency for ' +
                                     instance_name)
                        self.species.reac_ts_done[index] = -999
                    else:
                        #the reaction search is finished
                        self.species.reac_ts_done[
                            index] = -1  # this is the success code

                        # write a temporary pes input file
                        # remove old xval and im_extent files
                        if os.path.exists('{}_xval.txt'.format(
                                self.species.chemid)):
                            os.remove('{}_xval.txt'.format(
                                self.species.chemid))
                        if os.path.exists('{}_im_extent.txt'.format(
                                self.species.chemid)):
                            os.remove('{}_im_extent.txt'.format(
                                self.species.chemid))
                        postprocess.createPESViewerInput(
                            self.species, self.qc, self.par)
                elif self.species.reac_ts_done[index] == -999:
                    if self.par.par['delete_intermediate_files'] == 1:
                        if not self.species.reac_obj[
                                index].instance_name in deleted:
                            self.delete_files(
                                self.species.reac_obj[index].instance_name)
                            deleted.append(
                                self.species.reac_obj[index].instance_name)

            alldone = 1
            for index, instance in enumerate(self.species.reac_inst):
                if any(self.species.reac_ts_done[i] >= 0
                       for i in range(len(self.species.reac_inst))):
                    alldone = 1
                    break
                else:
                    alldone = 0

            # write a small summary while running
            wr = 1
            if wr:
                f_out = open('kinbot_monitor.out', 'w')
                for index, instance in enumerate(self.species.reac_inst):
                    f_out.write('{}\t{}\t{}\n'.format(
                        self.species.reac_ts_done[index],
                        self.species.reac_step[index],
                        self.species.reac_obj[index].instance_name))
                f_out.close()
            time.sleep(1)

        s = []
        for index, instance in enumerate(self.species.reac_inst):
            obj = self.species.reac_obj[index]
            instance_name = obj.instance_name
            # Write a summary on the combinatorial exploration
            if 'combinatorial' in instance_name:
                s.append('NAME\t' + instance_name)

                # Write the bonds that were broken and formed
                s.append('BROKEN_BONDS\t' +
                         '\t'.join('[{}, {}]'.format(re[0], re[1])
                                   for re in obj.reac))
                s.append('FORMED_BONDS\t' +
                         '\t'.join('[{}, {}]'.format(pr[0], pr[1])
                                   for pr in obj.prod))

                # Populate the ts_bond_lengths dict with the values
                # of this reaction

                if self.species.reac_ts_done[index] == -1:
                    for i in range(self.species.natom - 1):
                        for j in range(i + 1, self.species.natom):
                            if self.species.bond[i][j] != obj.product_bonds[i][
                                    j]:
                                if (self.species.bond[i][j] == 0
                                        or obj.product_bonds[i][j] == 0):
                                    syms = []
                                    syms.append(self.species.atom[i])
                                    syms.append(self.species.atom[j])
                                    syms = ''.join(sorted(syms))
                                    dist = np.linalg.norm(obj.ts.geom[i] -
                                                          obj.ts.geom[j])
                                    s.append('TS_BOND_LENGTHS\t{}\t{}'.format(
                                        syms, dist))
                # write the expected inchis

                s.append('EXPECTED_INCHIS\t' +
                         '\t'.join(inchi for inchi in obj.prod_inchi))
                # get the inchis the reaction found
                if self.species.reac_ts_done[index] == -1:
                    inchis = obj.get_final_inchis()
                    s.append('FOUND_INCHIS\t' + '\t'.join(inchis))
                s.append('\n')
            with open('combinatorial.txt', 'w') as f:
                f.write('\n'.join(s) + '\n')

        logging.info("Reaction generation done!")
Ejemplo n.º 2
0
    def generate(self):
        """ 
        Creates the input for each reaction, runs them, and tests for success.
        If successful, it creates the barrier and product objects.
        It also then does the conformational search, and finally, the hindered rotor scans.
        To make the code the most efficient, all of these happen in parallel, in a sense that
        the jobs are not waiting for each other. E.g., one reaction can still be in the stage
        of TS search, while the other can be already at the hindered rotor scan. This way, 
        all cores are occupied efficiently.

        The switching between the various stages are done via the reac_ts_done variable.
        0: initiate the TS search
        1: check barrier height and errors in TS, and initiates normal mode displacement test, start the irc calculations 
        2: submit product optimization
        3: submit the frequency calculation 
        4: do the optimization of the ts and the products
        5: follow up on the optimizations
        6: finalize calculations, check for wrong number of negative frequencies
        
        If at any times the calculation fails, reac_ts_done is set to -999.
        If all steps are successful, reac_ts_done is set to -1.
        """

        if len(self.species.reac_inst) > 0:
            alldone = 1
        else:
            alldone = 0

        while alldone:
            for index, instance in enumerate(self.species.reac_inst):
                obj = self.species.reac_obj[index]
                instance_name = obj.instance_name

                # START REATION SEARCH
                if self.species.reac_ts_done[
                        index] == 0 and self.species.reac_step[index] == 0:
                    #verify after restart if search has failed in previous kinbot run
                    status = self.qc.check_qc(instance_name)
                    if status == 'error' or status == 'killed':
                        logging.info(
                            '\tRxn search failed (error or killed) for {}'.
                            format(instance_name))
                        self.species.reac_ts_done[index] = -999

                if self.species.reac_ts_done[
                        index] == 0:  # ts search is ongoing

                    if obj.scan == 0:  #don't do a scan of a bond
                        if self.species.reac_step[index] == obj.max_step + 1:
                            status = self.qc.get_qc_freq(
                                instance_name, self.species.natom)[0]
                            if status == 0:
                                self.species.reac_ts_done[index] = 1
                            elif status == -1:
                                logging.info(
                                    '\tRxn search failed for {}'.format(
                                        instance_name))
                                self.species.reac_ts_done[index] = -999
                        else:
                            self.species.reac_step[
                                index] = reac_family.carry_out_reaction(
                                    obj, self.species.reac_step[index],
                                    self.par.par['qc_command'])

                    else:  # do a bond scan
                        if self.species.reac_step[
                                index] == self.par.par['scan_step'] + 1:
                            status = self.qc.get_qc_freq(
                                instance_name, self.species.natom)[0]
                            if status == 0:
                                self.species.reac_ts_done[index] = 1
                            elif status == -1:
                                logging.info(
                                    '\tRxn search failed for {}'.format(
                                        instance_name))
                                self.species.reac_ts_done[index] = -999
                        else:
                            if self.species.reac_step[index] == 0:
                                self.species.reac_step[
                                    index] = reac_family.carry_out_reaction(
                                        obj, self.species.reac_step[index],
                                        self.par.par['qc_command'])
                            elif self.species.reac_step[index] > 0:
                                status = self.qc.check_qc(instance_name)
                                if status == 'error' or status == 'killed':
                                    logging.info(
                                        '\tRxn search failed for {}'.format(
                                            instance_name))
                                    self.species.reac_ts_done[index] = -999
                                else:
                                    err, energy = self.qc.get_qc_energy(
                                        instance_name)
                                    if err == 0:
                                        self.species.reac_scan_energy[
                                            index].append(energy)
                                        if len(self.species.
                                               reac_scan_energy[index]) > 1:
                                            if self.species.reac_scan_energy[
                                                    index][
                                                        -1] < self.species.reac_scan_energy[
                                                            index][-2]:
                                                self.species.reac_step[
                                                    index] = self.par.par[
                                                        'scan_step']
                                        self.species.reac_step[
                                            index] = reac_family.carry_out_reaction(
                                                obj,
                                                self.species.reac_step[index],
                                                self.par.par['qc_command'])

                elif self.species.reac_ts_done[index] == 1:
                    status = self.qc.check_qc(instance_name)
                    if status == 'running': continue
                    elif status == 'error':
                        logging.info(
                            '\tRxn search failed (gaussian error) for {}'.
                            format(instance_name))
                        self.species.reac_ts_done[index] = -999
                    else:
                        #check the barrier height:
                        if self.species.reac_type[
                                index] == 'R_Addition_MultipleBond':
                            sp_energy = self.qc.get_qc_energy(
                                str(self.species.chemid) + '_well_mp2')[1]
                            barrier = (self.qc.get_qc_energy(instance_name)[1]
                                       - sp_energy) * constants.AUtoKCAL
                        else:
                            sp_energy = self.qc.get_qc_energy(
                                str(self.species.chemid) + '_well')[1]
                            barrier = (self.qc.get_qc_energy(instance_name)[1]
                                       - sp_energy) * constants.AUtoKCAL
                        if barrier > self.par.par['barrier_threshold']:
                            logging.info(
                                '\tRxn barrier too high ({val}) for {name}'.
                                format(val=barrier, name=instance_name))
                            self.species.reac_ts_done[index] = -999
                        else:
                            obj.irc = IRC(
                                obj, self.par
                            )  #TODO: this doesn't seem like a good design
                            irc_status = obj.irc.check_irc()
                            if 0 in irc_status:
                                # No IRC started yet, start the IRC now
                                logging.info(
                                    '\tStarting IRC calculations for {}'.
                                    format(instance_name))
                                obj.irc.do_irc_calculations()
                            elif irc_status[0] == 'running' or irc_status[
                                    1] == 'running':
                                continue
                            else:
                                #IRC's have succesfully finished, have an error or were killed, in any case
                                #read the geometries and try to make products out of them
                                #verify which of the ircs leads back to the reactant, if any
                                prod = obj.irc.irc2stationary_pt()
                                if prod == 0:
                                    logging.info(
                                        '\t\tNo product found for {}'.format(
                                            instance_name))
                                    self.species.reac_ts_done[index] = -999
                                else:
                                    #IRC's are done
                                    obj.products = prod
                                    obj.product_bonds = prod.bond
                                    self.species.reac_ts_done[index] = 2
                elif self.species.reac_ts_done[index] == 2:
                    #identify bimolecular products and wells
                    fragments, maps = obj.products.start_multi_molecular()
                    obj.products = []
                    for i, frag in enumerate(fragments):
                        obj.products.append(frag)
                        self.qc.qc_opt(frag, frag.geom)

                    self.species.reac_ts_done[index] = 3
                elif self.species.reac_ts_done[index] == 3:
                    #wait for the optimization to finish
                    err = 0
                    for st_pt in obj.products:
                        chemid = st_pt.chemid
                        orig_geom = copy.deepcopy(st_pt.geom)
                        e, st_pt.geom = self.qc.get_qc_geom(
                            str(st_pt.chemid) + '_well', st_pt.natom)
                        if e < 0:
                            logging.info(
                                '\tProduct optimization failed for {}, product {}'
                                .format(instance_name, st_pt.chemid))
                            self.species.reac_ts_done[index] = -999
                            err = -1
                        elif e != 0:
                            err = -1
                        else:
                            e2, st_pt.energy = self.qc.get_qc_energy(
                                str(st_pt.chemid) + '_well')
                            e2, st_pt.zpe = self.qc.get_qc_zpe(
                                str(st_pt.chemid) + '_well')
                            st_pt.bond_mx()
                            st_pt.characterize(
                                0)  # not allowed to use the dimer option here
                            st_pt.calc_chemid()
                            if chemid != st_pt.chemid:
                                # product was optimized to another structure, give warning and remove this reaction
                                logging.info(
                                    '\tProduct optimizatied to other structure for {}, product {} to {}'
                                    .format(instance_name, chemid,
                                            st_pt.chemid))
                                self.species.reac_ts_done[index] = -999
                                err = -1
                    if err == 0:
                        self.species.reac_ts_done[index] = 4
                elif self.species.reac_ts_done[index] == 4:
                    # Do the TS and product optimization

                    #make a stationary point object of the ts
                    bond_mx = np.zeros(
                        (self.species.natom, self.species.natom), dtype=int)
                    for i in range(self.species.natom):
                        for j in range(self.species.natom):
                            bond_mx[i][j] = max(self.species.bond[i][j],
                                                obj.product_bonds[i][j])
                    err, geom = self.qc.get_qc_geom(instance_name,
                                                    self.species.natom)
                    ts = StationaryPoint(instance_name,
                                         self.species.charge,
                                         self.species.mult,
                                         atom=self.species.atom,
                                         geom=geom,
                                         wellorts=1)
                    err, ts.energy = self.qc.get_qc_energy(instance_name)
                    err, ts.zpe = self.qc.get_qc_zpe(instance_name)
                    ts.bond = bond_mx
                    ts.find_cycle()
                    ts.find_conf_dihedral()
                    obj.ts = ts
                    #do the ts optimization
                    obj.ts_opt = Optimize(obj.ts, self.par, self.qc)
                    obj.ts_opt.do_optimization()
                    #do the products optimizations
                    for st_pt in obj.products:
                        #check for products of other reactions that are the same as this product
                        #in the case such products are found, use the same Optimize object for both
                        new = 1
                        for i, inst_i in enumerate(self.species.reac_inst):
                            if not i == index:
                                obj_i = self.species.reac_obj[i]
                                if self.species.reac_ts_done[i] > 3:
                                    for j, st_pt_i in enumerate(
                                            obj_i.products):
                                        if st_pt_i.chemid == st_pt.chemid:
                                            if len(obj_i.prod_opt) > j:
                                                prod_opt = obj_i.prod_opt[j]
                                                new = 0
                                                break
                        if new:
                            prod_opt = Optimize(st_pt, self.par, self.qc)
                            prod_opt.do_optimization()
                        obj.prod_opt.append(prod_opt)
                    self.species.reac_ts_done[index] = 5
                elif self.species.reac_ts_done[index] == 5:
                    #check up on the TS and product optimizations
                    opts_done = 1
                    fails = 0
                    #check if ts is done
                    if not obj.ts_opt.shir == 1:
                        opts_done = 0
                        obj.ts_opt.do_optimization()
                    if obj.ts_opt.shigh == -999:
                        fails = 1
                    for pr_opt in obj.prod_opt:
                        if not pr_opt.shir == 1:
                            opts_done = 0
                            pr_opt.do_optimization()
                        if pr_opt.shigh == -999:
                            fails = 1
                    if fails:
                        self.species.reac_ts_done[index] = -999
                    elif opts_done:
                        self.species.reac_ts_done[index] = 6
                elif self.species.reac_ts_done[index] == 6:
                    #Finilize the calculations

                    #continue to PES search in case a new well was found
                    if self.par.par['pes']:
                        #verify if product is monomolecular, and if it is new
                        if len(obj.products) == 1:
                            st_pt = obj.prod_opt[0].species
                            chemid = st_pt.chemid
                            energy = st_pt.energy
                            well_energy = self.species.energy
                            new_barrier_threshold = self.par.par[
                                'barrier_threshold'] - (
                                    energy - well_energy) * constants.AUtoKCAL
                            dir = os.path.dirname(os.getcwd())
                            jobs = open(dir + '/chemids',
                                        'r').read().split('\n')
                            jobs = [ji for ji in jobs]
                            if not str(chemid) in jobs:
                                #this well is new, add it to the jobs
                                while 1:
                                    try:
                                        #try to open the file and write to it
                                        pes.write_input(
                                            self.par, obj.products[0],
                                            new_barrier_threshold, dir)
                                        f = open(dir + '/chemids', 'a')
                                        f.write('{}\n'.format(chemid))
                                        f.close()
                                        break
                                    except IOError:
                                        #wait a second and try again
                                        time.sleep(1)
                                        pass

                    #check for wrong number of negative frequencies
                    neg_freq = 0
                    for st_pt in obj.products:
                        if any([fi < 0. for fi in st_pt.reduced_freqs]):
                            neg_freq = 1
                    if any([fi < 0. for fi in obj.ts.reduced_freqs[1:]]):
                        neg_freq = 1

                    if neg_freq:
                        logging.info('\tFound negative frequency for ' +
                                     instance_name)
                        self.species.reac_ts_done[index] = -999
                    else:
                        #the reaction search is finished
                        self.species.reac_ts_done[
                            index] = -1  # this is the success code

                        # write a temporary pes input file
                        # remove old xval and im_extent files
                        if os.path.exists('{}_xval.txt'.format(
                                self.species.chemid)):
                            os.remove('{}_xval.txt'.format(
                                self.species.chemid))
                        if os.path.exists('{}_im_extent.txt'.format(
                                self.species.chemid)):
                            os.remove('{}_im_extent.txt'.format(
                                self.species.chemid))
                        postprocess.createPESViewerInput(
                            self.species, self.qc, self.par)

            alldone = 1
            for index, instance in enumerate(self.species.reac_inst):
                if any(self.species.reac_ts_done[i] >= 0
                       for i in range(len(self.species.reac_inst))):
                    alldone = 1
                    break
                else:
                    alldone = 0

            # write a small summary while running
            wr = 1
            if wr:
                f_out = open('kinbot_monitor.out', 'w')
                for index, instance in enumerate(self.species.reac_inst):
                    f_out.write('{}\t{}\t{}\n'.format(
                        self.species.reac_ts_done[index],
                        self.species.reac_step[index],
                        self.species.reac_obj[index].instance_name))
                f_out.close()
            time.sleep(1)

        s = []
        for index, instance in enumerate(self.species.reac_inst):
            obj = self.species.reac_obj[index]
            instance_name = obj.instance_name
            # Write a summary on the combinatorial exploration
            if 'combinatorial' in instance_name:
                s.append('NAME\t' + instance_name)

                # Write the bonds that were broken and formed
                s.append('BROKEN_BONDS\t' +
                         '\t'.join('[{}, {}]'.format(re[0], re[1])
                                   for re in obj.reac))
                s.append('FORMED_BONDS\t' +
                         '\t'.join('[{}, {}]'.format(pr[0], pr[1])
                                   for pr in obj.prod))

                # Populate the ts_bond_lengths dict with the values
                # of this reaction
                if self.species.reac_ts_done[index] == -1:
                    for i in range(self.species.natom - 1):
                        for j in range(i + 1, self.species.natom):
                            if self.species.bond[i][j] != obj.product_bonds[i][
                                    j]:
                                if (self.species.bond[i][j] == 0
                                        or obj.product_bonds[i][j] == 0):
                                    syms = []
                                    syms.append(self.species.atom[i])
                                    syms.append(self.species.atom[j])
                                    syms = ''.join(sorted(syms))
                                    dist = np.linalg.norm(obj.ts.geom[i] -
                                                          obj.ts.geom[j])
                                    s.append('TS_BOND_LENGTHS\t{}\t{}'.format(
                                        syms, dist))
                # write the expected inchis
                s.append('EXPECTED_INCHIS\t' +
                         '\t'.join(inchi for inchi in obj.prod_inchi))
                # get the inchis the reaction found
                if self.species.reac_ts_done[index] == -1:
                    inchis = obj.get_final_inchis()
                    s.append('FOUND_INCHIS\t' + '\t'.join(inchis))
                s.append('\n')
            with open('combinatorial.txt', 'w') as f:
                f.write('\n'.join(s) + '\n')

        logging.info("Reaction generation done!")
Ejemplo n.º 3
0
    def generate(self):
        """
        Creates the input for each reaction, runs them, and tests for success.
        If successful, it creates the barrier and product objects.
        It also then does the conformational search, and finally, the hindered rotor scans.
        To make the code the most efficient, all of these happen in parallel, in a sense that
        the jobs are not waiting for each other. E.g., one reaction can still be in the stage
        of TS search, while the other can be already at the hindered rotor scan. This way,
        all cores are occupied efficiently.

        The switching between the various stages are done via the reac_ts_done variable.
        0: initiate the TS search
        1: check barrier height and errors in TS, and initiates normal mode displacement test, start the irc calculations
        2: submit product optimization
        3: submit the frequency calculation
        4: do the optimization of the ts and the products
        5: follow up on the optimizations
        6: finalize calculations, check for wrong number of negative frequencies

        If at any times the calculation fails, reac_ts_done is set to -999.
        If all steps are successful, reac_ts_done is set to -1.
        """
        deleted = []
        if len(self.species.reac_inst) > 0:
            alldone = 1
        else:
            alldone = 0

        # status to see of kinbot needs to wait for the product optimizations
        # from another kinbot run, to avoid duplication of calculations
        products_waiting_status = [[] for i in self.species.reac_inst]
        count = 0
        for i in self.species.reac_inst:
            count = count + 1
        frag_unique = []

        while alldone:
            for index, instance in enumerate(self.species.reac_inst):
                obj = self.species.reac_obj[index]
                # START REACTION SEARCH
                if self.species.reac_ts_done[
                        index] == 0 and self.species.reac_step[index] == 0:
                    # verify after restart if search has failed in previous kinbot run
                    status = self.qc.check_qc(obj.instance_name)
                    if status == 'error' or status == 'killed':
                        logging.info(
                            '\tRxn search failed (error or killed) for {}'.
                            format(obj.instance_name))
                        self.species.reac_ts_done[index] = -999
                if self.species.reac_type[
                        index] == 'hom_sci' and self.species.reac_ts_done[
                            index] == 0:  # no matter what, set to 2
                    # somewhat messy manipulation to force the new bond matrix for hom_sci
                    obj.products = copy.deepcopy(obj.species)
                    obj.products.bonds = copy.deepcopy(
                        obj.species.bond)  # plural/non plural!
                    obj.products.bonds[obj.instance[0]][
                        obj.instance[1]] = 0  # delete bond
                    obj.products.bonds[obj.instance[1]][
                        obj.instance[0]] = 0  # delete bond
                    obj.products.bond[obj.instance[0]][
                        obj.instance[1]] = 0  # delete bond
                    obj.products.bond[obj.instance[1]][
                        obj.instance[0]] = 0  # delete bond
                    obj.product_bonds = copy.deepcopy(
                        obj.species.bonds[0])  # the first resonance structure
                    obj.product_bonds[obj.instance[0]][
                        obj.instance[1]] = 0  # delete bond
                    obj.product_bonds[obj.instance[1]][
                        obj.instance[0]] = 0  # delete bond
                    self.species.reac_ts_done[index] = 2
                if self.species.reac_ts_done[
                        index] == 0:  # ts search is ongoing
                    if obj.scan == 0:  # don't do a scan of a bond
                        if self.species.reac_step[index] == obj.max_step + 1:
                            status, freq = self.qc.get_qc_freq(
                                obj.instance_name, self.species.natom)
                            if status == 0 and freq[0] < 0. and freq[1] > 0.:
                                self.species.reac_ts_done[index] = 1
                            elif status == 0 and freq[0] > 0.:
                                logging.info(
                                    '\tRxn search failed for {}, no imaginary freq.'
                                    .format(obj.instance_name))
                                self.species.reac_ts_done[index] = -999
                            elif status == 0 and freq[1] < 0.:
                                logging.info(
                                    '\tRxn search failed for {}, more than one imaginary freq.'
                                    .format(obj.instance_name))
                                self.species.reac_ts_done[index] = -999
                            elif status == -1:
                                logging.info(
                                    '\tRxn search failed for {}'.format(
                                        obj.instance_name))
                                self.species.reac_ts_done[index] = -999
                        else:
                            self.species.reac_step[
                                index] = reac_family.carry_out_reaction(
                                    obj, self.species.reac_step[index],
                                    self.par['qc_command'])

                    else:  # do a bond scan
                        if self.species.reac_step[
                                index] == self.par['scan_step'] + 1:
                            status, freq = self.qc.get_qc_freq(
                                obj.instance_name, self.species.natom)
                            if status == 0 and freq[0] < 0. and freq[1] > 0.:
                                self.species.reac_ts_done[index] = 1
                            elif status == 0 and freq[0] > 0.:
                                logging.info(
                                    '\tRxn search failed for {}, no imaginary freq.'
                                    .format(obj.instance_name))
                                self.species.reac_ts_done[index] = -999
                            elif status == 0 and freq[1] < 0.:
                                logging.info(
                                    '\tRxn search failed for {}, more than one imaginary freq.'
                                    .format(obj.instance_name))
                                self.species.reac_ts_done[index] = -999
                            elif status == -1:
                                logging.info(
                                    '\tRxn search using scan failed for {} in TS optimization stage.'
                                    .format(obj.instance_name))
                                self.species.reac_ts_done[index] = -999
                        else:
                            if self.species.reac_step[index] == 0:
                                self.species.reac_step[
                                    index] = reac_family.carry_out_reaction(
                                        obj, self.species.reac_step[index],
                                        self.par['qc_command'])
                            elif self.species.reac_step[index] < self.par[
                                    'scan_step']:
                                status = self.qc.check_qc(obj.instance_name)
                                if status == 'error' or status == 'killed':
                                    logging.info(
                                        '\tRxn search using scan failed for {} in step {}'
                                        .format(obj.instance_name,
                                                self.species.reac_step[index]))
                                    self.species.reac_ts_done[index] = -999
                                else:
                                    err, energy = self.qc.get_qc_energy(
                                        obj.instance_name)
                                    if err == 0:
                                        self.species.reac_scan_energy[
                                            index].append(energy)
                                        # need at least 3 points for a maximum
                                        if len(self.species.
                                               reac_scan_energy[index]) >= 3:
                                            ediff = np.diff(
                                                self.species.
                                                reac_scan_energy[index])
                                            if ediff[-1] < 0 and ediff[
                                                    -2] > 0:  # max
                                                self.species.reac_step[
                                                    index] = self.par[
                                                        'scan_step']  # ending the scan
                                            if len(ediff) >= 3:
                                                if 10. * (
                                                        ediff[-3] / ediff[-2]
                                                ) < (
                                                        ediff[-2] / ediff[-1]
                                                ):  # sudden change in slope
                                                    self.species.reac_step[
                                                        index] = self.par[
                                                            'scan_step']  # ending the scan
                                        logging.info(
                                            '\tCurrent raw scan energy for {}: {} Hartree.'
                                            .format(
                                                obj.instance_name,
                                                self.species.
                                                reac_scan_energy[index][-1]))
                                        # scan continues, and if reached scan_step, then goes for full optimization
                                        self.species.reac_step[
                                            index] = reac_family.carry_out_reaction(
                                                obj,
                                                self.species.reac_step[index],
                                                self.par['qc_command'])
                            else:  # the last step was reached, and no max or inflection was found
                                logging.info(
                                    '\tRxn search using scan failed for {}, no saddle guess found.'
                                    .format(obj.instance_name))
                                db = connect('{}/kinbot.db'.format(
                                    os.getcwd()))
                                rows = db.select(name=obj.instance_name)
                                for row in self.reversed_iterator(rows):
                                    row.data['status'] = 'error'
                                    break  # only write error to the last calculation
                                self.species.reac_ts_done[index] = -999

                elif self.species.reac_ts_done[index] == 1:
                    status = self.qc.check_qc(obj.instance_name)
                    if status == 'running':
                        continue
                    elif status == 'error':
                        logging.info(
                            '\tRxn search failed (gaussian error) for {}'.
                            format(obj.instance_name))
                        self.species.reac_ts_done[index] = -999
                    else:
                        # check the barrier height:
                        ts_energy = self.qc.get_qc_energy(obj.instance_name)[1]
                        ts_zpe = self.qc.get_qc_zpe(obj.instance_name)[1]
                        if self.species.reac_type[
                                index] == 'R_Addition_MultipleBond':
                            ending = 'well_mp2'
                        elif self.species.reac_type[
                                index] == 'barrierless_saddle':
                            ending = 'well_bls'
                        else:
                            ending = 'well'
                        sp_energy = self.qc.get_qc_energy('{}_{}'.format(
                            str(self.species.chemid), ending))[1]
                        sp_zpe = self.qc.get_qc_zpe('{}_{}'.format(
                            str(self.species.chemid), ending))[1]
                        try:
                            barrier = (ts_energy + ts_zpe - sp_energy -
                                       sp_zpe) * constants.AUtoKCAL
                        except TypeError:
                            logging.error(
                                f'Faulty calculations, check or delete files for {obj.instance_name}.'
                            )
                            sys.exit(-1)
                        if barrier > self.par['barrier_threshold']:
                            logging.info(
                                '\tRxn barrier too high ({0:.2f} kcal/mol) for {1}'
                                .format(barrier, obj.instance_name))
                            self.species.reac_ts_done[index] = -999
                        else:
                            obj.irc = IRC(
                                obj, self.par
                            )  # TODO: this doesn't seem like a good design
                            irc_status = obj.irc.check_irc()
                            if 0 in irc_status:
                                logging.info(
                                    '\tRxn barrier is {0:.2f} kcal/mol for {1}'
                                    .format(barrier, obj.instance_name))
                                # No IRC started yet, start the IRC now
                                logging.info(
                                    '\tStarting IRC calculations for {}'.
                                    format(obj.instance_name))
                                obj.irc.do_irc_calculations()
                            elif irc_status[0] == 'running' or irc_status[
                                    1] == 'running':
                                continue
                            else:
                                # IRC's have successfully finished, have an error or were killed, in any case
                                # read the geometries and try to make products out of them
                                # verify which of the ircs leads back to the reactant, if any
                                prod = obj.irc.irc2stationary_pt()
                                if prod == 0:
                                    logging.info(
                                        '\t\tNo product found for {}'.format(
                                            obj.instance_name))
                                    self.species.reac_ts_done[index] = -999
                                else:
                                    obj.products = prod
                                    obj.product_bonds = prod.bond
                                    self.species.reac_ts_done[index] = 2

                elif self.species.reac_ts_done[index] == 2:
                    if len(products_waiting_status[index]) == 0:
                        # identify bimolecular products and wells
                        fragments, maps = obj.products.start_multi_molecular()
                        obj.products = []

                        a = []
                        for frag in fragments:
                            a.append(frag)
                            if len(frag_unique) == 0:
                                frag_unique.append(frag)
                            elif len(frag_unique) > 0:
                                new = 1
                                for fragb in frag_unique:
                                    if frag.chemid == fragb.chemid:
                                        e, geom2 = self.qc.get_qc_geom(
                                            str(fragb.chemid) + '_well',
                                            fragb.natom)
                                        if e == 0:
                                            a.pop()
                                            frag = fragb
                                            a.append(frag)
                                            new = 0
                                            break
                                if new:
                                    frag_unique.append(frag)
                        obj.products_final = []
                        for frag in a:
                            self.qc.qc_opt(frag, frag.geom)
                            e, geom2 = self.qc.get_qc_geom(
                                str(frag.chemid) + '_well', frag.natom)
                            obj.products_final.append(frag)

                        # check products make sure they are the same
                        for i, st_pt_i in enumerate(obj.products_final):
                            for j, st_pt_j in enumerate(obj.products_final):
                                if st_pt_i.chemid == st_pt_j.chemid and i < j:
                                    obj.products_final[j] = obj.products_final[
                                        i]

                    # print products generated by IRC
                    products = []
                    for i, st_pt in enumerate(obj.products_final):
                        products.append(st_pt.chemid)

                    products.extend([' ', ' ', ' '])
                    logging.info(
                        '\tReaction {} leads to products {} {} {}'.format(
                            obj.instance_name, products[0], products[1],
                            products[2]))

                    for i, st_pt in enumerate(obj.products_final):
                        chemid = st_pt.chemid
                        e, st_pt.geom = self.qc.get_qc_geom(
                            str(st_pt.chemid) + '_well', st_pt.natom)
                        if e < 0:
                            logging.info(
                                '\tProduct optimization failed for {}, product {}'
                                .format(obj.instance_name, st_pt.chemid))
                            self.species.reac_ts_done[index] = -999
                            err = -1
                        elif e != 0:
                            err = -1
                        else:
                            e2, st_pt.energy = self.qc.get_qc_energy(
                                str(st_pt.chemid) + '_well')
                            e2, st_pt.zpe = self.qc.get_qc_zpe(
                                str(st_pt.chemid) + '_well')
                            st_pt.characterize(
                                dimer=0
                            )  # not allowed to use the dimer option here
                            if chemid != st_pt.chemid:
                                obj.products_final.pop(i)
                                newfrags, newmaps = st_pt.start_multi_molecular(
                                )  # newfrags is list of stpt obj
                                products_waiting_status[index] = [
                                    0 for frag in newfrags
                                ]
                                frag_chemid = []
                                for i, newfr in enumerate(newfrags):
                                    newfr.characterize(dimer=0)
                                    for prod in frag_unique:
                                        if newfr.chemid == prod.chemid:
                                            newfrags.pop(i)
                                            newfr = prod
                                            j = i - 1
                                            newfrags.insert(j, newfr)
                                    j = i - 1
                                    obj.products_final.insert(j, newfr)
                                    self.qc.qc_opt(newfr, newfr.geom, 0)
                                    frag_chemid.append(newfr.chemid)
                                if len(frag_chemid) == 1:
                                    frag_chemid.append(" ")
                                for i, frag in enumerate(newfrags):
                                    products_waiting_status[index][i] = 1
                                logging.info(
                                    '\ta) Product optimized to other structure for {}'
                                    ', product {} to {} {}'.format(
                                        obj.instance_name, chemid,
                                        frag_chemid[0], frag_chemid[1]))

                    obj.products = []
                    for prod in obj.products_final:
                        obj.products.append(prod)
                    obj.products_final = []

                    if all([pi == 1 for pi in products_waiting_status[index]]):
                        self.species.reac_ts_done[index] = 3

                elif self.species.reac_ts_done[index] == 3:
                    # wait for the optimization to finish
                    # if two st_pt are the same in the products, we make them exactly identical otherwise
                    # the different ordering of the atoms causes the chemid of the second to be seemingly wrong
                    for i, st_pt_i in enumerate(obj.products):
                        for j, st_pt_j in enumerate(obj.products):
                            if st_pt_i.chemid == st_pt_j.chemid and i < j:
                                obj.products[j] = obj.products[i]

                    err = 0
                    for st_pt in obj.products:
                        chemid = st_pt.chemid
                        e, st_pt.geom = self.qc.get_qc_geom(
                            str(st_pt.chemid) + '_well', st_pt.natom)
                        if e < 0:
                            logging.info(
                                '\tProduct optimization failed for {}, product {}'
                                .format(obj.instance_name, st_pt.chemid))
                            self.species.reac_ts_done[index] = -999
                            err = -1
                        elif e != 0:
                            err = -1
                        else:
                            e2, st_pt.energy = self.qc.get_qc_energy(
                                str(st_pt.chemid) + '_well')
                            e2, st_pt.zpe = self.qc.get_qc_zpe(
                                str(st_pt.chemid) + '_well')
                            st_pt.characterize(
                                dimer=0
                            )  # not allowed to use the dimer option here
                            if chemid != st_pt.chemid:
                                # product was optimized to another structure, give warning but don't remove reaction
                                logging.info(
                                    '\tb) Product optimized to other structure for {}'
                                    ', product {} to {}'.format(
                                        obj.instance_name, chemid,
                                        st_pt.chemid))
                                e, st_pt.geom = self.qc.get_qc_geom(
                                    str(st_pt.chemid) + '_well', st_pt.natom)
                                if e < 0:
                                    err = -1
                    if err == 0:
                        self.species.reac_ts_done[index] = 4
                elif self.species.reac_ts_done[index] == 4:
                    # Do the TS and product optimization
                    # make a stationary point object of the ts
                    bond_mx = np.zeros(
                        (self.species.natom, self.species.natom), dtype=int)
                    for i in range(self.species.natom):
                        for j in range(self.species.natom):
                            bond_mx[i][j] = max(self.species.bond[i][j],
                                                obj.product_bonds[i][j])

                    if self.species.reac_type[index] != 'hom_sci':
                        err, geom = self.qc.get_qc_geom(
                            obj.instance_name, self.species.natom)
                        ts = StationaryPoint(obj.instance_name,
                                             self.species.charge,
                                             self.species.mult,
                                             atom=self.species.atom,
                                             geom=geom,
                                             wellorts=1)
                        err, ts.energy = self.qc.get_qc_energy(
                            obj.instance_name)
                        err, ts.zpe = self.qc.get_qc_zpe(
                            obj.instance_name)  # NEW STOPS HERE
                        err, ts.freq = self.qc.get_qc_freq(
                            obj.instance_name, self.species.natom)
                        ts.distance_mx()
                        ts.bond = bond_mx
                        ts.find_cycle()
                        ts.find_conf_dihedral()
                        obj.ts = ts
                        # do the ts optimization
                        obj.ts_opt = Optimize(obj.ts, self.par, self.qc)
                        obj.ts_opt.do_optimization()
                    else:
                        obj.ts = copy.deepcopy(
                            obj.species
                        )  # the TS will be for now the species itself
                        obj.ts.wellorts = 1

                    # do the products optimizations
                    for st_pt in obj.products:
                        # do the products optimizations
                        # check for products of other reactions that are the same as this product
                        # in the case such products are found, use the same Optimize object for both
                        for i, inst_i in enumerate(self.species.reac_inst):
                            new = 1
                            if not i == index:
                                obj_i = self.species.reac_obj[i]
                                if self.species.reac_ts_done[i] > 3:
                                    for j, st_pt_i in enumerate(
                                            obj_i.products):
                                        if st_pt_i.chemid == st_pt.chemid:
                                            if len(obj_i.prod_opt) > j:
                                                prod_opt = obj_i.prod_opt[j]
                                                new = 0
                                                break
                        if new:
                            prod_opt = Optimize(st_pt, self.par, self.qc)
                            prod_opt.do_optimization()
                        obj.prod_opt.append(prod_opt)

                    for st_pt in obj.products:
                        # section where comparing products in same reaction occurs
                        if len(obj.prod_opt) > 0:
                            for j, st_pt_opt in enumerate(obj.prod_opt):
                                if st_pt.chemid == st_pt_opt.species.chemid:
                                    if len(obj.prod_opt) > j:
                                        prod_opt = obj.prod_opt[j]
                                        break

                    self.species.reac_ts_done[index] = 5

                elif self.species.reac_ts_done[index] == 5:
                    # check up on the TS and product optimizations
                    opts_done = 1
                    fails = 0
                    # check if ts is done
                    if self.species.reac_type[index] != 'hom_sci':
                        if not obj.ts_opt.shir == 1:  # last stage in optimize
                            opts_done = 0
                            obj.ts_opt.do_optimization()
                        if obj.ts_opt.shigh == -999:
                            logging.info(
                                "Reaction {} ts_opt_shigh failure".format(
                                    obj.instance_name))
                            fails = 1
                    for pr_opt in obj.prod_opt:
                        if not pr_opt.shir == 1:
                            opts_done = 0
                            pr_opt.do_optimization()
                        if pr_opt.shigh == -999:
                            logging.info(
                                "Reaction {} pr_opt_shigh failure".format(
                                    obj.instance_name))
                            fails = 1
                        break
                    if fails:
                        self.species.reac_ts_done[index] = -999
                    elif opts_done:
                        self.species.reac_ts_done[index] = 6
                elif self.species.reac_ts_done[index] == 6:
                    # Finilize the calculations
                    # continue to PES search in case a new well was found
                    if self.par['pes']:
                        # verify if product is monomolecular, and if it is new
                        if len(obj.products) == 1:
                            st_pt = obj.prod_opt[0].species
                            chemid = st_pt.chemid
                            new_barrier_threshold = self.par[
                                'barrier_threshold'] - (
                                    st_pt.energy -
                                    self.species.energy) * constants.AUtoKCAL
                            dirwell = os.path.dirname(os.getcwd())
                            jobs = open(dirwell + '/chemids',
                                        'r').read().split('\n')
                            jobs = [ji for ji in jobs]
                            if not str(chemid) in jobs:
                                # this well is new, add it to the jobs
                                while 1:
                                    try:
                                        # try to open the file and write to it
                                        pes.write_input(
                                            self.inp, obj.products[0],
                                            new_barrier_threshold, dirwell)
                                        with open(dirwell + '/chemids',
                                                  'a') as f:
                                            f.write('{}\n'.format(chemid))
                                        break
                                    except IOError:
                                        # wait a second and try again
                                        time.sleep(1)
                                        pass

                        # copy the files of the species to an upper directory
                        frags = obj.products
                        for frag in frags:
                            filecopying.copy_to_database_folder(
                                self.species.chemid, frag.chemid, self.qc)

                    # check for wrong number of negative frequencies
                    neg_freq = 0
                    for st_pt in obj.products:
                        if any([fi < 0. for fi in st_pt.reduced_freqs]):
                            neg_freq = 1
                    if any([fi < 0. for fi in obj.ts.reduced_freqs[1:]]):
                        neg_freq = 1

                    if neg_freq:
                        logging.info('\tFound negative frequency for ' +
                                     obj.instance_name)
                        self.species.reac_ts_done[index] = -999
                    else:
                        # the reaction search is finished
                        self.species.reac_ts_done[
                            index] = -1  # this is the success code

                        # write a temporary pes input file
                        # remove old xval and im_extent files
                        if os.path.exists('{}_xval.txt'.format(
                                self.species.chemid)):
                            os.remove('{}_xval.txt'.format(
                                self.species.chemid))
                        if os.path.exists('{}_im_extent.txt'.format(
                                self.species.chemid)):
                            os.remove('{}_im_extent.txt'.format(
                                self.species.chemid))
                        postprocess.createPESViewerInput(
                            self.species, self.qc, self.par)
                elif self.species.reac_ts_done[index] == -999:
                    if self.par['delete_intermediate_files'] == 1:
                        if not self.species.reac_obj[
                                index].instance_name in deleted:
                            self.delete_files(
                                self.species.reac_obj[index].instance_name)
                            deleted.append(
                                self.species.reac_obj[index].instance_name)

            alldone = 1
            for index, instance in enumerate(self.species.reac_inst):
                if any(self.species.reac_ts_done[i] >= 0
                       for i in range(len(self.species.reac_inst))):
                    alldone = 1
                    break
                else:
                    alldone = 0

            # write a small summary while running
            with open('kinbot_monitor.out', 'w') as f_out:
                for index, instance in enumerate(self.species.reac_inst):
                    if self.species.reac_ts_done[index] == -1:
                        prodstring = []
                        for pp in self.species.reac_obj[index].products:
                            prodstring.append(str(pp.chemid))
                        f_out.write('{}\t{}\t{}\t{}\n'.format(
                            self.species.reac_ts_done[index],
                            self.species.reac_step[index],
                            self.species.reac_obj[index].instance_name,
                            ' '.join(prodstring)))
                    else:
                        f_out.write('{}\t{}\t{}\n'.format(
                            self.species.reac_ts_done[index],
                            self.species.reac_step[index],
                            self.species.reac_obj[index].instance_name))
            time.sleep(1)

        # Create molpro file for the BLS products
        for index, instance in enumerate(self.species.reac_inst):
            if self.species.reac_type[index] == 'barrierless_saddle':
                obj = self.species.reac_obj[index]
                if len(obj.products) == 2:
                    blsprodatom = np.append(obj.products[0].atom,
                                            obj.products[1].atom)
                    # vector connecting the centers
                    # assumed: the two structures are naturally separated at the end of the IRC
                    x0 = 0
                    y0 = 0
                    z0 = 0
                    for g in obj.products[0].geom:
                        x0 += g[0]
                        y0 += g[1]
                        z0 += g[1]
                    x0 /= obj.products[0].natom
                    y0 /= obj.products[0].natom
                    z0 /= obj.products[0].natom

                    x1 = 0
                    y1 = 0
                    z1 = 0
                    for g in obj.products[1].geom:
                        x1 += g[0]
                        y1 += g[1]
                        z1 += g[1]
                    x1 /= obj.products[1].natom
                    y1 /= obj.products[1].natom
                    z1 /= obj.products[1].natom

                    center_vec = [x1 - x0, y1 - y0, z1 - z0]
                    blsprodgeom = np.append(obj.products[0].geom,
                                            obj.products[1].geom)
                    blsprodgeom = np.reshape(blsprodgeom, (-1, 3))
                    bps = list(zip(blsprodatom, blsprodgeom))
                    bps1 = [item for sublist in bps for item in sublist]
                    blsprodstruct = [
                        item for sublist in bps1 for item in sublist
                    ]
                    blsprod = StationaryPoint('blsprod',
                                              self.par['charge'],
                                              self.par['mult'],
                                              structure=blsprodstruct)
                    blsprod.characterize()
                    molp = Molpro(blsprod, self.par)
                    molpname = '{}_prod'.format(obj.instance_name)
                    molp.create_molpro_input(bls=1,
                                             name=molpname,
                                             shift_vec=center_vec,
                                             natom1=len(obj.products[1].geom))
                    molp.create_molpro_submit(name=molpname)

        s = []
        for index, instance in enumerate(self.species.reac_inst):
            obj = self.species.reac_obj[index]
            # Write a summary on the combinatorial exploration
            if 'combinatorial' in obj.instance_name:
                s.append('NAME\t' + obj.instance_name)

                # Write the bonds that were broken and formed
                s.append('BROKEN_BONDS\t' +
                         '\t'.join('[{}, {}]'.format(re[0], re[1])
                                   for re in obj.reac))
                s.append('FORMED_BONDS\t' +
                         '\t'.join('[{}, {}]'.format(pr[0], pr[1])
                                   for pr in obj.prod))

                # Populate the ts_bond_lengths dict with the values
                # of this reaction

                if self.species.reac_ts_done[index] == -1:
                    for i in range(self.species.natom - 1):
                        for j in range(i + 1, self.species.natom):
                            if self.species.bond[i][j] != obj.product_bonds[i][
                                    j]:
                                if (self.species.bond[i][j] == 0
                                        or obj.product_bonds[i][j] == 0):
                                    syms = []
                                    syms.append(self.species.atom[i])
                                    syms.append(self.species.atom[j])
                                    syms = ''.join(sorted(syms))
                                    dist = np.linalg.norm(obj.ts.geom[i] -
                                                          obj.ts.geom[j])
                                    s.append('TS_BOND_LENGTHS\t{}\t{}'.format(
                                        syms, dist))
                # write the expected inchis

                s.append('EXPECTED_INCHIS\t' +
                         '\t'.join(inchi for inchi in obj.prod_inchi))
                # get the inchis the reaction found
                if self.species.reac_ts_done[index] == -1:
                    inchis = obj.get_final_inchis()
                    s.append('FOUND_INCHIS\t' + '\t'.join(inchis))
                s.append('\n')
            with open('combinatorial.txt', 'w') as f:
                f.write('\n'.join(s) + '\n')

        logging.info("Reaction generation done!")
Ejemplo n.º 4
0
    def generate(self):
        """ 
        Creates the input for each reaction, runs them, and tests for success.
        If successful, it creates the barrier and product objects.
        It also then does the conformational search, and finally, the hindered rotor scans.
        To make the code the most efficient, all of these happen in parallel, in a sense that
        the jobs are not waiting for each other. E.g., one reaction can still be in the stage
        of TS search, while the other can be already at the hindered rotor scan. This way, 
        all cores are occupied efficiently.

        The switching between the various stages are done via the reac_ts_done variable.
        0: initiate the TS search
        1: check barrier height and errors in TS, and initiates normal mode displacement test, start the irc calculations 
        2: submit product optimization
        3: submit the frequency calculation 
        4: do the optimization of the ts and the products
        5: follow up on the optimizations
        6: finalize calculations, check for wrong number of negative frequencies
        
        If at any times the calculation fails, reac_ts_done is set to -999.
        If all steps are successful, reac_ts_done is set to -1.
        """
        deleted = []
        if len(self.species.reac_inst) > 0:
            alldone = 1
        else: 
            alldone = 0

        while alldone:
            for index, instance in enumerate(self.species.reac_inst):
                obj = self.species.reac_obj[index]
                instance_name = obj.instance_name

                # START REATION SEARCH
                if self.species.reac_ts_done[index] == 0 and self.species.reac_step[index] == 0:
                    #verify after restart if search has failed in previous kinbot run
                    status = self.qc.check_qc(instance_name)
                    if status == 'error' or status == 'killed':
                        logging.info('\tRxn search failed (error or killed) for {}'.format(instance_name))
                        self.species.reac_ts_done[index] = -999
                
                if self.species.reac_ts_done[index] == 0: # ts search is ongoing
                    
                    if obj.scan == 0: #don't do a scan of a bond
                        if self.species.reac_step[index] == obj.max_step + 1:
                            status = self.qc.get_qc_freq(instance_name, self.species.natom)[0]
                            if status == 0:
                                self.species.reac_ts_done[index] = 1
                            elif status == -1:
                                logging.info('\tRxn search failed for {}'.format(instance_name))
                                self.species.reac_ts_done[index] = -999
                        else: 
                            self.species.reac_step[index] = reac_family.carry_out_reaction(obj, self.species.reac_step[index], self.par.par['qc_command'])
                    
                    else: # do a bond scan
                        if self.species.reac_step[index] == self.par.par['scan_step'] + 1:
                            status = self.qc.get_qc_freq(instance_name, self.species.natom)[0]
                            if status == 0:
                                self.species.reac_ts_done[index] = 1
                            elif status == -1:
                                logging.info('\tRxn search failed for {}'.format(instance_name))
                                self.species.reac_ts_done[index] = -999
                        else:        
                            if self.species.reac_step[index] == 0:
                                self.species.reac_step[index] = reac_family.carry_out_reaction(obj, self.species.reac_step[index], self.par.par['qc_command'])
                            elif self.species.reac_step[index] > 0:
                                status = self.qc.check_qc(instance_name)
                                if status == 'error' or status == 'killed':
                                    logging.info('\tRxn search failed for {}'.format(instance_name))
                                    self.species.reac_ts_done[index] = -999
                                else:
                                    err, energy = self.qc.get_qc_energy(instance_name)
                                    if err == 0:
                                        self.species.reac_scan_energy[index].append(energy)
                                        if len(self.species.reac_scan_energy[index]) > 1:
                                            if self.species.reac_scan_energy[index][-1] < self.species.reac_scan_energy[index][-2]:
                                                self.species.reac_step[index] = self.par.par['scan_step'] 
                                        self.species.reac_step[index] = reac_family.carry_out_reaction(obj, self.species.reac_step[index], self.par.par['qc_command'])

                elif self.species.reac_ts_done[index] == 1:
                    status = self.qc.check_qc(instance_name)
                    if status == 'running': continue
                    elif status == 'error': 
                        logging.info('\tRxn search failed (gaussian error) for {}'.format(instance_name))
                        self.species.reac_ts_done[index] = -999
                    else: 
                        #check the barrier height:
                        if self.species.reac_type[index] == 'R_Addition_MultipleBond':
                            sp_energy = self.qc.get_qc_energy(str(self.species.chemid) + '_well_mp2')[1]
                            barrier = (self.qc.get_qc_energy(instance_name)[1] - sp_energy) * constants.AUtoKCAL
                        else:
                            sp_energy = self.qc.get_qc_energy(str(self.species.chemid) + '_well')[1]
                            barrier = (self.qc.get_qc_energy(instance_name)[1] - sp_energy) * constants.AUtoKCAL
                        if barrier > self.par.par['barrier_threshold']:
                            logging.info('\tRxn barrier too high ({val}) for {name}'.format(val=barrier,name=instance_name))
                            self.species.reac_ts_done[index] = -999
                        else:
                            obj.irc = IRC(obj, self.par) #TODO: this doesn't seem like a good design
                            irc_status = obj.irc.check_irc()
                            if 0 in irc_status:
                                # No IRC started yet, start the IRC now
                                logging.info('\tStarting IRC calculations for {}'.format(instance_name))
                                obj.irc.do_irc_calculations()
                            elif irc_status[0] == 'running' or irc_status[1] == 'running':
                                continue
                            else: 
                                #IRC's have succesfully finished, have an error or were killed, in any case
                                #read the geometries and try to make products out of them
                                #verify which of the ircs leads back to the reactant, if any
                                prod = obj.irc.irc2stationary_pt()
                                if prod == 0:
                                    logging.info('\t\tNo product found for {}'.format(instance_name))
                                    self.species.reac_ts_done[index] = -999
                                else:
                                    #IRC's are done
                                    obj.products = prod
                                    obj.product_bonds = prod.bond
                                    self.species.reac_ts_done[index] = 2
                elif self.species.reac_ts_done[index] == 2:
                    #identify bimolecular products and wells
                    fragments, maps = obj.products.start_multi_molecular()
                    obj.products = []
                    for i, frag in enumerate(fragments):
                        obj.products.append(frag)
                        self.qc.qc_opt(frag, frag.geom)
                    
                    self.species.reac_ts_done[index] = 3
                elif self.species.reac_ts_done[index] == 3:
                    #wait for the optimization to finish 
                    err = 0
                    for st_pt in obj.products:
                        chemid = st_pt.chemid
                        orig_geom = copy.deepcopy(st_pt.geom)
                        e, st_pt.geom = self.qc.get_qc_geom(str(st_pt.chemid) + '_well', st_pt.natom)
                        if e < 0:
                            logging.info('\tProduct optimization failed for {}, product {}'.format(instance_name,st_pt.chemid))
                            self.species.reac_ts_done[index] = -999
                            err = -1
                        elif e != 0:
                            err = -1
                        else:
                            e2, st_pt.energy = self.qc.get_qc_energy(str(st_pt.chemid) + '_well')
                            e2, st_pt.zpe = self.qc.get_qc_zpe(str(st_pt.chemid) + '_well')
                            st_pt.bond_mx()
                            st_pt.characterize(0)  # not allowed to use the dimer option here
                            st_pt.calc_chemid()
                            if chemid != st_pt.chemid:
                                # product was optimized to another structure, give warning and remove this reaction
                                logging.info('\tProduct optimizatied to other structure for {}, product {} to {}'.format(instance_name,chemid,st_pt.chemid))
                                self.species.reac_ts_done[index] = -999
                                err = -1
                    if err == 0:
                        self.species.reac_ts_done[index] = 4
                elif self.species.reac_ts_done[index] == 4:
                    # Do the TS and product optimization
                    
                    #make a stationary point object of the ts
                    bond_mx = np.zeros((self.species.natom, self.species.natom), dtype=int)
                    for i in range(self.species.natom):
                        for j in range(self.species.natom):
                            bond_mx[i][j] = max(self.species.bond[i][j],obj.product_bonds[i][j])
                    err, geom = self.qc.get_qc_geom(instance_name, self.species.natom)
                    ts = StationaryPoint(   instance_name, self.species.charge, self.species.mult,
                                            atom = self.species.atom, geom = geom, wellorts = 1)
                    err, ts.energy = self.qc.get_qc_energy(instance_name)
                    err, ts.zpe = self.qc.get_qc_zpe(instance_name)
                    ts.bond = bond_mx
                    ts.find_cycle()
                    ts.find_conf_dihedral()
                    obj.ts = ts
                    #do the ts optimization
                    obj.ts_opt = Optimize(obj.ts,self.par,self.qc)
                    obj.ts_opt.do_optimization()
                    #do the products optimizations
                    for st_pt in obj.products:
                        #check for products of other reactions that are the same as this product
                        #in the case such products are found, use the same Optimize object for both
                        new = 1
                        for i, inst_i in enumerate(self.species.reac_inst):
                            if not i == index:
                                obj_i = self.species.reac_obj[i]
                                if self.species.reac_ts_done[i] > 3:
                                    for j,st_pt_i in enumerate(obj_i.products):
                                        if st_pt_i.chemid == st_pt.chemid:
                                            if len(obj_i.prod_opt) > j:
                                                prod_opt = obj_i.prod_opt[j]
                                                new = 0
                                                break
                        if new:
                            prod_opt = Optimize(st_pt,self.par,self.qc)
                            prod_opt.do_optimization()
                        obj.prod_opt.append(prod_opt)
                    self.species.reac_ts_done[index] = 5
                elif self.species.reac_ts_done[index] == 5:
                    #check up on the TS and product optimizations 
                    opts_done = 1
                    fails = 0
                    #check if ts is done
                    if not obj.ts_opt.shir == 1:
                        opts_done = 0
                        obj.ts_opt.do_optimization()
                    if obj.ts_opt.shigh == -999:
                        fails = 1
                    for pr_opt in obj.prod_opt:
                        if not pr_opt.shir == 1:
                            opts_done = 0
                            pr_opt.do_optimization()
                        if pr_opt.shigh == -999:
                            fails = 1
                    if fails:
                        self.species.reac_ts_done[index] = -999
                    elif opts_done:
                        self.species.reac_ts_done[index] = 6
                elif self.species.reac_ts_done[index] == 6:
                    #Finilize the calculations
                    
                    #continue to PES search in case a new well was found
                    if self.par.par['pes']:
                        #verify if product is monomolecular, and if it is new
                        if len(obj.products) ==1:
                            st_pt = obj.prod_opt[0].species
                            chemid = st_pt.chemid
                            energy = st_pt.energy
                            well_energy = self.species.energy
                            new_barrier_threshold = self.par.par['barrier_threshold'] - (energy-well_energy)*constants.AUtoKCAL
                            dir = os.path.dirname(os.getcwd()) 
                            jobs = open(dir+'/chemids','r').read().split('\n')
                            jobs = [ji for ji in jobs]
                            if not str(chemid) in jobs:
                                #this well is new, add it to the jobs
                                while 1:
                                    try:
                                        #try to open the file and write to it
                                        pes.write_input(self.par,obj.products[0],new_barrier_threshold,dir)
                                        f = open(dir+'/chemids','a')
                                        f.write('{}\n'.format(chemid))
                                        f.close()
                                        break
                                    except IOError:
                                        #wait a second and try again
                                        time.sleep(1)
                                        pass
                                        
                    #check for wrong number of negative frequencies
                    neg_freq = 0
                    for st_pt in obj.products:
                        if any([fi < 0. for fi in st_pt.reduced_freqs]):
                            neg_freq = 1
                    if any([fi < 0. for fi in obj.ts.reduced_freqs[1:]]): 
                        neg_freq = 1
                    
                    if neg_freq:
                        logging.info('\tFound negative frequency for ' + instance_name)
                        self.species.reac_ts_done[index] = -999
                    else:
                        #the reaction search is finished
                        self.species.reac_ts_done[index] = -1 # this is the success code
                        
                        # write a temporary pes input file
                        # remove old xval and im_extent files
                        if os.path.exists('{}_xval.txt'.format(self.species.chemid)):
                            os.remove('{}_xval.txt'.format(self.species.chemid))
                        if os.path.exists('{}_im_extent.txt'.format(self.species.chemid)):
                            os.remove('{}_im_extent.txt'.format(self.species.chemid))
                        postprocess.createPESViewerInput(self.species, self.qc, self.par)
                elif self.species.reac_ts_done[index] == -999:
                    if not self.species.reac_obj[index].instance_name in deleted:
                        self.delete_files(self.species.reac_obj[index].instance_name)
                        deleted.append(self.species.reac_obj[index].instance_name)
                        
            alldone = 1
            for index, instance in enumerate(self.species.reac_inst):
                if any(self.species.reac_ts_done[i] >= 0 for i in range(len(self.species.reac_inst))):
                    alldone = 1
                    break 
                else: 
                    alldone = 0
            
            # write a small summary while running
            wr = 1
            if wr:
                f_out = open('kinbot_monitor.out','w')
                for index, instance in enumerate(self.species.reac_inst):
                    f_out.write('{}\t{}\t{}\n'.format(self.species.reac_ts_done[index],self.species.reac_step[index],self.species.reac_obj[index].instance_name))
                f_out.close()
            time.sleep(1)
        
        s = []
        for index, instance in enumerate(self.species.reac_inst):
            obj = self.species.reac_obj[index]
            instance_name = obj.instance_name
            # Write a summary on the combinatorial exploration
            if 'combinatorial' in instance_name:
                s.append('NAME\t' + instance_name)
                
                # Write the bonds that were broken and formed
                s.append('BROKEN_BONDS\t' + '\t'.join('[{}, {}]'.format(re[0], re[1]) for re in obj.reac))
                s.append('FORMED_BONDS\t' + '\t'.join('[{}, {}]'.format(pr[0], pr[1]) for pr in obj.prod))
                
                # Populate the ts_bond_lengths dict with the values
                # of this reaction
                if self.species.reac_ts_done[index] == -1:
                    for i in range(self.species.natom - 1):
                        for j in range(i + 1, self.species.natom):
                            if self.species.bond[i][j] != obj.product_bonds[i][j]:
                                if (self.species.bond[i][j] == 0 or
                                        obj.product_bonds[i][j] == 0):
                                    syms = []
                                    syms.append(self.species.atom[i])
                                    syms.append(self.species.atom[j])
                                    syms = ''.join(sorted(syms))
                                    dist = np.linalg.norm(obj.ts.geom[i] - obj.ts.geom[j])
                                    s.append('TS_BOND_LENGTHS\t{}\t{}'.format(syms, dist))
                # write the expected inchis
                s.append('EXPECTED_INCHIS\t' + '\t'.join(inchi for inchi in obj.prod_inchi))
                # get the inchis the reaction found
                if self.species.reac_ts_done[index] == -1:
                    inchis = obj.get_final_inchis()
                    s.append('FOUND_INCHIS\t' + '\t'.join(inchis))
                s.append('\n')
            with open('combinatorial.txt', 'w') as f:
                f.write('\n'.join(s) + '\n')

        logging.info("Reaction generation done!")