def main(): # Get user input. args = parse_user_input() _exec("rm -f ts-analyze.tar.bz2 irc_transition.xyz irc_transition.vib", print_command=False) # Standardize base name to "irc_transition". shutil.copy2(args.ts, 'irc_transition.xyz') # Run Q-Chem calculations.. QCT = QChem("irc_transition.xyz", charge=args.charge, mult=args.mult, method=args.method, basis=args.basis) # Ensure stability. QCT.make_stable() # Single point calculation for bond order matrix. QCT.jobtype = 'sp' QCT.remextra = OrderedDict([('scf_final_print', '1')]) QCT.calculate() np.savetxt("irc_transition.bnd", QCT.load_qcout().qm_bondorder, fmt="%8.3f") # Frequency calculation. QCT.jobtype = 'freq' QCT.calculate() QCT.write_vdata('irc_transition.vib') tarexit()
def QCOpt(initial, charge, mult, method, basis, cycles=100, gtol=600, dxtol=2400, etol=200, cart=False): """ Run a Q-Chem geometry optimization. Default tolerances for gradient, displacement and energy are higher than usual because we don't want significant nonbonded conformational changes in the pathway. Parameters ---------- initial : str Initial XYZ file for optimization charge : int Net charge mult : int Spin multiplicity method : str Electronic structure method basis : str Basis set cycles : int Number of optimization cycles gtol : int Gradient tolerance for Q-Chem dxtol : int Displacement tolerance for Q-Chem etol : int Energy tolerance for Q-Chem cart : bool Perform the optimization in Cartesian coordinates """ # Create Q-Chem object. QC = QChem(initial, charge=charge, mult=mult, method=method, basis=basis, qcin='optimize.in') # Run a stability analysis first to ensure we're in the ground state. QC.make_stable() # Set geometry optimization options. QC.jobtype = 'opt' QC.remextra = OrderedDict() if cart: QC.remextra['geom_opt_coords'] = 0 QC.remextra['thresh'] = 14 QC.remextra['geom_opt_tol_gradient'] = gtol QC.remextra['geom_opt_tol_displacement'] = dxtol QC.remextra['geom_opt_tol_energy'] = etol QC.remextra['geom_opt_max_cycles'] = cycles # Run Q-Chem. QC.calculate() # Create Molecule object from running Q-Chem. M = QC.load_qcout() return M
def main(): # Get user input. args = parse_user_input() # Start timer. click() # Run single point calculation to determine bond order matrix. QCSP = QChem(args.initial, charge=args.charge, mult=args.mult, method=args.method, basis=args.basis) QCSP.make_stable() QCSP.jobtype = 'sp' QCSP.remextra = OrderedDict([('scf_final_print', '1')]) QCSP.calculate() # Rebuild bond list using the bond order matrix. M = QCSP.load_qcout() InitE = M.qm_energies[0] M.bonds = [] # Determined from an incredibly small # of data points. # In example_1.xyz, an O-H distance of 1.89 Angstrom had a BO of 0.094 (so it should be > 0.1) # In example_1.xyz, a C-H distance of 1.39 Angstrom had a BO of 0.305 (so it should be < 0.3) bo_thresh = 0.2 numbonds = 0 bondfactor = 0.0 print("Atoms QM-BO Distance") for i in range(M.na): for j in range(i + 1, M.na): # Print out the ones that are "almost" bonds. if M.qm_bondorder[i, j] > 0.1: print("%s%i%s%s%i % .3f % .3f" % (M.elem[i], i, '-' if M.qm_bondorder[i, j] > bo_thresh else ' ', M.elem[j], j, M.qm_bondorder[i, j], np.linalg.norm(M.xyzs[-1][i] - M.xyzs[-1][j]))) if M.qm_bondorder[i, j] > bo_thresh: M.bonds.append((i, j)) numbonds += 1 if M.qm_bondorder[i, j] <= 1: bondfactor += M.qm_bondorder[i, j] else: # Count double bonds as 1 bondfactor += 1 bondfactor /= numbonds M.build_topology() subxyz = [] subef = [] subna = [] subchg = [] submult = [] subvalid = [] SumFrags = [] # Divide calculation into subsystems. for subg in M.molecules: matoms = subg.nodes() frag = M.atom_select(matoms) # Determine output file name. fout = os.path.splitext( args.initial)[0] + '.sub_%i' % len(subxyz) + '.xyz' # Assume we are getting the Mulliken charges from the last frame, it's safer. # Write the output .xyz file. Chg = sum(M.qm_mulliken_charges[-1][matoms]) SpnZ = sum(M.qm_mulliken_spins[-1][matoms]) Spn2 = sum([i**2 for i in M.qm_mulliken_spins[-1][matoms]]) frag.comms = [ "Molecular formula %s atoms %s from %s charge %+.3f sz %+.3f sz^2 %.3f" % (subg.ef(), commadash(subg.nodes()), args.initial, Chg, SpnZ, Spn2) ] frag.write(fout) subxyz.append(fout) subef.append(subg.ef()) subna.append(frag.na) # Determine integer charge and multiplicity. ichg, chgpass = extract_int(np.array([Chg]), 0.5, 1.0, label="charge") ispn, spnpass = extract_int(np.array([abs(SpnZ)]), 0.5, 1.0, label="spin-z") nproton = sum([Elements.index(i) for i in frag.elem]) nelectron = nproton + ichg # If calculation is valid, append to the list of xyz/chg/mult to be calculated. if (not chgpass or not spnpass): print("Cannot determine charge and spin for fragment %s\n" % subg.ef()) subchg.append(None) submult.append(None) subvalid.append(False) elif ((nelectron - ispn) / 2) * 2 != (nelectron - ispn): print("Inconsistent charge and spin-z for fragment %s\n" % subg.ef()) subchg.append(None) submult.append(None) subvalid.append(False) else: subchg.append(ichg) submult.append(ispn + 1) subvalid.append(True) print("%i/%i subcalculations are valid" % (len(subvalid), sum(subvalid))) for i in range(len(subxyz)): print("%s formula %-12s charge %i mult %i %s" % (subxyz[i], subef[i], subchg[i], submult[i], "valid" if subvalid[i] else "invalid")) fragid = open('fragmentid.txt', 'w') for formula in subef: fragid.write(formula + " ") fragid.write("\nBondfactor: " + str(bondfactor)) if not all(subvalid): fragid.write("\ninvalid") fragid.close() # Archive and exit tarexit()
def main(): # Get user input. args = parse_user_input() if len(args.methods) > 2: logger.error("Unsure what to do with >2 electronic structure methods") raise RuntimeError # Delete the result from previous jobs _exec("rm -rf ts_result.tar.bz2", print_command=False) # Perform transition state search. # First run the TS-calculation with the smaller basis set QCTS1 = QChemTS(args.tsest, charge=args.charge, mult=args.mult, method=args.methods[0], basis=args.bases[0], finalize=(len(args.methods) == 1), qcin='qcts1.in', vout='irc_transition.vib') QCTS1.write('ts1.xyz') if len(args.methods) == 2: print(' --== \x1b[1;92mUpgrading\x1b[0m ==--') QCTS2 = QChemTS("ts1.xyz", charge=args.charge, mult=args.mult, method=args.methods[1], basis=args.bases[1], finalize=True, qcin='qcts2.in', vout='irc_transition.vib') QCTS2.write('ts2.xyz') qcdir = QCTS2.qcdir shutil.copy2('ts2.xyz', 'ts.xyz') else: qcdir = QCTS1.qcdir shutil.copy2('ts1.xyz', 'ts.xyz') # Intrinsic reaction coordinate calculation. print("Intrinsic reaction coordinate..") # Process and save IRC results. M_IRC, E_IRC = QChemIRC("ts.xyz", charge=args.charge, mult=args.mult, method=args.methods[-1], basis=args.bases[-1], qcdir=qcdir, xyz0=args.initpath) M_IRC.write("irc.xyz") M_IRC.get_populations().write('irc.pop', ftype='xyz') # Save the IRC energy as a function of arc length. ArcMol = arc(M_IRC, RMSD=True) ArcMolCumul = np.insert(np.cumsum(ArcMol), 0, 0.0) np.savetxt("irc.nrg", np.hstack((ArcMolCumul.reshape(-1, 1), E_IRC.reshape(-1, 1))), fmt="% 14.6f", header="Arclength(Ang) Energy(kcal/mol)") # Create IRC with equally spaced structures. M_IRC_EV = SpaceIRC(M_IRC, E_IRC, RMSD=True) M_IRC_EV.write("irc_spaced.xyz") # Run two final single point calculations with SCF_FINAL_PRINT set to 1 M_IRC[0].write("irc_reactant.xyz") M_IRC[-1].write("irc_product.xyz") QCR = QChem("irc_reactant.xyz", charge=args.charge, mult=args.mult, method=args.methods[-1], basis=args.bases[-1]) QCP = QChem("irc_product.xyz", charge=args.charge, mult=args.mult, method=args.methods[-1], basis=args.bases[-1]) shutil.copy2("ts.xyz", "irc_transition.xyz") QCT = QChem("irc_transition.xyz", charge=args.charge, mult=args.mult, method=args.methods[-1], basis=args.bases[-1]) def FinalSCF(SP): SP.make_stable() SP.jobtype = 'sp' SP.remextra = OrderedDict([('scf_final_print', '1')]) SP.calculate() FinalSCF(QCR) FinalSCF(QCP) FinalSCF(QCT) np.savetxt("irc_reactant.bnd", QCR.load_qcout().qm_bondorder, fmt="%8.3f") np.savetxt("irc_product.bnd", QCP.load_qcout().qm_bondorder, fmt="%8.3f") np.savetxt("irc_transition.bnd", QCT.load_qcout().qm_bondorder, fmt="%8.3f") # Calculate ZPEs, entropy, enthalpy for Delta-G calcs # Should be able to take out freq calcs of R and P once we get can confidently get rid of this section. QCR.remextra = OrderedDict() QCP.remextra = OrderedDict() QCT.remextra = OrderedDict() QCR.freq() R = QCR.load_qcout() QCP.freq() P = QCP.load_qcout() QCT.freq() T = QCT.load_qcout() nrgfile = open('deltaG.nrg', 'w') deltaH = P.qm_energies[0] * Ha_to_kcalmol + P.qm_zpe[ 0] - R.qm_energies[0] * Ha_to_kcalmol - R.qm_zpe[0] deltaG = deltaH - P.qm_entropy[0] * 0.29815 + P.qm_enthalpy[ 0] + R.qm_entropy[0] * 0.29815 - R.qm_enthalpy[0] Ha = T.qm_energies[0] * Ha_to_kcalmol + T.qm_zpe[ 0] - R.qm_energies[0] * Ha_to_kcalmol - R.qm_zpe[0] Ga = Ha - T.qm_entropy[0] * 0.29815 + T.qm_enthalpy[ 0] + R.qm_entropy[0] * 0.29815 - R.qm_enthalpy[0] nrgfile.write( "=> ** The following data is referenced to reactant and product complexes **\n" ) nrgfile.write( "=> ** WARNING! Data may not be accurate in cases with >1 molecule **\n" ) nrgfile.write( "=> ** Activation enthalpy H_a (0K) = %.4f kcal/mol **\n" % Ha) nrgfile.write( "=> ** Activation Gibbs energy G_a (STP) = %.4f kcal/mol **\n" % Ga) nrgfile.write( "=> ** Delta-H(0K) = %.4f kcal/mol **\n" % deltaH) nrgfile.write( "=> ** Delta-G(STP) = %.4f kcal/mol **\n" % deltaG) # Calculate Delta-G's based on fragment information from reactant and product # Get data from fragment nrg files if args.fragpath == None: fragpath = os.path.abspath('../../../../../fragments') else: fragpath = args.fragpath formulas = [] nrg = [] zpe = [] entr = [] enth = [] validity = [] for frm in os.listdir(fragpath): optdir = os.path.join(fragpath, frm, "opt") if os.path.exists(os.path.join(optdir, 'fragmentopt.nrg')): fragnrgfile = open(os.path.join(optdir, 'fragmentopt.nrg')) formulas.append(fragnrgfile.readline().strip()) nrg.append(float(fragnrgfile.readline().split()[3])) zpe.append(float(fragnrgfile.readline().split()[2])) entr.append(float(fragnrgfile.readline().split()[3])) enth.append(float(fragnrgfile.readline().split()[3])) validity.append(fragnrgfile.readline().strip()) fragnrgfile.close() # Compare list of molecules to choose right energy formulasR = [] formulasP = [] nrgR = 0.0 nrgP = 0.0 R.build_topology() for subg in R.molecules: formulasR.append(subg.ef()) formulasR = sorted(formulasR) P.build_topology() for subg in P.molecules: formulasP.append(subg.ef()) formulasP = sorted(formulasP) for i in range(len(formulas)): formlist = sorted(formulas[i].split()) if formlist == formulasR and validity[i] != "invalid": nrgR = nrg[i] zpeR = zpe[i] entrR = entr[i] enthR = enth[i] if formlist == formulasP and validity[i] != "invalid": nrgP = nrg[i] zpeP = zpe[i] entrP = entr[i] enthP = enth[i] # Calculate energetics if nrgR != 0.0: Ha = T.qm_energies[0] * Ha_to_kcalmol + T.qm_zpe[ 0] - nrgR * Ha_to_kcalmol - zpeR Ga = Ha - T.qm_entropy[0] * 0.29815 + T.qm_enthalpy[ 0] + entrR * 0.29815 - enthR nrgfile.write( "=> ## The following data is calculated referenced to isolated reactant and product molecules:\n" ) nrgfile.write( "=> ## Activation enthalpy H_a (0K) = %.4f kcal/mol ##\n" % Ha) nrgfile.write( "=> ## Activation Gibbs energy G_a (STP) = %.4f kcal/mol ##\n" % Ga) else: nrgfile.write( "=> Reactant state could not be identified among fragment calculations\n" ) nrgfile.write( "=> No energetics referenced to isolated molecules will be calculated for this pathway\n" ) if nrgR != 0.0 and nrgP != 0.0: deltaH = nrgP * Ha_to_kcalmol + zpeP - nrgR * Ha_to_kcalmol - zpeR deltaG = deltaH - entrP * 0.29815 + enthP + entrR * 0.29815 - enthR nrgfile.write( "=> ## Delta-H(0K) = %.4f kcal/mol **\n" % deltaH) nrgfile.write( "=> ## Delta-G(STP) = %.4f kcal/mol **\n" % deltaG) elif nrgP == 0.0: nrgfile.write( "=> Product state could not be identified among fragment calculations\n" ) nrgfile.write( "=> No reaction energies referenced to isolated molecules will be calculated for this pathway\n" ) nrgfile.close() print("\x1b[1;92mIRC Success!\x1b[0m") tarexit()
def main(): # Get user input. args = parse_user_input() # Start timer. click() subxyz = [] subna = [] subchg = [] submult = [] subvalid = [] subefstart = [] subeffinal = [] SumFrags = [] fragfiles = [] # Pick out the files in the tarfile that have the structure "example*.sub_*.xyz" idhome = os.path.abspath('..') tar = tarfile.open(os.path.join(idhome, 'fragmentid.tar.bz2'), 'r') for tarinfo in tar: splitlist = tarinfo.name.split(".") if len(splitlist) > 2: if splitlist[2] == "xyz": subxyz.append(tarinfo.name) fragfiles.append(tarinfo) # Extract these files from the tarfile tar.extractall(members=fragfiles) tar.close() # Load files as molecules for frag in subxyz: M = Molecule(frag) M.read_comm_charge_mult() subchg.append(M.charge) submult.append(M.mult) subna.append(M.na) efstart = re.search('(?<=Molecular formula )\w+', M.comms[0]).group(0) subefstart.append(efstart) print("Running individual optimizations.") # Run individual geometry optimizations. FragE = 0.0 FragZPE = 0.0 FragEntr = 0.0 FragEnth = 0.0 for i in range(len(subxyz)): if subna[i] > 1: M = QCOptIC(subxyz[i], subchg[i], submult[i], args.method, args.basis, cycles=args.cycles, qcin='%s.opt.in' % os.path.splitext(subxyz[i])[0]) # Select frames where the energy is monotonically decreasing. M = M[monotonic_decreasing(M.qm_energies)] else: # Special case of a single atom QCSP = QChem(subxyz[i], charge=subchg[i], mult=submult[i], method=args.method, basis=args.basis, qcin='%s.sp.in' % os.path.splitext(subxyz[i])[0]) QCSP.make_stable() QCSP.jobtype = 'sp' QCSP.calculate() M = QCSP.load_qcout() # Write optimization final result to file. M = M[-1] fragoptfile = os.path.splitext(subxyz[i])[0] + ".opt.xyz" M.write(fragoptfile) QCFR = QChem(fragoptfile, charge=subchg[i], mult=submult[i], method=args.method, basis=args.basis, qcin='%s.freq.in' % os.path.splitext(fragoptfile)[0]) QCFR.freq() M = QCFR.load_qcout() # Print out new molecular formulas. # This time we should be able to use covalent radii. M.build_topology(force_bonds=True) optformula = ' '.join([m.ef() for m in M.molecules]) subeffinal.append(optformula) print(("%s.opt.xyz : formula %s; charge %i; mult %i; energy %f Ha;" "ZPE %f kcal/mol; entropy %f cal/mol.K; enthalpy %f kcal/mol" % (os.path.splitext(subxyz[i])[0], optformula, subchg[i], submult[i], M.qm_energies[0], M.qm_zpe[0], M.qm_entropy[0], M.qm_enthalpy[0]))) FragE += M.qm_energies[0] FragZPE += M.qm_zpe[0] FragEntr += M.qm_entropy[0] FragEnth += M.qm_enthalpy[0] SumFrags.append(M[0]) for fragment in SumFrags: fragment.write('fragmentopt.xyz', append=True) if subefstart != subeffinal: print("Fragments changed during optimization, calculation invalid") print("Final electronic energy (Ha) of optimized frags: % 18.10f" % FragE) print("Final ZPE (kcal/mol) of optimized frags: % 18.10f" % FragZPE) print("Final entropy (cal/mol.K) of optimized frags: % 18.10f" % FragEntr) print("Final enthalpy (kcal/mol) of optimized frags: % 18.10f" % FragEnth) nrg = open('fragmentopt.nrg', 'w') for subef in subeffinal: nrg.write(subef + " ") nrg.write("\nTotal electronic energy: %f Ha\n" % FragE) nrg.write("Total ZPE: %f kcal/mol\n" % FragZPE) nrg.write("Total entropy (STP): %f cal/mol.K\n" % FragEntr) nrg.write("Total enthalpy (STP): %f kcal/mol\n" % FragEnth) if subefstart != subeffinal: nrg.write("invalid") nrg.close() # Archive and exit. tarexit()