Example #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!")
Example #2
0
def main():
    try:
        input_file = sys.argv[1]
    except IndexError:
        print('To use KinBot, supply one argument being the input file!')
        sys.exit(-1)

    # print the license message to the console
    print(license_message.message)

    # initialize the parameters for this run
    masterpar = Parameters(input_file)
    par = masterpar.par
    input_file = masterpar.input_file
    # set up the logging environment
    if par['verbose']:
        logging.basicConfig(filename='kinbot.log', level=logging.DEBUG)
    else:
        logging.basicConfig(filename='kinbot.log', level=logging.INFO)

    # write the license message to the log file
    logging.info(license_message.message)
    logging.info('Input parameters')
    for param in par:
        logging.info('{} {}'.format(param, par[param]))
    # time stamp of the KinBot start
    logging.info('Starting KinBot at {}'.format(datetime.datetime.now()))

    # Make the necessary directories
    if not os.path.exists('perm'):
        os.makedirs('perm')
    if not os.path.exists('scratch'):
        os.makedirs('scratch')
    if not os.path.exists(par['single_point_qc']):
        os.mkdir(par['single_point_qc'])
    if par['rotor_scan'] == 1:
        if not os.path.exists('hir'):
            os.mkdir('hir')
        if not os.path.exists('hir_profiles'):
            os.mkdir('hir_profiles')
        if not os.path.exists('perm/hir/'):
            os.makedirs('perm/hir/')
    if par['conformer_search'] == 1:
        if not os.path.exists('conf'):
            os.mkdir('conf')
        if not os.path.exists('perm/conf'):
            os.makedirs('perm/conf')
    if not os.path.exists('me'):
        os.mkdir('me')

    # initialize the reactant
    well0 = StationaryPoint('well0',
                            par['charge'],
                            par['mult'],
                            smiles=par['smiles'],
                            structure=par['structure'])
    well0.short_name = 'w1'
    # write the initial reactant geometry to a file for visualization
    geom_out = open('geometry.xyz', 'w')
    geom_out.write('{}\n\n'.format(well0.natom))
    for i, at in enumerate(well0.atom):
        x, y, z = well0.geom[i]
        geom_out.write('{} {:.6f} {:.6f} {:.6f}\n'.format(at, x, y, z))
    geom_out.write('\n\n')
    geom_out.close()

    # characterize the initial reactant
    well0.characterize(dimer=par['dimer'])
    well0.name = str(well0.chemid)
    start_name = well0.name

    # initialize the qc instance
    qc = QuantumChemistry(par)
    # only run filecopying if PES is turned on
    # if par['pes']:
    # check if this well was calcualted before in another directory
    # this flag indicates that this kinbot run
    # should wait for the information from another
    # kinbot run to become available and copy the necessary information
    #    wait_for_well = 1
    #    while wait_for_well:
    #        wait_for_well = filecopying.copy_from_database_folder(well0.chemid, well0.chemid, qc)
    #        if wait_for_well:
    #            time.sleep(1)

    # start the initial optimization of the reactant
    logging.info('Starting optimization of intial well')
    qc.qc_opt(well0, well0.geom)
    err, well0.geom = qc.get_qc_geom(str(well0.chemid) + '_well',
                                     well0.natom,
                                     wait=1)
    err, well0.freq = qc.get_qc_freq(str(well0.chemid) + '_well',
                                     well0.natom,
                                     wait=1)
    if err < 0:
        logging.error('Error with initial structure optimization.')
        return
    if any(well0.freq[i] <= 0 for i in range(len(well0.freq))):
        logging.error('Found imaginary frequency for initial structure.')
        return

    # characterize again and look for differences
    well0.characterize(dimer=par['dimer'])
    well0.name = str(well0.chemid)
    if well0.name != start_name:
        logging.error(
            'The first well optimized to a structure different from the input.'
        )
        return

    # do an MP2 optimization of the reactant,
    # to compare some scan barrier heigths to
    if par['families'] == ['all'] or \
            'birad_recombination_R' in par['families'] or \
            'r12_cycloaddition' in par['families'] or \
            'r14_birad_scission' in par['families'] or \
            'R_Addition_MultipleBond' in par['families'] or \
            (par['skip_families'] != ['none'] and \
            ('birad_recombination_R' not in par['skip_families'] or \
            'r12_cycloaddition' not in par['skip_families'] or \
            'r14_birad_scission' not in par['skip_families'] or \
            'R_Addition_MultipleBond' not in par['skip_families'])) or \
            par['reaction_search'] == 0:
        logging.info('Starting MP2 optimization of intial well')
        qc.qc_opt(well0, well0.geom, mp2=1)
        err, geom = qc.get_qc_geom(
            str(well0.chemid) + '_well_mp2', well0.natom, 1)

    # comparison for barrierless scan
    if par['barrierless_saddle']:
        logging.info(
            'Optimization of intial well for barrierless at {}/{}'.format(
                par['barrierless_saddle_method'],
                par['barrierless_saddle_basis']))
        qc.qc_opt(well0, well0.geom, bls=1)
        err, geom = qc.get_qc_geom(
            str(well0.chemid) + '_well_bls', well0.natom, 1)

    # characterize again and look for differences
    well0.characterize(dimer=par['dimer'])
    well0.name = str(well0.chemid)

    err, well0.energy = qc.get_qc_energy(str(well0.chemid) + '_well', 1)
    err, well0.zpe = qc.get_qc_zpe(str(well0.chemid) + '_well', 1)

    well_opt = Optimize(well0, par, qc, wait=1)
    well_opt.do_optimization()
    if well_opt.shigh == -999:
        logging.error(
            'Error with high level optimization of initial structure.')
        return

    if par['pes']:
        filecopying.copy_to_database_folder(well0.chemid, well0.chemid, qc)

    if par['reaction_search'] == 1:
        logging.info('Starting reaction searches of intial well')
        rf = ReactionFinder(well0, par, qc)
        rf.find_reactions()
        rg = ReactionGenerator(well0, par, qc, input_file)
        rg.generate()

    if par['homolytic_scissions'] == 1:
        logging.info('Starting the search for homolytic scission products')
        well0.homolytic_scissions = HomolyticScissions(well0, par, qc)
        well0.homolytic_scissions.find_homolytic_scissions()

    if par['me'] > 0:  # it will be 2 for kinbots when the mess file is needed but not run
        mess = MESS(par, well0)
        mess.write_input(qc)

        if par['me'] == 1:
            logging.info('Starting Master Equation calculations')
            if par['me_code'] == 'mess':
                mess.run()

    postprocess.createSummaryFile(well0, qc, par)
    postprocess.createPESViewerInput(well0, qc, par)
    postprocess.creatMLInput(well0, qc, par)

    logging.info('Finished KinBot at {}'.format(datetime.datetime.now()))
    print("Done!")
Example #3
0
    def find_homolytic_scissions(self):
        """
        Enumerate all unique homolytic scission reactions
        """
        # set of unique bonds to break
        bonds = []
        for i in range(len(self.species.atom)-1):
            for j in range(i+1, len(self.species.atom)):
                # only consider a bond of which both
                # atoms are not in the same cycle
                cycle = []
                for cyc in self.species.cycle_chain:
                    if i in cyc:
                        cycle.extend(cyc)
                if j not in cycle:
                    # only consider single bonds for homolytic scissions
                    if self.species.bond[i][j] == 1:
                        # check if a bond with identical atomids
                        # has been added to the bonds list yet
                        new = 1
                        for bi in bonds:
                            if sorted([self.species.atomid[at] for at in bi]) == sorted([self.species.atomid[i], self.species.atomid[j]]):
                                new = 0
                        if new:
                            bonds.append([i, j])
                            hs = HomolyticScission(self.species, self.par,
                                                   self.qc, [i, j])
                            hs.create_geometries()
                            self.hss.append(hs)

        # optimize the products of the hss
        while 1:
            for index, hs in enumerate(self.hss):
                if hs.status == 0:
                    # do the initial optimization
                    for prod in hs.products:
                        hs.qc.qc_opt(prod, prod.geom)
                    hs.status = 1
                if hs.status == 1:
                    # wait for the optimization to finish
                    err = 0
                    for prod in hs.products:
                        e, prod.geom = hs.qc.get_qc_geom(str(prod.chemid) + '_well', prod.natom)
                        if e < 0:
                            # optimizatin failed
                            hs.status = -999
                            err = -1
                        elif e != 0:
                            err = -1
                        else:
                            e2, prod.energy = hs.qc.get_qc_energy(str(prod.chemid) + '_well', prod.natom)
                            e2, prod.zpe = hs.qc.get_qc_zpe(str(prod.chemid) + '_well', prod.natom)
                    if err == 0:
                        hs.status = 2
                if hs.status == 2:
                    # Do the product conf search, high level opt and HIR
                    for prod in hs.products:
                        prod_opt = Optimize(prod, self.par, self.qc)
                        prod_opt.do_optimization()
                        hs.prod_opt.append(prod_opt)
                    hs.status = 3
                if hs.status == 3:
                    # check up on the optimization
                    opts_done = 1
                    fails = 0
                    for pr_opt in hs.prod_opt:
                        if not pr_opt.shir == 1:
                            opts_done = 0
                            pr_opt.do_optimization()
                        if pr_opt.shigh == -999:
                            fails = 1
                    if fails:
                        hs.status = -999
                    elif opts_done:
                        # check if the energy is higher
                        # than the barrier threshold
                        species_energy = self.species.energy
                        prod_energy = 0.
                        for pr_opt in hs.prod_opt:
                            prod_energy += pr_opt.species.energy
                        barrier = (prod_energy - species_energy)*constants.AUtoKCAL
                        if barrier > self.par.par['barrier_threshold']:
                            hs.status = -999
                        else:
                            hs.status = -1
            if all([hs.status < 0 for hs in self.hss]):
                break
Example #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

        # 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!")
Example #5
0
 def find_homolytic_scissions(self):
     """
     Enumerate all unique homolytic scission reactions
     """
     # set of unique bonds to break
     bonds = []
     for i in range(len(self.species.atom)-1):
         for j in range(i+1, len(self.species.atom)):
             # only consider a bond of which both
             # atoms are not in the same cycle
             cycle = []
             for cyc in self.species.cycle_chain:
                 if i in cyc:
                     cycle.extend(cyc)
             if j not in cycle:
                 # only consider single bonds for homolytic scissions
                 if self.species.bond[i][j] == 1:
                     # check if the bond is present in the 'homolytic_bonds' list, or if the list is empty
                     # add is a boolean that tells if the bond needs to be added to the homolytic scissions
                     # to be considered (i.e. the 'bonds' list)
                     add = 0
                     # get the elements of the current bond under consideration, and put them
                     # in alphabetical order
                     bond_elements = sorted([self.species.atom[i], self.species.atom[j]])
                     # check all bonds to be considered, supplied by the user
                     for user_bond in self.par['homolytic_bonds']:
                         if sorted(user_bond) == bond_elements:
                             # if there is a match between the current bond and a user-defined
                             # bond, put the add boolean to 1
                             add = 1
                     # add the bond if the 'add' boolean is 1 or if the user defined list is empty
                     # (in the latter case, all single bonds that are not in a cycle are considered. 
                     if len(self.par['homolytic_bonds']) == 0 or add:
                         # check if a bond with identical atomids
                         # has been added to the bonds list yet
                         new = 1
                         for bi in bonds:
                             if sorted([self.species.atomid[at] for at in bi]) == sorted([self.species.atomid[i], self.species.atomid[j]]):
                                 new = 0
                         if new:
                             bonds.append([i, j])
                             hs = HomolyticScission(self.species, self.par,
                                                    self.qc, [i, j])
                             hs.create_geometries()
                             self.hss.append(hs)
     
     # optimize the products of the hss
     while not all([hs.status < 0 for hs in self.hss]):
         for index, hs in enumerate(self.hss):
             if hs.status == 0:
                 # do the initial optimization
                 for prod in hs.products:
                     hs.qc.qc_opt(prod, prod.geom)
                 hs.status = 1
             if hs.status == 1:
                 # wait for the optimization to finish
                 err = 0
                 for prod in hs.products:
                     e, prod.geom = hs.qc.get_qc_geom(str(prod.chemid) + '_well', prod.natom)
                     if e < 0:
                         # optimizatin failed
                         logging.info("HS optimization failed for {}".format(prod.chemid))
                         hs.status = -999
                         err = -1
                     elif e != 0:
                         err = -1
                     else:
                         e2, prod.energy = hs.qc.get_qc_energy(str(prod.chemid) + '_well')
                         e2, prod.zpe = hs.qc.get_qc_zpe(str(prod.chemid) + '_well')
                 if err == 0:
                     hs.status = 2
             if hs.status == 2:
                 # Do the product conf search, high level opt and HIR
                 err = 0
                 for i, prod in enumerate(hs.products):
                     chemid = prod.chemid
                     prod_opt = Optimize(prod, self.par, self.qc)
                     if str(chemid) != str(prod_opt.species.chemid):
                         logging.info("HS product {} changed to {} during optimization.".format(chemid, prod_opt.species.chemid))
                         hs.qc.qc_opt(prod_opt.species, prod_opt.species.geom)
                         j = i - 1
                         # wait for new opt to finish
                         er, prod.geom = hs.qc.get_qc_geom(str(prod_opt.species.chemid) + '_well', prod_opt.species.natom)
                         if er < 0:
                             # optimization failed
                             logging.info("HS optimization failed for {}".format(prod_opt.species.chemid))
                             err = -1
                         elif er != 0:
                             err = -1
                         else:
                             er2, prod.energy = hs.qc.get_qc_energy(str(prod_opt.species.chemid) + '_well')
                             er2, prod.zpe = hs.qc.get_qc_zpe(str(prod_opt.species.chemid) + '_well')
                         hs.products.pop(i)
                         hs.products.insert(j, prod_opt.species)
                     prod_opt.do_optimization()
                     hs.prod_opt.append(prod_opt)
                 if err == 0:
                     hs.status = 3
             if hs.status == 3:
                 # check up on the optimization
                 opts_done = 1
                 fails = 0
                 for pr_opt in hs.prod_opt:
                     if not pr_opt.shir == 1:
                         opts_done = 0
                         pr_opt.do_optimization()
                     if pr_opt.shigh == -999:
                         fails = 1
                 if fails:
                     hs.status = -999
                 elif opts_done:
                     # check if the energy is higher
                     # than the barrier threshold
                     species_zeroenergy = self.species.energy + self.species.zpe
                     prod_zeroenergy = 0.
                     for pr_opt in hs.prod_opt:
                         prod_zeroenergy += pr_opt.species.energy + pr_opt.species.zpe
                     barrier = (prod_zeroenergy - species_zeroenergy) * constants.AUtoKCAL
                     prod_name = ' '.join(sorted([str(prod.species.chemid) for prod in hs.prod_opt]))
                     if barrier > self.par['barrier_threshold']:
                         logging.info("Energy of HS product {}, {:.2f} kcal/mol, is too high.".format(prod_name, barrier))
                         hs.status = -999
                     else:
                         hs.status = -1
                         name = '_'.join(sorted([str(prod.species.chemid) for prod in hs.prod_opt]))
                         logging.info('Homolytic scission (asymptote {:.2f} kcal/mol) lead to products {}'.format(barrier, name))
Example #6
0
def main():
    try:
        input_file = sys.argv[1]
    except IndexError:
        print('To use KinBot, supply one argument being the input file!')
        sys.exit(-1)

    # print the license message to the console
    print(license_message.message)

    # initialize the parameters for this run
    par = Parameters(input_file)

    # set up the logging environment
    if par.par['verbose']:
        logging.basicConfig(filename='kinbot.log', level=logging.DEBUG)
    else:
        logging.basicConfig(filename='kinbot.log', level=logging.INFO)

    # write the license message to the log file
    logging.info(license_message.message)
    # time stamp of the KinBot start
    logging.info('Starting KinBot at {}'.format(datetime.datetime.now()))

    # Make the necessary directories
    if not os.path.exists('perm'):
        os.makedirs('perm')
    if not os.path.exists('scratch'):
        os.makedirs('scratch')
    if not os.path.exists(par.par['single_point_qc']):
        os.mkdir(par.par['single_point_qc'])
    if par.par['rotor_scan'] == 1:
        if not os.path.exists('hir'):
            os.mkdir('hir')
        if not os.path.exists('hir_profiles'):
            os.mkdir('hir_profiles')
        if not os.path.exists('perm/hir/'):
            os.makedirs('perm/hir/')
    if par.par['conformer_search'] == 1:
        if not os.path.exists('conf'):
            os.mkdir('conf')
        if not os.path.exists('perm/conf'):
            os.makedirs('perm/conf')
    if not os.path.exists('me'):
        os.mkdir('me')


    if par.par['pes'] and par.par['specific_reaction']:
        logging.error('Specific reaction cannot be searched in PES mode.')
        return

    # initialize the reactant
    well0 = StationaryPoint('well0',
                            par.par['charge'],
                            par.par['mult'],
                            smiles=par.par['smiles'],
                            structure=par.par['structure'])
    well0.short_name = 'w1'

    # wrtie the initial reactant geometry to a file for visualization
    geom_out = open('geometry.xyz', 'w')
    geom_out.write('{}\n\n'.format(well0.natom))
    for i, at in enumerate(well0.atom):
        x, y, z = well0.geom[i]
        geom_out.write('{} {:.6f} {:.6f} {:.6f}\n'.format(at, x, y, z))
    geom_out.write('\n\n')
    geom_out.close()

    # characterize the initial reactant
    well0.characterize(par.par['dimer'])
    well0.name = str(well0.chemid)
    start_name = well0.name

    # initialize the qc instance
    qc = QuantumChemistry(par)

    # start the initial optimization of the reactant
    logging.info('Starting optimization of intial well')
    qc.qc_opt(well0, well0.geom)
    err, well0.geom = qc.get_qc_geom(str(well0.chemid) + '_well',
                                     well0.natom, wait=1)
    err, well0.freq = qc.get_qc_freq(str(well0.chemid) + '_well',
                                     well0.natom, wait=1)
    if err < 0:
        logging.error('Error with initial structure optimization.')
        return
    if any(well0.freq[i] <= 0 for i in range(len(well0.freq))):
        logging.error('Found imaginary frequency for initial structure.')
        return

    # characterize again and look for differences
    well0.characterize(par.par['dimer'])
    well0.name = str(well0.chemid)
    if well0.name != start_name:
        logging.error('The first well optimized to a structure different from the input.')
        return

    # do an MP2 optimization of the reactant,
    # to compare Beta scission barrier heigths to
    logging.info('Starting MP2 optimization of intial well')
    qc.qc_opt(well0, well0.geom, mp2=1)
    err, geom = qc.get_qc_geom(str(well0.chemid) + '_well_mp2', well0.natom, 1)

    # characterize again and look for differences
    well0.characterize(par.par['dimer'])
    well0.name = str(well0.chemid)

    # read the energy and the zpe corrected energy
    err, well0.energy = qc.get_qc_energy(str(well0.chemid) + '_well', 1)
    err, well0.zpe = qc.get_qc_zpe(str(well0.chemid) + '_well', 1)

    well_opt = Optimize(well0, par, qc, wait=1)
    well_opt.do_optimization()
    if well_opt.shigh == -999:
        logging.error('Error with high level optimization of initial structure.')
        return

    # do the reaction search using heuristics
    if par.par['reaction_search'] == 1:
        logging.info('Starting reaction searches of intial well')
        rf = ReactionFinder(well0, par, qc)
        rf.find_reactions()
        rg = ReactionGenerator(well0, par, qc)
        rg.generate()
    # do the homolytic scission products search
    if par.par['homolytic_scissions'] == 1:
        logging.info('Starting the search for homolytic scission products')
        well0.homolytic_scissions = HomolyticScissions(well0, par, qc)
        well0.homolytic_scissions.find_homolytic_scissions()
    # initialize the master equation instance
    mess = MESS(par, well0)
    mess.write_input()
    mesmer = MESMER(par, well0)
    mesmer.write_input()
    if par.par['me'] == 1:
        logging.info('Starting Master Equation calculations')
        if par.par['me_code'] == 'mess':
            mess.run()
        elif par.par['me_code'] == 'mesmer':
            mesmer.run()
        else:
            logging.error('Cannot recognize me code {}'.format(par.par['me_code']))

    # postprocess the calculations
    postprocess.createSummaryFile(well0, qc, par)
    postprocess.createPESViewerInput(well0, qc, par)
    postprocess.creatMLInput(well0, qc, par)

    logging.info('Finished KinBot at {}'.format(datetime.datetime.now()))
    print("Done!")
Example #7
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!")
Example #8
0
    def find_homolytic_scissions(self):
        """
        Enumerate all unique homolytic scission reactions
        """
        # set of unique bonds to break
        bonds = []
        for i in range(len(self.species.atom) - 1):
            for j in range(i + 1, len(self.species.atom)):
                # only consider a bond of which both
                # atoms are not in the same cycle
                cycle = []
                for cyc in self.species.cycle_chain:
                    if i in cyc:
                        cycle.extend(cyc)
                if j not in cycle:
                    # only consider single bonds for homolytic scissions
                    if self.species.bond[i][j] == 1:
                        # check if a bond with identical atomids
                        # has been added to the bonds list yet
                        new = 1
                        for bi in bonds:
                            if sorted([self.species.atomid[at]
                                       for at in bi]) == sorted([
                                           self.species.atomid[i],
                                           self.species.atomid[j]
                                       ]):
                                new = 0
                        if new:
                            bonds.append([i, j])
                            hs = HomolyticScission(self.species, self.par,
                                                   self.qc, [i, j])
                            hs.create_geometries()
                            self.hss.append(hs)

        # optimize the products of the hss
        while 1:
            for index, hs in enumerate(self.hss):
                if hs.status == 0:
                    # do the initial optimization
                    for prod in hs.products:
                        hs.qc.qc_opt(prod, prod.geom)
                    hs.status = 1
                if hs.status == 1:
                    # wait for the optimization to finish
                    err = 0
                    for prod in hs.products:
                        e, prod.geom = hs.qc.get_qc_geom(
                            str(prod.chemid) + '_well', prod.natom)
                        if e < 0:
                            # optimizatin failed
                            hs.status = -999
                            err = -1
                        elif e != 0:
                            err = -1
                        else:
                            e2, prod.energy = hs.qc.get_qc_energy(
                                str(prod.chemid) + '_well', prod.natom)
                            e2, prod.zpe = hs.qc.get_qc_zpe(
                                str(prod.chemid) + '_well', prod.natom)
                    if err == 0:
                        hs.status = 2
                if hs.status == 2:
                    # Do the product conf search, high level opt and HIR
                    for prod in hs.products:
                        prod_opt = Optimize(prod, self.par, self.qc)
                        prod_opt.do_optimization()
                        hs.prod_opt.append(prod_opt)
                    hs.status = 3
                if hs.status == 3:
                    # check up on the optimization
                    opts_done = 1
                    fails = 0
                    for pr_opt in hs.prod_opt:
                        if not pr_opt.shir == 1:
                            opts_done = 0
                            pr_opt.do_optimization()
                        if pr_opt.shigh == -999:
                            fails = 1
                    if fails:
                        hs.status = -999
                    elif opts_done:
                        # check if the energy is higher
                        # than the barrier threshold
                        species_energy = self.species.energy
                        prod_energy = 0.
                        for pr_opt in hs.prod_opt:
                            prod_energy += pr_opt.species.energy
                        barrier = (prod_energy -
                                   species_energy) * constants.AUtoKCAL
                        if barrier > self.par.par['barrier_threshold']:
                            hs.status = -999
                        else:
                            hs.status = -1
            if all([hs.status < 0 for hs in self.hss]):
                break
Example #9
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!")